diff --git a/.coveragerc b/.coveragerc index 398ff08af..041ddba2d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,2 +1,16 @@ +# Copyright (c) 2018 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + [run] branch = True diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..77471a933 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,24 @@ +# Copyright (c) 2021 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + assignees: + - "dkfellows" + reviewers: + - "rowleya" diff --git a/.github/workflows/python_actions.yml b/.github/workflows/python_actions.yml new file mode 100644 index 000000000..77efd41a3 --- /dev/null +++ b/.github/workflows/python_actions.yml @@ -0,0 +1,106 @@ +# Copyright (c) 2020 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This workflow will install Python dependencies, run tests, lint and rat with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Python Actions +on: [push] +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + matrix: + python-version: [3.8, 3.9, "3.10", "3.11"] + + steps: + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Checkout + uses: actions/checkout@v4 + - name: Checkout SupportScripts + uses: actions/checkout@v4 + with: + repository: SpiNNakerManchester/SupportScripts + path: support + - name: Install pip, etc + uses: ./support/actions/python-tools + + - name: Install Spinnaker Dependencies + uses: ./support/actions/install-spinn-deps + with: + repositories: SpiNNUtils SpiNNMachine + install: true + + - name: Run Install + uses: ./support/actions/run-install + + - name: Test with pytest + uses: ./support/actions/pytest + with: + tests: unittests + coverage: ${{ matrix.python-version == 3.8 }} + cover-packages: spinnman + coveralls-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Lint with flake8 + run: flake8 spinnman unittests + - name: Lint with pylint + uses: ./support/actions/pylint + with: + package: spinnman + language: en_GB + + validate: + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + matrix: + python-version: ["3.8"] + + steps: + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Checkout + uses: actions/checkout@v4 + - name: Checkout SupportScripts + uses: actions/checkout@v4 + with: + repository: SpiNNakerManchester/SupportScripts + path: support + - name: Install pip, etc + uses: ./support/actions/python-tools + - name: Install Spinnaker Dependencies + uses: ./support/actions/install-spinn-deps + with: + repositories: SpiNNUtils SpiNNMachine + install: true + - name: Run Install + uses: ./support/actions/run-install + + - name: Run rat copyright enforcement + uses: ./support/actions/check-copyrights + with: + config_file: rat_asl20.xml + - name: Build documentation with sphinx + uses: ./support/actions/sphinx + with: + directory: doc/source + - name: Validate CITATION.cff + uses: dieghernan/cff-validator@main diff --git a/.gitignore b/.gitignore index b62202d3a..ed7a51646 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. /.project /.pydevproject @@ -30,4 +29,3 @@ SpiNNMan.egg-info .coverage .cache/ .pytest_cache/ -/.pylintrc diff --git a/.pylint_dict.txt b/.pylint_dict.txt new file mode 100644 index 000000000..18dfaeac7 --- /dev/null +++ b/.pylint_dict.txt @@ -0,0 +1,39 @@ +# Copyright (c) 2023 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Our abbreviations/names +Spalloc +bmpc +ybug +txrx + +# Our special words +keepalive + +# Python packages +websocket + +# Python types +BMPConnectionData +BufferedIOBase +CoreSubset +CPUInfo +CPUState +HeapElement +IOBuffer +RawIOBase +SCAMPConnection +SpinnakerBootMessage +SystemVariableDefinition +WebSocket diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 000000000..2d054cde5 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,469 @@ +# Copyright (c) 2019 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist=numpy + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. +jobs=1 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Specify a configuration file. +#rcfile= + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +disable=R,C + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable=c-extension-no-member,C0402,C0403 + + +[REPORTS] + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio).You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages +reports=no + +# Activate the evaluation score. +score=yes + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=optparse.Values,sys.exit + + +[BASIC] + +# Naming style matching correct argument names +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style +#argument-rgx= + +# Naming style matching correct attribute names +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Naming style matching correct class attribute names +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style +#class-attribute-rgx= + +# Naming style matching correct class names +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming-style +#class-rgx= + +# Naming style matching correct constant names +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma +good-names=i, + j, + k, + ex, + Run, + _ + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Naming style matching correct inline iteration names +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style +#inlinevar-rgx= + +# Naming style matching correct method names +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style +#method-rgx= + +# Naming style matching correct module names +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty + +# Naming style matching correct variable names +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style +#variable-rgx= + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module +max-module-lines=1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging,FormatAdapter + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members=numpy.* + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local,numpy + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules=numpy + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + _hard_reset, + + setUp + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of statements in function / method body +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[IMPORTS] + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=optparse,tkinter.tix + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=builtins.Exception diff --git a/.ratexcludes b/.ratexcludes index 4e3f6c32f..26527b824 100644 --- a/.ratexcludes +++ b/.ratexcludes @@ -1 +1,3 @@ **/file_data/* +**/SpiNNUtils/** +**/SpiNNMachine/** \ No newline at end of file diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 000000000..47ca8e7ed --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,28 @@ +# Copyright (c) 2023 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +version: 2 +build: + os: ubuntu-22.04 + tools: + python: "3.11" +sphinx: + configuration: doc/source/conf.py + builder: dirhtml + fail_on_warning: false +python: + install: + - requirements: doc/doc_requirements.txt + - method: pip + path: . diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 8bed0e54d..000000000 --- a/.travis.yml +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -language: python -python: - - 2.7 - - 3.6 - - 3.7 -dist: xenial -addons: - apt: - packages: - - openjdk-8-jre-headless - - ant -cache: pip - -before_install: - # Work around ludicrous Travis bug - - git clone https://github.com/SpiNNakerManchester/SupportScripts.git support - - python support/travis_blocking_stdout.py - - support/rat.sh download - # Bring pip up to date - - pip install --upgrade pip setuptools wheel - # SpiNNakerManchester internal dependencies; development mode - - support/pipinstall.sh git://github.com/SpiNNakerManchester/SpiNNUtils.git - - support/pipinstall.sh git://github.com/SpiNNakerManchester/SpiNNMachine.git - - support/pipinstall.sh git://github.com/SpiNNakerManchester/SpiNNStorageHandlers.git - -install: - - python ./setup.py install - - pip install -r requirements-test.txt - - pip install python-coveralls 'coverage>=4.4' - - pip install spinnaker_proxy - -script: - - py.test unittests integration_tests --cov spinnman - # Code quality check - - flake8 - - support/rat.sh run - - cd doc/source - - sphinx-build -T -E -b html -d _build/doctrees-readthedocsdirhtml -D language=en . _build/html - - sphinx-build -T -b json -d _build/doctrees-json -D language=en . _build/json - - sphinx-build -T -b singlehtml -d _build/doctrees-readthedocssinglehtmllocalmedia -D language=en . _build/localmedia - -after_success: - - cd $TRAVIS_BUILD_DIR - - coveralls diff --git a/CITATION.cff b/CITATION.cff index 87b29333b..def654cfb 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,96 +1,99 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +cff-version: 1.2.0 +message: If you use this software, please cite it as below. +preferred-citation: + type: article + doi: 10.3389/fnins.2019.00231 + issn: 1662-453X + url: https://www.frontiersin.org/articles/10.3389/fnins.2019.00231 + title: "SpiNNTools: The Execution Engine for the SpiNNaker Platform" + journal: Frontiers in Neuroscience + volume: 13 + year: 2019 + month: 3 + abstract: SpiNNaker is a massively parallel distributed architecture primarily focused on real time simulation of spiking neural networks. The largest realization of the architecture consists of one million general purpose processors, making it the largest neuromorphic computing platform in the world at the present time. Utilizing these processors efficiently requires expert knowledge of the architecture to generate executable code and to harness the potential of the unique inter-processor communications infra-structure that lies at the heart of the SpiNNaker architecture. This work introduces a software suite called SpiNNTools that can map a computational problem described as a graph into the required set of executables, application data and routing information necessary for simulation on this novel machine. The SpiNNaker architecture is highly scalable, giving rise to unique challenges in mapping the problem to the machines resources, loading the generated files to the machine and subsequently retrieving the results of simulation. In this paper we describe these challenges in detail and the solutions implemented. + authors: + - given-names: Andrew + family-names: Rowley + affiliation: University Of Manchester + email: Andrew.Rowley@manchester.ac.uk + orcid: https://orcid.org/0000-0002-2646-8520 + website: https://www.researchgate.net/profile/Andrew_Rowley2 + - given-names: Christian Y. + family-names: Brenninkmeijer + affiliation: University Of Manchester + email: christian.brenninkmeijer@manchester.ac.uk + orcid: https://orcid.org/0000-0002-2937-7819 + website: https://www.researchgate.net/profile/Christian_Brenninkmeijer + - given-names: Simon + family-names: Davidson + affiliation: University Of Manchester + orcid: https://orcid.org/0000-0001-5385-442X + website: https://research.manchester.ac.uk/en/persons/simon.davidson + - given-names: Donal + family-names: Fellows + affiliation: University Of Manchester + orcid: https://orcid.org/0000-0002-9091-5938 + website: https://www.researchgate.net/profile/Donal-Fellows + - given-names: Andrew + family-names: Gait + affiliation: University Of Manchester + orcid: https://orcid.org/0000-0001-9349-1096 + website: https://personalpages.manchester.ac.uk/staff/andrew.gait/ + - given-names: David R. + family-names: Lester + affiliation: University Of Manchester + orcid: https://orcid.org/0000-0002-7267-291X + - given-names: Luis A. + family-names: Plana + affiliation: University Of Manchester + orcid: https://orcid.org/0000-0002-6113-3929 + website: https://research.manchester.ac.uk/en/persons/luis.plana + - given-names: Oliver + family-names: Rhodes + affiliation: University Of Manchester + orcid: https://orcid.org/0000-0003-1728-2828 + website: https://research.manchester.ac.uk/en/persons/oliver.rhodes + - given-names: Alan B. + family-names: Stokes + affiliation: University Of Manchester + orcid: https://orcid.org/0000-0002-6110-1484 + - given-names: Steve B. + family-names: Furber + affiliation: University Of Manchester + orcid: https://orcid.org/0000-0002-6524-3367 + website: https://research.manchester.ac.uk/en/persons/steve.furber + +title: Python utilities for interacting with a SpiNNaker machine. authors: -- affiliation: University Of Manchester - email: alan.stokes-2@manchester.ac.uk - family-names: Stokes - given-names: Alan - orcid: https://orcid.org/0000-0002-6110-1484 - website: http://www.cs.man.ac.uk/~stokesa6/ -- affiliation: University Of Manchester - email: Andrew.Rowley@manchester.ac.uk - family-names: Rowley - given-names: Andrew - orcid: https://orcid.org/0000-0002-2646-8520 - website: https://www.researchgate.net/profile/Andrew_Rowley2 -- affiliation: University Of Manchester - email: Andrew.Gait@manchester.ac.uk - family-names: Gait - given-names: Andrew - orcid: http://orcid.org/0000-0001-9349-1096 - website: https://personalpages.manchester.ac.uk/staff/Andrew.Gait/ -- affiliation: University Of Manchester - email: christian.brenninkmeijer@manchester.ac.uk - family-names: Brenninkmeijer - given-names: Christian Y. - orcid: https://orcid.org/0000-0002-2937-7819 - website: https://www.researchgate.net/profile/Christian_Brenninkmeijer -- affiliation: University Of Manchester - email: donal.k.fellows@manchester.ac.uk - family-names: Fellows - given-names: Donal - orcid: https://orcid.org/0000-0002-9091-5938 - website: http://www.cs.manchester.ac.uk/about-us/staff/profile/?ea=donal.k.fellows -- affiliation: University Of Manchester - email: luis.plana@manchester.ac.uk - family-names: Plana - given-names: Luis - orcid: https://orcid.org/0000-0002-6113-3929 - website: https://www.research.manchester.ac.uk/portal/luis.plana.html -- affiliation: University Of Manchester - email: jheathcote@cs.man.ac.uk - family-names: Heathcote - given-names: Jonathan - website: http://apt.cs.manchester.ac.uk/people/jheathcote/ -- affiliation: University Of Manchester - email: oliver.rhodes@manchester.ac.uk - family-names: Rhodes - given-names: Oliver - orcid: https://orcid.org/0000-0003-1728-2828 - website: http://apt.cs.manchester.ac.uk/people/orhodes/ -- affiliation: University Of Manchester - email: petrut.bogdan@manchester.ac.uk - family-names: Bogdan - given-names: Petrut - orcid: https://orcid.org/0000-0001-5535-7865 - website: https://personalpages.manchester.ac.uk/staff/petrut.bogdan/ -- affiliation: University Of Manchester - email: sergio.davies@gmail.com - family-names: Davies - given-names: Sergio - orcid: https://orcid.org/0000-0001-5330-5527 - website: http://apt.cs.manchester.ac.uk/people/daviess/ -- affiliation: University Of Manchester - email: simon.davidson@manchester.ac.uk - family-names: Davidson - given-names: Simon - orcid: https://orcid.org/0000-0001-5385-442X - website: http://apt.cs.manchester.ac.uk/people/sdavidson/ -cff-version: 1.0.3 + - name: SpiNNaker Software Team + alias: For a list of contributors see https://github.com/SpiNNakerManchester/SpiNNMan/graphs/contributors or for a combined list see https://spinnakermanchester.github.io/latest/LicenseAgreement.html#contributors + address: University of Manchester, Oxford Road + city: Manchester + country: GB + email: spinnakerusers@googlegroups.com + post-code: M13 9PL + website: https://apt.cs.manchester.ac.uk/projects/SpiNNaker/ +url: https://spinnakermanchester.github.io/ contact: - address: University of Manchester, Oxford Road city: Manchester country: GB + email: spinnakerusers@googlegroups.com name: SpiNNaker Software Team post-code: M13 9PL -date-released: -email: spinnakerusers@googlegroups.com -identifier: -message: If you use this software, please cite it as below. +license: Apache-2.0 repository: https://github.com/SpiNNakerManchester/SpiNNMan -title: Python interface to SpiNNaker machines -url: http://spinnakermanchester.github.io/ -version: diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..62589edd1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE.md b/LICENSE_POLICY.md similarity index 52% rename from LICENSE.md rename to LICENSE_POLICY.md index 6f91c8232..7ca674f8e 100644 --- a/LICENSE.md +++ b/LICENSE_POLICY.md @@ -1,17 +1,17 @@ # License Agreement -Up to date information for the whole [SpiNNakerManchester Projects](https://github.com/SpiNNakerManchester) can be found [here](http://spinnakermanchester.github.io/common_pages/4.0.0/LicenseAgreement.html) +Up to date information for the whole [SpiNNakerManchester Projects](https://github.com/SpiNNakerManchester) can be found [here](https://spinnakermanchester.github.io/latest/LicenseAgreement.html) -As shown there the software is currently being released under the GPL version 3 license listed [here](http://www.gnu.org/copyleft/gpl.html) +As shown there the software is currently being released under the Apache License 2.0 listed [here](https://www.apache.org/licenses/LICENSE-2.0) # Paper Authorship -See: [here](http://spinnakermanchester.github.io/common_pages/4.0.0/LicenseAgreement.html#paper-authorship) +See: [here](https://spinnakermanchester.github.io/latest/LicenseAgreement.html#paper-authorship) # Modifications -See: [here](http://spinnakermanchester.github.io/common_pages/4.0.0/LicenseAgreement.html#modifications) +See: [here](https://spinnakermanchester.github.io/latest/LicenseAgreement.html#modifications) # Contributors @@ -19,5 +19,5 @@ For up to date information on Contributors see the graphs/contributors pages on For example [https://github.com/SpiNNakerManchester/SpiNNMan/graphs/contributors](https://github.com/SpiNNakerManchester/SpiNNMan/graphs/contributors) -[Combined list](http://spinnakermanchester.github.io/common_pages/4.0.0/LicenseAgreement.html#contributors) +[Combined list](https://spinnakermanchester.github.io/latest/LicenseAgreement.html#contributors) diff --git a/MANIFEST.in b/MANIFEST.in index 860619287..4e4eef7b4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1 @@ -include CITATION.cff LICENSE.md requirements.txt pypi_to_import \ No newline at end of file +include LICENSE LICENSE_POLICY.md README.md CITATION.cff \ No newline at end of file diff --git a/README.md b/README.md index 96a66b842..0a14486f9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -[![Build Status](https://api.travis-ci.org/SpiNNakerManchester/SpiNNMan.svg?branch=master)](https://travis-ci.org/SpiNNakerManchester/SpiNNMan) +[![PyPi version](https://img.shields.io/pypi/v/SpiNNMan.svg?style=flat)](https://pypi.org/project/SpiNNMan/) +[![Build Status](https://github.com/SpiNNakerManchester/SpiNNMan/workflows/Python%20Actions/badge.svg?branch=master)](https://github.com/SpiNNakerManchester/SpiNNMan/actions?query=workflow%3A%22Python+Actions%22+branch%3Amaster) +[![Documentation Status](https://readthedocs.org/projects/spinnman/badge/?version=latest)](https://spinnman.readthedocs.io/en/latest/?badge=latest) [![Coverage Status](https://coveralls.io/repos/github/SpiNNakerManchester/SpiNNMan/badge.svg?branch=master)](https://coveralls.io/github/SpiNNakerManchester/SpiNNMan?branch=master) @@ -8,14 +10,10 @@ Requirements ============ In addition to a standard Python installation, this package depends on: - - six - - enum34 - SpiNNMachine These requirements can be install using pip: - pip install six - pip install enum34 pip install SpiNNMachine User Installation @@ -44,21 +42,25 @@ the git repository: To install as a development version which all users will then be able to use, run the following where the code has been extracted: - sudo python setup.py develop + sudo pip install -e . To install as a development version for only yourself, run: - python setup.py develop --user + pip install -e . --user -To install as a development version in a virtualenv, with the virutalenv +To install as a development version in a `virtualenv`, with the `virutalenv` enabled, run: - python setup.py develop + pip install -e . + +Test Installation +================= +To be able to run the unitests add [Test] to the pip installs above + + pip install -e .[Test] Documentation ============= [SpiNNMan Python documentation](http://spinnman.readthedocs.io) -[Combined PyNN7 python documentation](http://spinnaker7manchester.readthedocs.io) - -[Combined PyNN8 python documentation](http://spinnaker8manchester.readthedocs.io) +[Combined python documentation](http://spinnakermanchester.readthedocs.io) diff --git a/board_test_configuration.py b/board_test_configuration.py deleted file mode 100644 index 0cb07900e..000000000 --- a/board_test_configuration.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import os -import socket -import unittest -from six.moves import configparser -from spinn_utilities.ping import Ping -from spinnman.model import BMPConnectionData - -_LOCALHOST = "127.0.0.1" -# Microsoft invalid IP address. For more details see: -# http://answers.microsoft.com/en-us/windows/forum/windows_vista-networking/invalid-ip-address-169254xx/ce096728-e2b7-4d54-80cc-52a4ed342870 -_NOHOST = "169.254.254.254" -_PORT = 54321 - - -class BoardTestConfiguration(object): - - def __init__(self): - self.localhost = None - self.localport = None - self.remotehost = None - self.board_version = None - self.bmp_names = None - self.auto_detect_bmp = None - - self._config = configparser.RawConfigParser() - config_file = os.path.join(os.path.dirname(__file__), "test.cfg") - self._config.read(config_file) - - def set_up_local_virtual_board(self): - self.localhost = _LOCALHOST - self.localport = _PORT - self.remotehost = _LOCALHOST - self.board_version = self._config.getint("Machine", "version") - - def set_up_remote_board(self): - self.remotehost = self._config.get("Machine", "machineName") - if not Ping.host_is_reachable(self.remotehost): - raise unittest.SkipTest("test board appears to be down") - self.board_version = self._config.getint("Machine", "version") - self.bmp_names = self._config.get("Machine", "bmp_names") - if self.bmp_names == "None": - self.bmp_names = None - else: - self.bmp_names = [BMPConnectionData( - 0, 0, self.bmp_names, [0], None)] - self.auto_detect_bmp = \ - self._config.getboolean("Machine", "auto_detect_bmp") - self.localport = _PORT - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - try: - s.connect((self.remotehost, _PORT)) - self.localhost = s.getsockname()[0] - finally: - s.close() - - def set_up_nonexistent_board(self): - self.localhost = None - self.localport = _PORT - self.remotehost = _NOHOST - self.board_version = self._config.getint("Machine", "version") diff --git a/doc/Makefile b/doc/Makefile index 1c5e80ca9..2ff971636 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # Minimal makefile for Sphinx documentation # diff --git a/doc/doc_requirements.txt b/doc/doc_requirements.txt index 5cdf2f1cf..910bac4fd 100644 --- a/doc/doc_requirements.txt +++ b/doc/doc_requirements.txt @@ -1,20 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -six -enum34 --e git+https://github.com/SpiNNakerManchester/SpiNNUtils.git@master#egg=spinn_utilities +-e git+https://github.com/SpiNNakerManchester/SpiNNUtils.git@master#egg=SpiNNUtilities -e git+https://github.com/SpiNNakerManchester/SpiNNMachine.git@master#egg=SpiNNMachine --e git+https://github.com/SpiNNakerManchester/SpiNNStorageHandlers.git@master#egg=spinn_storage_handlers \ No newline at end of file diff --git a/doc/make.bat b/doc/make.bat index 99e695990..eeb5e5772 100644 --- a/doc/make.bat +++ b/doc/make.bat @@ -1,19 +1,18 @@ @ECHO OFF -: Copyright (c) 2017-2019 The University of Manchester +: Copyright (c) 2017 The University of Manchester : -: This program is free software: you can redistribute it and/or modify -: it under the terms of the GNU General Public License as published by -: the Free Software Foundation, either version 3 of the License, or -: (at your option) any later version. +: Licensed under the Apache License, Version 2.0 (the "License"); +: you may not use this file except in compliance with the License. +: You may obtain a copy of the License at : -: This program is distributed in the hope that it will be useful, -: but WITHOUT ANY WARRANTY; without even the implied warranty of -: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -: GNU General Public License for more details. +: https://www.apache.org/licenses/LICENSE-2.0 : -: You should have received a copy of the GNU General Public License -: along with this program. If not, see . +: Unless required by applicable law or agreed to in writing, software +: distributed under the License is distributed on an "AS IS" BASIS, +: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +: See the License for the specific language governing permissions and +: limitations under the License. pushd %~dp0 @@ -37,7 +36,7 @@ if errorlevel 9009 ( echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ + echo.https://sphinx-doc.org/ exit /b 1 ) diff --git a/doc/source/.gitignore b/doc/source/.gitignore index c85cba7d8..5169fa8a1 100644 --- a/doc/source/.gitignore +++ b/doc/source/.gitignore @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. *.rst !index.rst diff --git a/doc/source/conf.py b/doc/source/conf.py index 310c39179..0da78ccf5 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,369 +1,424 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# data_allocation documentation build configuration file, created by -# sphinx-quickstart on Tue Jun 17 08:56:46 2014. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# import sys -import os -from sphinx import apidoc - - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.viewcode', - 'sphinx.ext.autosummary', - 'sphinx.ext.intersphinx' -] - -intersphinx_mapping = {'spinn_machine': - ('http://spinnmachine.readthedocs.org/en/latest/', - None), - 'spinn_storage_handlers': - ('http://spinnmachine.readthedocs.io/en/latest/', - None) - } - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -# source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'SpiNNMan' -copyright = u'2014-2017' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '' -# The full version, including alpha/beta/rc tags. -release = '' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -# default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -# keep_warnings = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'sphinxdoc' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'SpiNNMandoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # 'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'SpiNNMan.tex', u'SpiNNMan Documentation', - u'', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'SpiNNMan', u'SpiNNMan Documentation', - [u''], 1) -] - -# If true, show URL addresses after external links. -# man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'SpiNNMan', u'SpiNNMan Documentation', - u'', 'SpiNNMan', '', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -# texinfo_appendices = [] - -# If false, no module index is generated. -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -# texinfo_no_detailmenu = False - - -# -- Options for Epub output ---------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = u'SpiNNMan' -epub_author = u'' -epub_publisher = u'' -epub_copyright = u'2014-2017' - -# The basename for the epub file. It defaults to the project name. -# epub_basename = u'data_allocation' - -# The HTML theme for the epub output. -# Since the default themes are not optimized -# for small screen space, using the same theme for HTML and epub output is -# usually not wise. This defaults to 'epub', a theme designed to save visual -# space. -# epub_theme = 'epub' - -# The language of the text. It defaults to the language option -# or en if the language is not set. -# epub_language = '' - -# The scheme of the identifier. Typical schemes are ISBN or URL. -# epub_scheme = '' - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# epub_identifier = '' - -# A unique identification for the text. -# epub_uid = '' - -# A tuple containing the cover image and cover page html template filenames. -# epub_cover = () - -# A sequence of (type, uri, title) tuples for the guide element of content.opf. -# epub_guide = () - -# HTML files that should be inserted before the pages created by sphinx. -# The format is a list of tuples containing the path and title. -# epub_pre_files = [] - -# HTML files shat should be inserted after the pages created by sphinx. -# The format is a list of tuples containing the path and title. -# epub_post_files = [] - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] - -# The depth of the table of contents in toc.ncx. -# epub_tocdepth = 3 - -# Allow duplicate toc entries. -# epub_tocdup = True - -# Choose between 'default' and 'includehidden'. -# epub_tocscope = 'default' - -# Fix unsupported image types using the PIL. -# epub_fix_images = False - -# Scale large images. -# epub_max_image_width = 0 - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# epub_show_urls = 'inline' - -# If false, no index is generated. -# epub_use_index = True - -autoclass_content = 'both' - -# Do the rst generation if in READ_THE_DOCS -# Do the rst generation -for f in os.listdir("."): - if (os.path.isfile(f) and f.endswith( - ".rst") and f != "index.rst" and f != "modules.rst"): - os.remove(f) -apidoc.main([None, '-o', ".", "../../spinnman"]) +# -*- coding: utf-8 -*- +# +# Copyright (c) 2014 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# data_allocation documentation build configuration file, created by +# sphinx-quickstart on Tue Jun 17 08:56:46 2014. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# import sys +import os +import re +import sys +from sphinx.ext import apidoc + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.viewcode', + 'sphinx.ext.autosummary', + 'sphinx.ext.intersphinx' +] + +# Which version of other SpiNNaker docs do we refer to? +spinnaker_doc_version = "latest" + +intersphinx_mapping = { + 'python': ('https://docs.python.org/3.8', None), + 'spinn_utilities': ( + f'https://spinnutils.readthedocs.io/en/{spinnaker_doc_version}/', + None), + 'spinn_machine': ( + f'https://spinnmachine.readthedocs.io/en/{spinnaker_doc_version}/', + None), +} + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'SpiNNMan' +copyright = u'2014' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '6.0' +# The full version, including alpha/beta/rc tags. +release = '6.0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +# keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'sphinxdoc' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'SpiNNMandoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # 'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'SpiNNMan.tex', u'SpiNNMan Documentation', + u'', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'SpiNNMan', u'SpiNNMan Documentation', + [u''], 1) +] + +# If true, show URL addresses after external links. +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'SpiNNMan', u'SpiNNMan Documentation', + u'', 'SpiNNMan', '', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +# texinfo_appendices = [] + +# If false, no module index is generated. +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +# texinfo_no_detailmenu = False + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = u'SpiNNMan' +epub_author = u'' +epub_publisher = u'' +epub_copyright = u'2014' + +# The basename for the epub file. It defaults to the project name. +# epub_basename = u'data_allocation' + +# The HTML theme for the epub output. +# Since the default themes are not optimized +# for small screen space, using the same theme for HTML and epub output is +# usually not wise. This defaults to 'epub', a theme designed to save visual +# space. +# epub_theme = 'epub' + +# The language of the text. It defaults to the language option +# or en if the language is not set. +# epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +# epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# epub_identifier = '' + +# A unique identification for the text. +# epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +# epub_cover = () + +# A sequence of (type, uri, title) tuples for the guide element of content.opf. +# epub_guide = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +# epub_post_files = [] + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# The depth of the table of contents in toc.ncx. +# epub_tocdepth = 3 + +# Allow duplicate toc entries. +# epub_tocdup = True + +# Choose between 'default' and 'includehidden'. +# epub_tocscope = 'default' + +# Fix unsupported image types using the PIL. +# epub_fix_images = False + +# Scale large images. +# epub_max_image_width = 0 + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# epub_show_urls = 'inline' + +# If false, no index is generated. +# epub_use_index = True + +autoclass_content = 'both' + +# We want to document __call__ when encountered +autodoc_default_options = { + "members": None, + "special-members": "__call__" +} + +_package_base = "spinnman" + + +# Automatically called by sphinx at startup +def setup(app): + # NB: extra dot at end is deliberate! + trim = (_package_base + ".", "spinn_utilities.", "spinn_machine.") + + # Magic to shorten the names of our classes to their public versions + def skip_handler(_app, what, name, obj, skip, _options): + if not skip and what == 'module' and hasattr(obj, "__module__"): + # Get parent module *and* check if our name is in it + m = re.sub(r'\.[a-z0-9_]+$', '', obj.__module__) + if any(m.startswith(prefix) for prefix in trim) and \ + name in dir(sys.modules[m]): + # It is, so update to say that's canonical location for + # documentation purposes + obj.__module__ = m + return skip # We don't care to change this + + # Connect the callback to the autodoc-skip-member event from apidoc + app.connect('autodoc-skip-member', skip_handler) + + +def excluded_because_in_init(base): + for root, _dirs, files in os.walk(base): + if "__init__.py" in files: + init = os.path.join(root, "__init__.py") + with open(init) as f: + for line in f: + if line.startswith("from ."): + parts = line.split() + yield os.path.join(root, parts[1][1:]+".py") + + +_output_dir = os.path.abspath(".") + +# Do the rst generation; remove files which aren't in git first! +for fl in os.listdir("."): + if (os.path.isfile(fl) and fl.endswith(".rst") and + fl not in ("index.rst", "modules.rst")): + os.remove(fl) +os.chdir("../..") # WARNING! RELATIVE FILENAMES CHANGE MEANING HERE! +apidoc.main([ + '-o', _output_dir, _package_base, + "spinnman/messages/spinnaker_boot/boot_data/*", + # Special case: Don't want that empty package at all. + *excluded_because_in_init(_package_base)]) + +# See Note at bottom of global doc conf.py + diff --git a/doc/source/index.rst b/doc/source/index.rst index 812dbe00e..4f7c6bede 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1,19 +1,19 @@ These pages document the python code for the SpiNNMan_ module which is part of the SpiNNaker_ Project. -This code depends on SpiNNUtils_, SpiNNMachine_ and SpiNNStorageHandlers_ (Combined_documentation_). +This code depends on SpiNNUtils_ and SpiNNMachine_ (Combined_documentation_). -.. _SpiNNaker: http://apt.cs.manchester.ac.uk/projects/SpiNNaker/ +.. _SpiNNaker: https://apt.cs.manchester.ac.uk/projects/SpiNNaker/ .. _SpiNNMan: https://github.com/SpiNNakerManchester/SpiNNMan -.. _SpiNNUtils: http://spinnutils.readthedocs.io -.. _SpiNNMachine: http://spinnmachine.readthedocs.io -.. _SpiNNStorageHandlers: http://spinnstoragehandlers.readthedocs.io -.. _Combined_documentation: http://spinnakermanchester.readthedocs.io +.. _SpiNNUtils: https://spinnutils.readthedocs.io +.. _SpiNNMachine: https://spinnmachine.readthedocs.io +.. _Combined_documentation: https://spinnakermanchester.readthedocs.io SpiNNMan ======== .. automodule:: spinnman + :noindex: Contents -------- @@ -21,8 +21,7 @@ Contents .. toctree:: :maxdepth: 4 - spinnman - + modules Indices and tables ------------------ @@ -30,4 +29,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - diff --git a/hello.c b/hello.c deleted file mode 100644 index 6b7cd2569..000000000 --- a/hello.c +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2017-2019 The University of Manchester -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#include - -void timer_callback(uint unused0, uint unused1) { - io_printf(IO_BUF, "Hello!\n"); - spin1_exit(0); -} - -void c_main() { - io_printf(IO_BUF, "Starting...\n"); - spin1_set_timer_tick(1000); - spin1_callback_on(TIMER_TICK, timer_callback, 1); - spin1_start(SYNC_WAIT); - io_printf(IO_BUF, "Finished\n"); -} diff --git a/integration_tests/.gitignore b/integration_tests/.gitignore deleted file mode 100644 index 635b85eed..000000000 --- a/integration_tests/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -/test diff --git a/integration_tests/__init__.py b/integration_tests/__init__.py deleted file mode 100644 index d358f58a8..000000000 --- a/integration_tests/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . diff --git a/integration_tests/test_file_io.py b/integration_tests/test_file_io.py deleted file mode 100644 index 1f6eaf356..000000000 --- a/integration_tests/test_file_io.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import os -import struct -import numpy -from spinnman.utilities.io.file_io import FileIO - - -def test_file_io(): - n_bytes = 1000 - read_start_offset = 5 - read_end_offset = -10 - my_file = "test" - data = bytearray(numpy.random.randint(0, 256, n_bytes).astype("uint8")) - memory = FileIO(my_file, 0, n_bytes) - memory.write(bytes(data)) - memory = memory[read_start_offset:read_end_offset] - read_data = bytearray(memory.read()) - compare_data = data[read_start_offset:read_end_offset] - assert(compare_data == read_data) - - memory.seek(20) - test_data = bytearray(list(range(10))) - memory.write(bytes(test_data)) - memory.write(bytes(test_data)) - memory.seek(-10, os.SEEK_CUR) - read_data = bytearray(memory.read(10)) - assert(test_data == read_data) - - _test_fill(memory, 100) - _test_fill(memory, 101) - _test_fill(memory, 102) - _test_fill(memory, 103) - - memory.seek(0) - sub_memory_1 = memory[0:10] - sub_memory_2 = memory[0:10] - sub_memory_1.write(b'test') - result = sub_memory_2.read(4) - assert(result == b'test') - - memory.close() - os.remove(my_file) - - -def _test_fill(memory, offset): - memory.seek(offset + 12) - memory.write(struct.pack("B", 10)) - memory[offset:offset + 12].fill(5) - memory.seek(offset) - test_data = struct.unpack(". - -import os -import struct -import numpy -from spinnman.utilities.io.memory_io import MemoryIO - - -class _MockTransceiver(object): - - def __init__(self): - self._data = dict() - - def _get_memory(self, x, y): - if (x, y) not in self._data: - self._data[(x, y)] = numpy.zeros(128 * 1024 * 1024, dtype="uint8") - return self._data[(x, y)] - - def write_memory(self, x, y, address, data, n_bytes=None): - memory = self._get_memory(x, y) - if isinstance(data, int): - memory[address:address + 4] = numpy.array( - [data], dtype="uint32").view(dtype="uint8") - else: - if n_bytes is None: - n_bytes = len(data) - numpy_data = numpy.frombuffer(data[:n_bytes], dtype="uint8") - memory[address:address + n_bytes] = numpy_data - - def read_memory(self, x, y, address, n_bytes): - memory = self._get_memory(x, y) - return bytearray(memory[address:address + n_bytes]) - - def fill_memory( - self, x, y, address, repeat_value, bytes_to_fill, data_type): - memory = self._get_memory(x, y) - data_to_fill = numpy.array([repeat_value], dtype="uint{}".format( - data_type.value * 8)).view("uint8") - data_to_write = numpy.tile( - data_to_fill, bytes_to_fill / data_type.value) - memory[address:address + bytes_to_fill] = data_to_write - - -def test_memory_io(): - n_bytes = 1000 - read_start_offset = 5 - read_end_offset = -10 - transceiver = _MockTransceiver() - address = 0 - data = bytearray(numpy.random.randint(0, 256, n_bytes).astype("uint8")) - memory = MemoryIO(transceiver, 0, 0, address, address + n_bytes) - memory.write(bytes(data)) - read_memory = memory[read_start_offset:read_end_offset] - read_data = bytearray(read_memory.read()) - compare_data = data[read_start_offset:read_end_offset] - assert(compare_data == read_data) - - memory.seek(20) - test_data = bytearray(list(range(10))) - memory.write(bytes(test_data)) - memory.write(bytes(test_data)) - memory.seek(-10, os.SEEK_CUR) - read_data = bytearray(memory.read(10)) - assert(test_data == read_data) - - _test_fill(memory, 100) - _test_fill(memory, 101) - _test_fill(memory, 102) - _test_fill(memory, 103) - - memory.seek(0) - sub_memory_1 = memory[0:10] - sub_memory_2 = memory[0:10] - sub_memory_1.write(b'test') - result = sub_memory_2.read(4) - assert(result == b'test') - - -def _test_fill(memory, offset): - memory.seek(offset + 12) - memory.write(struct.pack("B", 10)) - memory[offset:offset + 12].fill(5) - memory.seek(offset) - test_data = struct.unpack(" + +void timer_callback(uint unused0, uint unused1) { + io_printf(IO_BUF, "Hello!\n"); + spin1_exit(0); +} + +void c_main(void) { + io_printf(IO_BUF, "Starting...\n"); + spin1_set_timer_tick(1000); + spin1_callback_on(TIMER_TICK, timer_callback, 1); + spin1_start(SYNC_WAIT); + io_printf(IO_BUF, "Finished\n"); +} diff --git a/quick_tests.py b/quick_tests/quick_tests.py similarity index 84% rename from quick_tests.py rename to quick_tests/quick_tests.py index 959abee3b..f28772a71 100644 --- a/quick_tests.py +++ b/quick_tests/quick_tests.py @@ -1,26 +1,36 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -from __future__ import print_function +""" +To run this you need an hello.aplx + +to build that cd into this directory +then run + +make -f $SPINN_DIRS/make/app.make APP=hello + +""" import logging from random import randint import struct import time from spinn_machine import CoreSubsets, CoreSubset, MulticastRoutingEntry from spinn_machine.tags import IPTag, ReverseIPTag -from spinnman.transceiver import create_transceiver_from_hostname +from spinnman.data import SpiNNManDataView +from spinnman.config_setup import unittest_setup +from spinnman.extended.extended_transceiver import ( + create_transceiver_from_hostname) from spinnman.model.enums import CPUState from spinnman.messages.scp.enums import Signal from spinnman.model import DiagnosticFilter @@ -28,11 +38,12 @@ from spinnman.model.enums import ( DiagnosticFilterDestination, DiagnosticFilterPacketType) from spinnman.constants import ROUTER_REGISTER_REGISTERS -from board_test_configuration import BoardTestConfiguration +from spinnman.board_test_configuration import BoardTestConfiguration logging.basicConfig(level=logging.INFO) logging.getLogger("spinnman.transceiver").setLevel(logging.DEBUG) +unittest_setup() board_config = BoardTestConfiguration() board_config.set_up_remote_board() @@ -40,10 +51,6 @@ core_subsets = CoreSubsets(core_subsets=[CoreSubset(0, 0, range(1, 11)), CoreSubset(1, 1, range(1, 11))]) -down_cores = CoreSubsets() -down_cores.add_processor(0, 0, 5) -down_chips = CoreSubsets(core_subsets=[CoreSubset(0, 1, [])]) - def print_enums(name, enum_list): string = "" @@ -124,12 +131,12 @@ def print_reinjection_status(status): print("Reinjected packets:", status.n_reinjected_packets) print("Router timeout: {} emergency timeout {}".format( status.router_timeout, status.router_emergency_timeout)) - print("Re-injecting multicast: {} point_to_point: {} nearest_neighbour:" - " {} fixed_route: {}").format( - status.is_reinjecting_multicast, - status.is_reinjecting_point_to_point, - status.is_reinjecting_nearest_neighbour, - status.is_reinjecting_fixed_route) + print(("Re-injecting multicast: {} point_to_point: {} nearest_neighbour:" + " {} fixed_route: {}").format( + status.is_reinjecting_multicast, + status.is_reinjecting_point_to_point, + status.is_reinjecting_nearest_neighbour, + status.is_reinjecting_fixed_route)) class Section(object): @@ -151,7 +158,7 @@ def print_transceiver_tests(transceiver): version_info = transceiver.ensure_board_is_ready() print(version_info) - app_id = transceiver.app_id_tracker.get_new_id() + app_id = SpiNNManDataView().get_new_id() with Section("Discovering other connections to the machine"): connections = transceiver.discover_scamp_connections() @@ -184,7 +191,7 @@ def print_transceiver_tests(transceiver): time.sleep(0.1) with Section("CPU Information"): - cpu_infos = transceiver.get_cpu_information(core_subsets) + cpu_infos = transceiver.get_cpu_infos(core_subsets) cpu_infos = sorted(cpu_infos, key=lambda x: (x.x, x.y, x.p)) print("{} CPUs".format(len(cpu_infos))) for cpu_info in cpu_infos: @@ -206,7 +213,7 @@ def print_transceiver_tests(transceiver): with Section("Stop Application"): transceiver.send_signal(app_id, Signal.STOP) time.sleep(0.5) - cpu_infos = transceiver.get_cpu_information(core_subsets) + cpu_infos = transceiver.get_cpu_infos(core_subsets) cpu_infos = sorted(cpu_infos, key=lambda x: (x.x, x.y, x.p)) print("{} CPUs".format(len(cpu_infos))) for cpu_info in cpu_infos: @@ -302,12 +309,12 @@ def print_transceiver_tests(transceiver): data = struct.unpack( ". - --r requirements.txt -flake8 -numpy -# pytest will be brought in by pytest-cov -pytest-cov -sphinx==1.5.3 diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 05f78b81b..000000000 --- a/requirements.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -six -future -enum34 -futures; python_version == "2.7" -SpiNNUtilities >= 1!5.0.1, < 1!6.0.0 -SpiNNMachine >= 1!5.0.1, < 1!6.0.0 -SpiNNStorageHandlers >= 1!5.0.1, < 1!6.0.0 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 000000000..b5552c79e --- /dev/null +++ b/setup.cfg @@ -0,0 +1,73 @@ +# Copyright (c) 2023 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +[metadata] +name = SpiNNMan +version = attr: spinnman._version.__version__ +description = Interaction with a SpiNNaker Machine +long_description = file: README.md +long_description_content_type = text/markdown +url = https://github.com/SpiNNakerManchester/SpiNNMan +license = Apache-2.0 +classifiers= + Development Status :: 5 - Production/Stable + Intended Audience :: Developers + Intended Audience :: Science/Research + License :: OSI Approved :: Apache Software License + Natural Language :: English + Operating System :: POSIX :: Linux + Operating System :: Microsoft :: Windows + Operating System :: MacOS + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 +maintainer = SpiNNakerTeam +maintainer_email = spinnakerusers@googlegroups.com +keywords = + spinnaker + machine interface + +[options] +python_requires = >=3.7, <4 +packages = find: +zip_safe = True +include_package_data = True +install_requires = + SpiNNMachine == 1!7.1.0 + websocket-client + +[options.packages.find] +include = + spinnman + spinnman.* + +[options.package_data] +* = + spinnman.cfg + board_test_configuration.cfg + scamp.boot + +[options.extras_require] +test = + # pytest will be brought in by pytest-cov + pytest-cov + testfixtures + httpretty != 1.0.0 + +[options.entry_points] +console_scripts = get_cores_in_run_state = spinnman.get_cores_in_run_state:main + diff --git a/setup.py b/setup.py index 204327ef8..e9de24390 100644 --- a/setup.py +++ b/setup.py @@ -1,91 +1,33 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2023 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -try: - from setuptools import setup -except ImportError: - from distutils.core import setup -try: - from collections.abc import defaultdict -except ImportError: - from collections import defaultdict +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import distutils.dir_util +from setuptools import setup import os - -__version__ = None -exec(open("spinnman/_version.py").read()) -assert __version__ - -# Build a list of all project modules, as well as supplementary files -main_package = "spinnman" -extensions = {".aplx", ".boot", ".cfg", ".json", ".sql", ".template", ".xml", - ".xsd"} -main_package_dir = os.path.join(os.path.dirname(__file__), main_package) -start = len(main_package_dir) -packages = [] -package_data = defaultdict(list) -for dirname, dirnames, filenames in os.walk(main_package_dir): - if '__init__.py' in filenames: - package = "{}{}".format( - main_package, dirname[start:].replace(os.sep, '.')) - packages.append(package) - for filename in filenames: - _, ext = os.path.splitext(filename) - if ext in extensions: - package = "{}{}".format( - main_package, dirname[start:].replace(os.sep, '.')) - package_data[package].append(filename) - -setup( - name="SpiNNMan", - version=__version__, - description="Interaction with a SpiNNaker Machine", - url="https://github.com/SpiNNakerManchester/SpiNNMan", - license="GNU GPLv3.0", - classifiers=[ - "Development Status :: 3 - Alpha", - - "Intended Audience :: Developers", - "Intended Audience :: Science/Research", - - "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", - - "Natural Language :: English", - - "Operating System :: POSIX :: Linux", - "Operating System :: Microsoft :: Windows", - "Operating System :: MacOS", - - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - ], - packages=packages, - package_data=package_data, - install_requires=[ - 'SpiNNUtilities >= 1!5.0.1, < 1!6.0.0', - 'SpiNNStorageHandlers >= 1!5.0.1, < 1!6.0.0', - 'SpiNNMachine >= 1!5.0.1, < 1!6.0.0', - 'enum34', - 'future', - 'futures; python_version == "2.7"', - 'six'], - entry_points={ - "console_scripts": [ - "get_cores_in_run_state = spinnman.get_cores_in_run_state:main"] - }, - maintainer="SpiNNakerTeam", - maintainer_email="spinnakerusers@googlegroups.com" -) +import sys + + +if __name__ == '__main__': + # Repeated installs assume files have not changed + # https://github.com/pypa/setuptools/issues/3236 + if len(sys.argv) > 0 and sys.argv[1] == 'egg_info': + # on the first call to setpy.py remove files left by previous install + this_dir = os.path.dirname(os.path.abspath(__file__)) + build_dir = os.path.join(this_dir, "build") + if os.path.isdir(build_dir): + distutils.dir_util.remove_tree(build_dir) + egg_dir = os.path.join(this_dir, "SpiNNMan.egg-info") + if os.path.isdir(egg_dir): + distutils.dir_util.remove_tree(egg_dir) + setup() diff --git a/spinnman/__init__.py b/spinnman/__init__.py index 249552591..65d927987 100644 --- a/spinnman/__init__.py +++ b/spinnman/__init__.py @@ -1,144 +1,135 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -""" Used to communicate with a SpiNNaker Board. The main part of this package\ - is the :py:class:`spinnman.transceiver.Transceiver` class. This can be\ - used to send and receive packets in various SpiNNaker formats, depending\ - on what connections are available. +""" +Used to communicate with a SpiNNaker Board. The main part of this package is +the :py:class:`~spinnman.transceiver.Transceiver` class. This can be used to +send and receive packets in various SpiNNaker formats, depending on what +connections are available. - Functional Requirements - ======================= +Functional Requirements +======================= - * Connect to and communicate with a machine using a number of\ - different connections. +#. *Connect* to and communicate with a machine using a number of different + connections. - * Boot a machine with the expected version of the software. +#. *Boot* a machine with the expected version of the software. - * If the machine is already booted but the version is not the\ - version expected, an exception will be thrown. + * If the machine is already booted but the version is not the version + expected, an exception will be thrown. - * Check the version of the software which the machine is booted with. +#. *Check the version* of the software which the machine is booted with. - * Query the state of the machine to determine: +#. *Query the state* of the machine to determine: - * What the current state of the machine is in terms of the chips\ - and cores available, the SDRAM available on the chips and the\ - which links are available between which chips. + * What the current state of the machine is in terms of the chips and cores + available, the SDRAM available on the chips and which links are available + between which chips. - * What external links to the host exist (and separately add the\ - discovered links to the set of links used to communicate with\ - the machine). + * What external links to the host exist (and separately add the discovered + links to the set of links used to communicate with the machine). - * What is running on the machine and where, and what the current\ - status of those processes are. + * What is running on the machine and where, and what the current status of + those processes are. - * How many cores are in a given state. + * How many cores are in a given state. - * What is in the IOBUF buffers. + * What is in the IOBUF buffers. - * What the current routing entries for a given router are. + * What the current routing entries for a given router are. - * What the routing status counter values are. + * What the routing status counter values are. - * Load application binaries on to the machine, either to individual\ - cores or via a "flood-fill" mechanism to multiple cores\ - simultaneously (which may be a subset of the cores on a subset of\ - the chips). +#. *Load application binaries* on to the machine, either to individual cores or + via a "flood-fill" mechanism to multiple cores simultaneously (which may be + a subset of the cores on a subset of the chips). - * Write data to SDRAM, either on an individual chip, or via a\ - "flood-fill" mechanism to multiple chips simultaneously. +#. *Write data to SDRAM*, either on an individual chip, or via a "flood-fill" + mechanism to multiple chips simultaneously. - * Send a signal to an application. +#. Send a *signal* to an application. - * Read data from SDRAM on an individual chip. +#. *Read data from SDRAM* on an individual chip. - * Send and receive SpiNNaker packets where the connections allow this. +#. *Send and receive SpiNNaker packets* where the connections allow this. - * If no connection supports this packet type, an exception is\ - thrown. + * If no connection supports this packet type, an exception is thrown. - * The user should be able to select which connection is used.\ - Selection of a connection which does not support the traffic\ - type will also result in an exception. + * The user should be able to select which connection is used. Selection of a + connection which does not support the traffic type will also result in an + exception. - * Send and receive SCP and SDP packets where the connections allow\ - this. +#. *Send and receive SCP and SDP* packets where the connections allow this. - * If no connection supports the packet type, an exception is\ - thrown. + * If no connection supports the packet type, an exception is thrown. - * The user should be able to select which connection is used.\ - Selection of a connection which does not support the traffic\ - type will also result in an exception. + * The user should be able to select which connection is used. Selection of a + connection which does not support the traffic type will also result in an + exception. - * It should be possible to call any of the functions simultaneously,\ - including the same function more than once. +#. It should be possible to *call* any of the functions *simultaneously,* + including the same function more than once. - * Where possible, multiple connections should be used to overlap\ - calls. + * Where possible, multiple connections should be used to overlap calls. - * The functions should not return until they have confirmed that\ - any messages sent have been received, and any responses have\ - been received. + * The functions should not return until they have confirmed that any + messages sent have been received, and any responses have been received. - * Functions should not respond with the result of a different\ - function. + * Functions should not respond with the result of a different function. - * Functions can further sub-divide the call into a number of\ - separate calls that can be divided across the available\ - connections, so long as the other requirements are met. + * Functions can further sub-divide the call into a number of separate calls + that can be divided across the available connections, so long as the other + requirements are met. - * More than one machine can be connected to the same host. +#. *More than one machine* can be connected to the same host. - * Once the subset of connections has been worked out for each\ - machine, the operation of these machines should be independent. + * Once the subset of connections has been worked out for each machine, the + operation of these machines should be independent. - Use Cases - ========= +Use Cases +========= - * :py:meth:`~spinnman.transceiver.Transceiver.boot_board` and\ - :py:meth:`~spinnman.transceiver.Transceiver.get_scamp_version` are\ - used to ensure that the board is booted correctly before starting a\ - simulation. +* Connecting is done by using + :py:func:`~spinnman.transceiver.create_transceiver_from_hostname`. - * :py:meth:`~spinnman.transceiver.Transceiver.get_machine_details` is\ - used to get a representation of the current state of the machine,\ - which is used to decide where executables are to be run on the board\ - for a particular simulation, where any external peripherals are\ - connected, and how messages between the executables and/or the\ - external peripherals are to be routed +* :py:meth:`~spinnman.transceiver.Transceiver.boot_board` and + :py:meth:`~spinnman.transceiver.Transceiver.get_scamp_version` are used to + ensure that the board is booted correctly before starting a simulation. - * :py:meth:`~spinnman.transceiver.Transceiver.write_memory` and\ - :py:meth:`~spinnman.transceiver.Transceiver.execute` are used to\ - write parameters and execute executables on the board +* :py:meth:`~spinnman.transceiver.Transceiver.get_machine_details` is used to + get a representation of the current state of the machine, which is used to + decide where executables are to be run on the board for a particular + simulation, where any external peripherals are connected, and how messages + between the executables and/or the external peripherals are to be routed. - * :py:meth:`~spinnman.transceiver.Transceiver.send_signal` is used to\ - send a signal which starts, stops or pauses a simulation. +* :py:meth:`~spinnman.transceiver.Transceiver.write_memory` and + :py:meth:`~spinnman.transceiver.Transceiver.execute` are used to write + parameters and execute executables on the board - * :py:meth:`~spinnman.transceiver.Transceiver.get_core_status_count`\ - is used to determine if a simulation is complete or has gone into an\ - error state. +* :py:meth:`~spinnman.transceiver.Transceiver.send_signal` is used to send a + signal which starts, stops or pauses a simulation. - * :py:meth:`~spinnman.transceiver.Transceiver.get_iobuf`,\ - :py:meth:`~spinnman.transceiver.Transceiver.get_cpu_information` and\ - :py:meth:`~spinnman.transceiver.Transceiver.get_router_diagnostics`\ - are used to diagnose a problem with a simulation +* :py:meth:`~spinnman.transceiver.Transceiver.get_core_state_count` is used to + determine if a simulation is complete or has gone into an error state. - * :py:meth:`~spinnman.transceiver.Transceiver.read_memory` is used\ - to read some statistics recorded in SDRAM after a simulation +* :py:meth:`~spinnman.transceiver.Transceiver.get_iobuf`, + :py:meth:`~spinnman.transceiver.Transceiver.get_cpu_infos` and + :py:meth:`~spinnman.transceiver.Transceiver.get_router_diagnostics` are used + to diagnose a problem with a simulation. +* :py:meth:`~spinnman.transceiver.Transceiver.read_memory` is used to read some + statistics recorded in SDRAM after a simulation. """ from spinnman._version import __version__ # NOQA from spinnman._version import __version_name__ # NOQA diff --git a/spinnman/_version.py b/spinnman/_version.py index 6284d6f50..6288012f2 100644 --- a/spinnman/_version.py +++ b/spinnman/_version.py @@ -1,20 +1,19 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -__version__ = "1!5.0.1" -__version_month__ = "August" -__version_year__ = "2019" -__version_day__ = "09" -__version_name__ = "Liveware Problem" +__version__ = "1!7.1.0" +__version_month__ = "TBD" +__version_year__ = "TBD" +__version_day__ = "TBD" +__version_name__ = "Not yet released" diff --git a/spinnman/board_test_configuration.py b/spinnman/board_test_configuration.py new file mode 100644 index 000000000..329c625fd --- /dev/null +++ b/spinnman/board_test_configuration.py @@ -0,0 +1,64 @@ +# Copyright (c) 2015 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from spinn_utilities.config_holder import set_config +from spinn_utilities.ping import Ping +# from spinnman.model import BMPConnectionData + +_LOCALHOST = "127.0.0.1" +# Microsoft invalid IP address. For more details see: +# https://answers.microsoft.com/en-us/windows/forum/windows_vista-networking/invalid-ip-address-169254xx/ce096728-e2b7-4d54-80cc-52a4ed342870 +_NOHOST = "169.254.254.254" +_PORT = 54321 + + +class BoardTestConfiguration(object): + + def __init__(self): + self.localport = None + self.remotehost = None + self.board_version = None + self.bmp_names = None + self.auto_detect_bmp = None + + def set_up_local_virtual_board(self): + self.localport = _PORT + self.remotehost = _LOCALHOST + self.board_version = 5 + + def set_up_remote_board(self): + if Ping.host_is_reachable("192.168.240.253"): + self.remotehost = "192.168.240.253" + self.board_version = 3 + set_config("Machine", "version", 3) + self.auto_detect_bmp = False + elif Ping.host_is_reachable("spinn-4.cs.man.ac.uk"): + self.remotehost = "spinn-4.cs.man.ac.uk" + self.board_version = 5 + set_config("Machine", "version", 5) + elif Ping.host_is_reachable("192.168.240.1"): + self.remotehost = "192.168.240.1" + self.board_version = 5 + set_config("Machine", "version", 5) + else: + raise unittest.SkipTest("None of the test boards reachable") + + # it always was None but this is what to do if not + # self.bmp_names = BMPConnectionData(0, 0, self.bmp_names, [0], None) + + def set_up_nonexistent_board(self): + self.localport = _PORT + self.remotehost = _NOHOST + self.board_version = None diff --git a/spinnman/config_setup.py b/spinnman/config_setup.py new file mode 100644 index 000000000..9a13d44be --- /dev/null +++ b/spinnman/config_setup.py @@ -0,0 +1,42 @@ +# Copyright (c) 2017 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from spinn_utilities.config_holder import ( + add_default_cfg, clear_cfg_files) +from spinn_machine.config_setup import add_spinn_machine_cfg +from spinnman.data.spinnman_data_writer import SpiNNManDataWriter + +BASE_CONFIG_FILE = "spinnman.cfg" + + +def unittest_setup(): + """ + Resets the configurations so only the local default configuration is + included. + + .. note:: + This file should only be called from `SpiNNMan/unittests`. + """ + clear_cfg_files(True) + add_spinnman_cfg() + SpiNNManDataWriter.mock() + + +def add_spinnman_cfg(): + """ + Add the local configuration and all dependent configuration files. + """ + add_spinn_machine_cfg() # This add its dependencies too + add_default_cfg(os.path.join(os.path.dirname(__file__), BASE_CONFIG_FILE)) diff --git a/spinnman/connections/__init__.py b/spinnman/connections/__init__.py index 663d8f961..25d1eca41 100644 --- a/spinnman/connections/__init__.py +++ b/spinnman/connections/__init__.py @@ -1,22 +1,19 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .connection_listener import ConnectionListener from .scp_request_pipeline import SCPRequestPipeLine -from .socket_address_with_chip import SocketAddressWithChip from .token_bucket import TokenBucket -__all__ = ["ConnectionListener", "SCPRequestPipeLine", - "SocketAddressWithChip", "TokenBucket"] +__all__ = ["ConnectionListener", "SCPRequestPipeLine", "TokenBucket"] diff --git a/spinnman/connections/abstract_classes/__init__.py b/spinnman/connections/abstract_classes/__init__.py index 8ba62396a..ba4a8be97 100644 --- a/spinnman/connections/abstract_classes/__init__.py +++ b/spinnman/connections/abstract_classes/__init__.py @@ -1,34 +1,19 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .connection import Connection -from .eieio_receiver import EIEIOReceiver -from .eieio_sender import EIEIOSender from .listenable import Listenable -from .multicast_receiver import MulticastReceiver -from .multicast_sender import MulticastSender -from .scp_receiver import SCPReceiver -from .scp_sender import SCPSender -from .sdp_receiver import SDPReceiver -from .sdp_sender import SDPSender -from .spinnaker_boot_receiver import SpinnakerBootReceiver -from .spinnaker_boot_sender import SpinnakerBootSender +from .abstract_scp_connection import AbstractSCPConnection -__all__ = ["Connection", "EIEIOReceiver", - "EIEIOSender", "Listenable", - "MulticastReceiver", "MulticastSender", - "SCPReceiver", "SCPSender", "SDPReceiver", - "SDPSender", "SpinnakerBootReceiver", - "SpinnakerBootSender"] +__all__ = ("Connection", "Listenable", "AbstractSCPConnection") diff --git a/spinnman/connections/abstract_classes/abstract_scp_connection.py b/spinnman/connections/abstract_classes/abstract_scp_connection.py new file mode 100644 index 000000000..3a62ceaea --- /dev/null +++ b/spinnman/connections/abstract_classes/abstract_scp_connection.py @@ -0,0 +1,102 @@ +# Copyright (c) 2014 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from spinn_utilities.abstract_base import ( + AbstractBase, abstractmethod, abstractproperty) +from .connection import Connection + + +class AbstractSCPConnection(Connection, metaclass=AbstractBase): + """ + A sender and receiver of SCP messages. + """ + + __slots__ = () + + @abstractmethod + def is_ready_to_receive(self, timeout=0): + """ + Determines if there is an SCP packet to be read without blocking. + + :param int timeout: + The time to wait before returning if the connection is not ready + :return: True if there is an SCP packet to be read + :rtype: bool + """ + + @abstractmethod + def receive_scp_response(self, timeout=1.0): + """ + Receives an SCP response from this connection. Blocks + until a message has been received, or a timeout occurs. + + :param int timeout: + The time in seconds to wait for the message to arrive; if not + specified, will wait forever, or until the connection is closed + :return: The SCP result, the sequence number, the data of the response + and the offset at which the data starts (i.e., where the SDP + header starts). + :rtype: tuple(SCPResult, int, bytes, int) + :raise SpinnmanIOException: + If there is an error receiving the message + :raise SpinnmanTimeoutException: + If there is a timeout before a message is received + """ + + @abstractmethod + def get_scp_data(self, scp_request): + """ + Returns the data of an SCP request as it would be sent down this + connection. + """ + + @abstractmethod + def send_scp_request(self, scp_request): + """ + Sends an SCP request down this connection. + + Messages must have the following properties: + + * source_port is `None` or 7 + * source_cpu is `None` or 31 + * source_chip_x is `None` or 0 + * source_chip_y is `None` or 0 + + tag in the message is optional; if not set, the default set in the + constructor will be used. + sequence in the message is optional; if not set, (sequence number + last assigned + 1) % 65536 will be used + + :param AbstractSCPRequest scp_request: message packet to send + :raise SpinnmanIOException: + If there is an error sending the message + """ + + @abstractproperty + def chip_x(self): + """ + The X-coordinate of the chip at which messages sent down this + connection will arrive at first. + + :rtype: int + """ + + @abstractproperty + def chip_y(self): + """ + The Y-coordinate of the chip at which messages sent down this + connection will arrive at first. + + :rtype: int + """ diff --git a/spinnman/connections/abstract_classes/connection.py b/spinnman/connections/abstract_classes/connection.py index 81927bb59..380c4381c 100644 --- a/spinnman/connections/abstract_classes/connection.py +++ b/spinnman/connections/abstract_classes/connection.py @@ -1,45 +1,42 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -from six import add_metaclass from spinn_utilities.abstract_base import AbstractBase, abstractmethod +from spinn_utilities.abstract_context_manager import AbstractContextManager -@add_metaclass(AbstractBase) -class Connection(object): - """ An abstract connection to the SpiNNaker board over some medium +class Connection(AbstractContextManager, metaclass=AbstractBase): + """ + An abstract connection to the SpiNNaker board over some medium. """ __slots__ = () @abstractmethod def is_connected(self): - """ Determines if the medium is connected at this point in time + """ + Determines if the medium is connected at this point in time. :return: True if the medium is connected, False otherwise :rtype: bool - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error when determining the connectivity of the\ + :raise SpinnmanIOException: + If there is an error when determining the connectivity of the medium. """ @abstractmethod def close(self): - """ Closes the connection - - :return: Nothing is returned - :rtype: None - :raise None: No known exceptions are raised + """ + Closes the connection. """ diff --git a/spinnman/connections/abstract_classes/eieio_receiver.py b/spinnman/connections/abstract_classes/eieio_receiver.py deleted file mode 100644 index 7290f38e5..000000000 --- a/spinnman/connections/abstract_classes/eieio_receiver.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from six import add_metaclass -from spinn_utilities.abstract_base import AbstractBase, abstractmethod -from .connection import Connection - - -@add_metaclass(AbstractBase) -class EIEIOReceiver(Connection): - """ A receiver of EIEIO data or commands - """ - - __slots__ = () - - @abstractmethod - def receive_eieio_message(self, timeout=None): - """ Receives an EIEIO message from this connection. Blocks until\ - a message has been received, or a timeout occurs. - - :param timeout: \ - The time in seconds to wait for the message to arrive; if not\ - specified, will wait forever, or until the connection is closed - :type timeout: int - :return: an EIEIO message - :rtype:\ - :py:class:`spinnman.messages.eieio.AbstractEIEIOMessage` - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error receiving the message. - :raise spinnman.exceptions.SpinnmanTimeoutException: \ - If there is a timeout before a message is received. - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ - If the received packet is not a valid EIEIO message. - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ - If one of the fields of the EIEIO message is invalid. - """ diff --git a/spinnman/connections/abstract_classes/eieio_sender.py b/spinnman/connections/abstract_classes/eieio_sender.py deleted file mode 100644 index 36b974b80..000000000 --- a/spinnman/connections/abstract_classes/eieio_sender.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from six import add_metaclass -from spinn_utilities.abstract_base import AbstractBase, abstractmethod -from .connection import Connection - - -@add_metaclass(AbstractBase) -class EIEIOSender(Connection): - """ A sender of EIEIO messages - """ - - __slots__ = () - - @abstractmethod - def send_eieio_message(self, eieio_message): - """ Sends an EIEIO message down this connection - - :param eieio_message: The EIEIO message to be sent - :type eieio_message: \ - :py:class:`spinnman.messages.eieio.AbstractEIEIOMessage` - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error sending the message - """ diff --git a/spinnman/connections/abstract_classes/listenable.py b/spinnman/connections/abstract_classes/listenable.py index 7e8ebb673..b1e09a3ce 100644 --- a/spinnman/connections/abstract_classes/listenable.py +++ b/spinnman/connections/abstract_classes/listenable.py @@ -1,39 +1,44 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -from six import add_metaclass from spinn_utilities.abstract_base import AbstractBase, abstractmethod -@add_metaclass(AbstractBase) -class Listenable(object): +# Should inherit from Connection, but doesn't for MRO reasons +class Listenable(object, metaclass=AbstractBase): + """ + An interface for connections that can listen for incoming messages. + + Implementing this interface means that the connection can be used with + :py:class:`ConnectionListener`. + """ __slots__ = () @abstractmethod def get_receive_method(self): - """ Get the method that receives for this connection + """ + Get the method that receives for this connection. """ @abstractmethod def is_ready_to_receive(self, timeout=0): - """ Determines if there is an SCP packet to be read without blocking + """ + Determines if there is an SCP packet to be read without blocking. - :param timeout: \ + :param int timeout: The time to wait before returning if the connection is not ready - :type timeout: int :return: True if there is an SCP packet to be read :rtype: bool """ diff --git a/spinnman/connections/abstract_classes/multicast_receiver.py b/spinnman/connections/abstract_classes/multicast_receiver.py deleted file mode 100644 index 422aaa772..000000000 --- a/spinnman/connections/abstract_classes/multicast_receiver.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from six import add_metaclass -from spinn_utilities.abstract_base import AbstractBase, abstractmethod -from .connection import Connection - - -@add_metaclass(AbstractBase) -class MulticastReceiver(Connection): - """ A receiver of multicast messages - """ - - __slots__ = () - - @abstractmethod - def get_input_chips(self): - """ Get a list of chips which identify the chips from which this\ - receiver can receive receive packets directly - - :return: The coordinates, (x, y), of the chips - :rtype: iterable of (int, int) - :raise None: No known exceptions are raised - """ - - @abstractmethod - def receive_multicast_message(self, timeout=None): - """ Receives a multicast message from this connection. Blocks until\ - a message has been received, or a timeout occurs. - - :param timeout: \ - The time in seconds to wait for the message to arrive; if not\ - specified, will wait forever, or until the connection is closed - :type timeout: int - :return: a multicast message - :rtype:\ - :py:class:`spinnman.messages.multicast_message.MulticastMessage` - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error receiving the message. - :raise spinnman.exceptions.SpinnmanTimeoutException: \ - If there is a timeout before a message is received. - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ - If the received packet is not a valid multicast message. - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ - If one of the fields of the multicast message is invalid. - """ diff --git a/spinnman/connections/abstract_classes/multicast_sender.py b/spinnman/connections/abstract_classes/multicast_sender.py deleted file mode 100644 index fd5ed52ca..000000000 --- a/spinnman/connections/abstract_classes/multicast_sender.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from six import add_metaclass -from spinn_utilities.abstract_base import AbstractBase, abstractmethod -from .connection import Connection - - -@add_metaclass(AbstractBase) -class MulticastSender(Connection): - """ A sender of Multicast messages - """ - - __slots__ = () - - @abstractmethod - def get_input_chips(self): - """ Get a list of chips which identify the chips to which this sender\ - can send multicast packets directly - - :return: The coordinates, (x, y), of the chips - :rtype: iterable of (int, int) - :raise None: No known exceptions are raised - """ - - @abstractmethod - def send_multicast_message(self, multicast_message): - """ Sends a SpiNNaker multicast message using this connection - - :param multicast_message: The message to be sent - :type multicast_message:\ - :py:class:`spinnman.messages.multicast_message.MulticastMessage` - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error sending the message - """ diff --git a/spinnman/connections/abstract_classes/scp_receiver.py b/spinnman/connections/abstract_classes/scp_receiver.py deleted file mode 100644 index 4e302816c..000000000 --- a/spinnman/connections/abstract_classes/scp_receiver.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from six import add_metaclass -from spinn_utilities.abstract_base import AbstractBase, abstractmethod -from .connection import Connection - - -@add_metaclass(AbstractBase) -class SCPReceiver(Connection): - """ A receiver of SCP messages - """ - - __slots__ = () - - @abstractmethod - def is_ready_to_receive(self, timeout=0): - """ Determines if there is an SCP packet to be read without blocking - - :param timeout: \ - The time to wait before returning if the connection is not ready - :type timeout: int - :return: True if there is an SCP packet to be read - :rtype: bool - """ - - @abstractmethod - def receive_scp_response(self, timeout=1.0): - """ Receives an SCP response from this connection. Blocks\ - until a message has been received, or a timeout occurs. - - :param timeout: \ - The time in seconds to wait for the message to arrive; if not\ - specified, will wait forever, or until the connection is closed - :type timeout: int - :return: The SCP result, the sequence number, the data of the response\ - and the offset at which the data starts (i.e., where the SDP\ - header starts). - :rtype: tuple(:py:class:`spinnman.messages.scp.scp_result.SCPResult`,\ - int, bytestring, int) - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error receiving the message - :raise spinnman.exceptions.SpinnmanTimeoutException: \ - If there is a timeout before a message is received - """ diff --git a/spinnman/connections/abstract_classes/scp_sender.py b/spinnman/connections/abstract_classes/scp_sender.py deleted file mode 100644 index 637d63451..000000000 --- a/spinnman/connections/abstract_classes/scp_sender.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from six import add_metaclass -from spinn_utilities.abstract_base import ( - AbstractBase, abstractmethod, abstractproperty) -from .connection import Connection - - -@add_metaclass(AbstractBase) -class SCPSender(Connection): - """ A sender of SCP messages - """ - - __slots__ = () - - @abstractmethod - def get_scp_data(self, scp_request): - """ Returns the data of an SCP request as it would be sent down this\ - connection - """ - - @abstractmethod - def send_scp_request(self, scp_request): - """ Sends an SCP request down this connection - - Messages must have the following properties: - - * source_port is None or 7 - * source_cpu is None or 31 - * source_chip_x is None or 0 - * source_chip_y is None or 0 - - tag in the message is optional; if not set, the default set in the\ - constructor will be used. - sequence in the message is optional; if not set, (sequence number\ - last assigned + 1) % 65536 will be used - - :param scp_request: message packet to send - :type scp_request:\ - :py:class:`spinnman.messages.scp.abstract_scp_request.AbstractSCPRequest` - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error sending the message - """ - - @abstractproperty - def chip_x(self): - """ The x-coordinate of the chip at which messages sent down this\ - connection will arrive at first - - :rtype: int - """ - - @abstractproperty - def chip_y(self): - """ The y-coordinate of the chip at which messages sent down this\ - connection will arrive at first - - :rtype: int - """ diff --git a/spinnman/connections/abstract_classes/sdp_receiver.py b/spinnman/connections/abstract_classes/sdp_receiver.py deleted file mode 100644 index 71c766619..000000000 --- a/spinnman/connections/abstract_classes/sdp_receiver.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from six import add_metaclass -from spinn_utilities.abstract_base import AbstractBase, abstractmethod -from .connection import Connection - - -@add_metaclass(AbstractBase) -class SDPReceiver(Connection): - """ A receiver of SDP messages - """ - - __slots__ = () - - @abstractmethod - def receive_sdp_message(self, timeout=None): - """ Receives an SDP message from this connection. Blocks until the\ - message has been received, or a timeout occurs. - - :param timeout: \ - The time in seconds to wait for the message to arrive; if not\ - specified, will wait forever, or until the connection is closed. - :type timeout: int - :return: The received SDP message - :rtype: :py:class:`spinnman.messages.sdp.sdp_message.SDPMessage` - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error receiving the message - :raise spinnman.exceptions.SpinnmanTimeoutException: \ - If there is a timeout before a message is received - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ - If the received packet is not a valid SDP message - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ - If one of the fields of the SDP message is invalid - """ diff --git a/spinnman/connections/abstract_classes/sdp_sender.py b/spinnman/connections/abstract_classes/sdp_sender.py deleted file mode 100644 index ab5a4c80d..000000000 --- a/spinnman/connections/abstract_classes/sdp_sender.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from six import add_metaclass -from spinn_utilities.abstract_base import AbstractBase, abstractmethod -from .connection import Connection - - -@add_metaclass(AbstractBase) -class SDPSender(Connection): - """ A sender of SDP messages. - """ - - __slots__ = () - - @abstractmethod - def send_sdp_message(self, sdp_message): - """ Sends an SDP message down this connection - - :param sdp_message: The SDP message to be sent - :type sdp_message: spinnman.messages.sdp.sdp_message.SDPMessage - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error sending the message. - """ diff --git a/spinnman/connections/abstract_classes/spinnaker_boot_receiver.py b/spinnman/connections/abstract_classes/spinnaker_boot_receiver.py deleted file mode 100644 index c3f46a62b..000000000 --- a/spinnman/connections/abstract_classes/spinnaker_boot_receiver.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from six import add_metaclass -from spinn_utilities.abstract_base import AbstractBase, abstractmethod -from .connection import Connection - - -@add_metaclass(AbstractBase) -class SpinnakerBootReceiver(Connection): - """ A receiver of SpiNNaker boot messages - """ - - __slots__ = () - - @abstractmethod - def receive_boot_message(self, timeout=None): - """ Receives a boot message from this connection. Blocks until a\ - message has been received, or a timeout occurs. - - :param timeout: \ - The time in seconds to wait for the message to arrive; if not\ - specified, will wait forever, or until the connection is closed. - :type timeout: int - :return: a boot message - :rtype:\ - :py:class:`spinnman.messages.spinnaker_boot.SpinnakerBootMessage` - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error receiving the message - :raise spinnman.exceptions.SpinnmanTimeoutException: \ - If there is a timeout before a message is received - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ - If the received packet is not a valid SpiNNaker boot message - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ - If one of the fields of the SpiNNaker boot message is invalid - """ diff --git a/spinnman/connections/abstract_classes/spinnaker_boot_sender.py b/spinnman/connections/abstract_classes/spinnaker_boot_sender.py deleted file mode 100644 index 4fe092076..000000000 --- a/spinnman/connections/abstract_classes/spinnaker_boot_sender.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from six import add_metaclass -from spinn_utilities.abstract_base import AbstractBase, abstractmethod -from .connection import Connection - - -@add_metaclass(AbstractBase) -class SpinnakerBootSender(Connection): - """ A sender of SpiNNaker Boot messages - """ - - __slots__ = () - - @abstractmethod - def send_boot_message(self, boot_message): - """ Sends a SpiNNaker boot message using this connection. - - :param boot_message: The message to be sent - :type boot_message:\ - :py:class:`spinnman.messages.spinnaker_boot.SpinnakerBootMessage` - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error sending the message - """ diff --git a/spinnman/connections/connection_listener.py b/spinnman/connections/connection_listener.py index 0573e7919..635639162 100644 --- a/spinnman/connections/connection_listener.py +++ b/spinnman/connections/connection_listener.py @@ -1,88 +1,112 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import logging from threading import Thread from concurrent.futures import ThreadPoolExecutor +from spinn_utilities.abstract_context_manager import AbstractContextManager +from spinn_utilities.log import FormatAdapter +from spinnman.exceptions import SpinnmanEOFException -logger = logging.getLogger(__name__) +logger = FormatAdapter(logging.getLogger(__name__)) _POOL_SIZE = 4 _TIMEOUT = 1 -class ConnectionListener(Thread): - """ Thread that listens to a connection and calls callbacks with new\ - messages when they arrive. +class ConnectionListener(Thread, AbstractContextManager): + """ + Thread that listens to a connection and calls callbacks with new + messages when they arrive. """ __slots__ = [ - "_callback_pool", - "_callbacks", - "_connection", - "_done", - "_timeout"] + "__callback_pool", + "__callbacks", + "__connection", + "__done", + "__timeout"] def __init__(self, connection, n_processes=_POOL_SIZE, timeout=_TIMEOUT): """ - :param connection: An AbstractListenable connection to listen to - :param n_processes: \ + :param Listenable connection: A connection to listen to + :param int n_processes: The number of threads to use when calling callbacks - :param timeout: How long to wait for messages before checking to see\ - if the connection is to be terminated. + :param float timeout: + How long to wait for messages before checking to see if the + connection is to be terminated. """ - super(ConnectionListener, self).__init__( - name="Connection listener for connection {}".format(connection)) + super().__init__( + name=f"Connection listener for connection {connection}") self.daemon = True - self._connection = connection - self._timeout = timeout - self._callback_pool = ThreadPoolExecutor(max_workers=n_processes) - self._done = False - self._callbacks = set() + self.__connection = connection + self.__timeout = timeout + self.__callback_pool = ThreadPoolExecutor(max_workers=n_processes) + self.__done = False + self.__callbacks = set() - def _run_step(self, handler): - if self._connection.is_ready_to_receive(timeout=self._timeout): + def __run_step(self, handler): + """ + :param ~collections.abc.Callable handler: + """ + if self.__connection.is_ready_to_receive(timeout=self.__timeout): message = handler() - for callback in self._callbacks: - self._callback_pool.submit(callback, message) + for callback in self.__callbacks: + future = self.__callback_pool.submit(callback, message) + future.add_done_callback(self.__done_callback) - def run(self): + def __done_callback(self, future): + """ + :param ~concurrent.futures.Future future: + """ try: - handler = self._connection.get_receive_method() - while not self._done: + future.result() + except Exception: # pylint: disable=broad-except + logger.exception("problem in listener call") + + def run(self): + """ + Implements the listening thread. + """ + with self.__callback_pool: + handler = self.__connection.get_receive_method() + while not self.__done: try: - self._run_step(handler) - except Exception: - if not self._done: + self.__run_step(handler) + except SpinnmanEOFException: + self.__done = True + except Exception: # pylint: disable=broad-except + if not self.__done: logger.warning("problem when dispatching message", exc_info=True) - finally: - self._callback_pool.shutdown() - self._callback_pool = None def add_callback(self, callback): - """ Add a callback to be called when a message is received + """ + Add a callback to be called when a message is received. - :param callback: A callable which takes a single parameter, which is\ - the message received - :type callback: callable (connection message type -> None) + :param ~collections.abc.Callable callback: + A callable which takes a single parameter, which is the message + received; the result of the callback will be ignored. """ - self._callbacks.add(callback) + self.__callbacks.add(callback) def close(self): - """ Closes the listener. Note that this does not close the provider\ - of the messages; this instead marks the listener as closed. The\ - listener will not truly stop until the get message call returns. """ - self._done = True + Closes the listener. + + .. note:: + This does not close the provider of the messages; this instead + marks the listener as closed. The listener will not truly stop + until the get message call returns. + """ + self.__done = True self.join() diff --git a/spinnman/connections/scp_request_pipeline.py b/spinnman/connections/scp_request_pipeline.py index 8161c95ad..70f18f5bc 100644 --- a/spinnman/connections/scp_request_pipeline.py +++ b/spinnman/connections/scp_request_pipeline.py @@ -1,22 +1,20 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import sys from threading import RLock import time -from six import iteritems from spinnman.messages.scp.enums import SCPResult from spinnman.exceptions import SpinnmanTimeoutException, SpinnmanIOException from spinnman.constants import SCP_TIMEOUT, N_RETRIES @@ -32,15 +30,16 @@ class SCPRequestPipeLine(object): - """ Allows a set of SCP requests to be grouped together in a communication\ - across a number of channels for a given connection. - - This class implements an SCP windowing, first suggested by Andrew\ - Mundy. This extends the idea by having both send and receive windows.\ - These are represented by the n_channels and the\ - intermediate_channel_waits parameters respectively. This seems to\ - help with the timeout issue; when a timeout is received, all requests\ - for which a reply has not been received can also timeout. + """ + Allows a set of SCP requests to be grouped together in a communication + across a number of channels for a given connection. + + This class implements an SCP windowing, first suggested by Andrew Mundy. + This extends the idea by having both send and receive windows. + These are represented by the n_channels and the + intermediate_channel_waits parameters respectively. This seems to + help with the timeout issue; when a timeout is received, all requests + for which a reply has not been received can also timeout. """ __slots__ = [ "_callbacks", @@ -65,17 +64,17 @@ def __init__(self, connection, n_channels=1, intermediate_channel_waits=0, n_retries=N_RETRIES, packet_timeout=SCP_TIMEOUT): """ - :param connection: \ + :param SCAMPConnection connection: The connection over which the communication is to take place - :param n_channels: The number of requests to send before checking for\ - responses. If None, this will be determined automatically - :param intermediate_channel_waits: The number of outstanding responses\ - to wait for before continuing sending requests. If None, this will\ - be determined automatically - :param n_retries: The number of times to resend any packet for any\ + :param int n_channels: The number of requests to send before checking + for responses. If `None`, this will be determined automatically + :param int intermediate_channel_waits: The number of outstanding + responses to wait for before continuing sending requests. + If `None`, this will be determined automatically + :param int n_retries: The number of times to resend any packet for any reason before an error is triggered - :param packet_timeout: The number of elapsed seconds after sending a\ - packet before it is considered a timeout. + :param float packet_timeout: The number of elapsed seconds after + sending a packet before it is considered a timeout. """ self._connection = connection self._n_channels = n_channels @@ -122,31 +121,35 @@ def __init__(self, connection, n_channels=1, # self._token_bucket = TokenBucket(3408, 700000) def _get_next_sequence_number(self): - """ Get the next number from the global sequence, applying appropriate\ - wrapping rules as the sequence numbers have a fixed number of bits. + """ + Get the next number from the global sequence, applying appropriate + wrapping rules as the sequence numbers have a fixed number of bits. :return: The next number in the sequence. :rtype: int """ - global _next_sequence, _next_sequence_lock + # pylint: disable=global-statement + global _next_sequence with _next_sequence_lock: sequence = _next_sequence _next_sequence = (sequence + 1) % MAX_SEQUENCE return sequence def send_request(self, request, callback, error_callback): - """ Add an SCP request to the set to be sent + """ + Add an SCP request to the set to be sent. - :param request: The SCP request to be sent - :param callback: A callback function to call when the response has\ - been received; takes SCPResponse as a parameter, or None if the\ + :param AbstractSCPRequest request: The SCP request to be sent + :param ~collections.abc.Callable callback: + A callback function to call when the response has been received; + takes a :py:class:`SCPResponse` as a parameter, or `None` if the response doesn't need to be processed - :param error_callback: A callback function to call when an error is\ - found when processing the message; takes original\ - AbstractSCPRequest, exception caught and a list of tuples of\ - (filename, line number, function name, text) as a traceback + :param ~collections.abc.Callable error_callback: + A callback function to call when an error is found when processing + the message; takes the original :py:class:`AbstractSCPRequest`, the + exception caught and a list of tuples of (filename, line number, + function name, text) as a traceback """ - # If the connection has not been measured if self._n_channels is None: if self._connection.is_ready_to_receive(): @@ -180,26 +183,48 @@ def send_request(self, request, callback, error_callback): self._in_progress += 1 def finish(self): - """ Indicate the end of the packets to be sent. This must be called\ - to ensure that all responses are received and handled. + """ + Indicate the end of the packets to be sent. This must be called + to ensure that all responses are received and handled. """ while self._in_progress > 0: self._do_retrieve(0, self._packet_timeout) @property def n_timeouts(self): + """ + The number of timeouts that occurred. + + :rtype: int + """ return self._n_timeouts @property def n_channels(self): + """ + The number of requests to send before checking for responses. + + :rtype: int + """ return self._n_channels @property def n_resent(self): + """ + The number of packets that have been resent. + + :rtype: int + """ return self._n_resent @property def n_retry_code_resent(self): + """ + The number of resends due to reasons for which automated retry is + the correct response in-protocol. + + :rtype: int + """ return self._n_retry_code_resent def _remove_record(self, seq): @@ -227,9 +252,10 @@ def _single_retrieve(self, timeout): time.sleep(0.1) self._resend(seq, request_sent, str(result)) self._n_retry_code_resent += 1 - except Exception as e: + except Exception as e: # pylint: disable=broad-except self._error_callbacks[seq]( - request_sent, e, sys.exc_info()[2]) + request_sent, e, sys.exc_info()[2], + self._connection) self._remove_record(seq) else: @@ -239,9 +265,10 @@ def _single_retrieve(self, timeout): response.read_bytestring(raw_data, offset) if self._callbacks[seq] is not None: self._callbacks[seq](response) - except Exception as e: + except Exception as e: # pylint: disable=broad-except self._error_callbacks[seq]( - request_sent, e, sys.exc_info()[2]) + request_sent, e, sys.exc_info()[2], + self._connection) # Remove the sequence from the outstanding responses self._remove_record(seq) @@ -251,13 +278,14 @@ def _handle_receive_timeout(self): # If there is a timeout, all packets remaining are resent to_remove = list() - for seq, request_sent in iteritems(self._requests): + for seq, request_sent in self._requests.items(): self._in_progress -= 1 try: self._resend(seq, request_sent, "timeout") - except Exception as e: + except Exception as e: # pylint: disable=broad-except self._error_callbacks[seq]( - request_sent, e, sys.exc_info()[2]) + request_sent, e, sys.exc_info()[2], + self._connection) to_remove.append(seq) for seq in to_remove: @@ -268,18 +296,16 @@ def _resend(self, seq, request_sent, reason): # Report timeouts as timeout exception if all(reason == "timeout" for reason in self._retry_reason[seq]): raise SpinnmanTimeoutException( - request_sent.scp_request_header.command, + request_sent, self._packet_timeout) # Report any other exception raise SpinnmanIOException( - "Errors sending request {} to {}, {}, {} over {} retries: {}" - .format( - request_sent.scp_request_header.command, - request_sent.sdp_header.destination_chip_x, - request_sent.sdp_header.destination_chip_y, - request_sent.sdp_header.destination_cpu, - self._n_retries, self._retry_reason[seq])) + f"Errors sending request {request_sent} to " + f"{request_sent.sdp_header.destination_chip_x}, " + f"{request_sent.sdp_header.destination_chip_y}, " + f"{request_sent.sdp_header.destination_cpu} over " + f"{self._n_retries} retries: {self._retry_reason[seq]}") # If the request can be retried, retry it self._retries[seq] -= 1 @@ -290,11 +316,12 @@ def _resend(self, seq, request_sent, reason): self._n_resent += 1 def _do_retrieve(self, n_packets, timeout): - """ Receives responses until there are only n_packets responses left - - :param n_packets: The number of packets that can remain after running """ + Receives responses until there are only n_packets responses left. + :param int n_packets: + The number of packets that can remain after running + """ # While there are still more packets in progress than some threshold while self._in_progress > n_packets: try: diff --git a/spinnman/connections/socket_address_with_chip.py b/spinnman/connections/socket_address_with_chip.py deleted file mode 100644 index c2934a9d2..000000000 --- a/spinnman/connections/socket_address_with_chip.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from spinnman.constants import SCP_SCAMP_PORT - - -class SocketAddressWithChip(object): - """ The address of a socket and an associated chip - """ - __slots__ = [ - "_chip_x", "_chip_y", - "_hostname", - "_port_num"] - - def __init__(self, hostname, chip_x, chip_y, port_num=SCP_SCAMP_PORT): - self._hostname = hostname - self._port_num = port_num - self._chip_x = chip_x - self._chip_y = chip_y - - @property - def hostname(self): - """ The hostname of the socket - - :return: the hostname - """ - return self._hostname - - @property - def port_num(self): - """ The port number of the socket - - :return: the port - """ - return self._port_num - - @property - def chip_x(self): - """ The x-coordinate of the chip - - :return: the x-coordinate - """ - return self._chip_x - - @property - def chip_y(self): - """ The y-coordinate of the chip - - :return: the y-coordinate - """ - return self._chip_y - - def __str__(self): - return "{}:{}:{}:{}".format( - self._hostname, self._port_num, self._chip_x, self._chip_y) diff --git a/spinnman/connections/token_bucket.py b/spinnman/connections/token_bucket.py index 76b4b1713..750b955b5 100644 --- a/spinnman/connections/token_bucket.py +++ b/spinnman/connections/token_bucket.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import time class TokenBucket(object): - """ An implementation of the token bucket algorithm. Usage:: + """ + An implementation of the token bucket algorithm. Usage:: >>> bucket = TokenBucket(80, 0.5) >>> print(bucket.consume(10)) @@ -30,8 +30,8 @@ class TokenBucket(object): def __init__(self, tokens, fill_rate): """ - :param tokens: the total tokens in the bucket - :param fill_rate:\ + :param int tokens: the total tokens in the bucket + :param float fill_rate: the rate in tokens/second that the bucket will be refilled. """ self._capacity = float(tokens) @@ -40,15 +40,20 @@ def __init__(self, tokens, fill_rate): self._timestamp = time.time() def consume(self, tokens, block=True): - """ Consume tokens from the bucket. Returns True if there were\ - sufficient tokens. + """ + Consume tokens from the bucket. Returns True if there were + sufficient tokens. - If there are not enough tokens and block is True, sleeps until the\ + If there are not enough tokens and block is True, sleeps until the bucket is replenished enough to satisfy the deficiency. If there are not enough tokens and block is False, returns False. - It is an error to consume more tokens than the bucket _capacity. + It is an error to consume more tokens than the bucket capacity. + + :param int tokens: + :param bool block: + :rtype: bool """ while block and tokens > self.tokens: deficit = tokens - self._tokens @@ -62,6 +67,11 @@ def consume(self, tokens, block=True): @property def tokens(self): + """ + The number of tokens currently in the bucket. + + :rtype: int + """ if self._tokens < self._capacity: now = time.time() delta = self._fill_rate * (now - self._timestamp) diff --git a/spinnman/connections/udp_packet_connections/__init__.py b/spinnman/connections/udp_packet_connections/__init__.py index 99b9db9a6..12185b858 100644 --- a/spinnman/connections/udp_packet_connections/__init__.py +++ b/spinnman/connections/udp_packet_connections/__init__.py @@ -1,22 +1,20 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .bmp_connection import BMPConnection from .boot_connection import BootConnection from .udp_connection import UDPConnection -from .udp_listenable_connection import UDPListenableConnection from .eieio_connection import EIEIOConnection from .ip_address_connection import IPAddressesConnection from .scamp_connection import SCAMPConnection @@ -25,6 +23,5 @@ __all__ = ["BMPConnection", "BootConnection", "UDPConnection", "EIEIOConnection", "IPAddressesConnection", - "UDPListenableConnection", "SCAMPConnection", "SDPConnection", "update_sdp_header_for_udp_send"] diff --git a/spinnman/connections/udp_packet_connections/bmp_connection.py b/spinnman/connections/udp_packet_connections/bmp_connection.py index 9fa3cfff8..43ad50002 100644 --- a/spinnman/connections/udp_packet_connections/bmp_connection.py +++ b/spinnman/connections/udp_packet_connections/bmp_connection.py @@ -1,108 +1,91 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinn_utilities.overrides import overrides from .udp_connection import UDPConnection -from .utils import update_sdp_header_for_udp_send from spinnman.constants import SCP_SCAMP_PORT from spinnman.messages.scp.enums import SCPResult -from spinnman.connections.abstract_classes import SCPReceiver, SCPSender +from spinnman.connections.abstract_classes import AbstractSCPConnection +from .utils import update_sdp_header_for_udp_send _TWO_SHORTS = struct.Struct("<2H") _TWO_SKIP = struct.Struct("<2x") -_REPR_TEMPLATE = "BMPConnection(cabinet={}, frame={}, boards={}, " \ - "local_host={}, local_port={}, remote_host={}, remote_port={}" -class BMPConnection(UDPConnection, SCPReceiver, SCPSender): - """ A BMP connection which supports queries to the BMP of a SpiNNaker\ - machine +class BMPConnection(UDPConnection, AbstractSCPConnection): + """ + A BMP connection which supports queries to the BMP of a SpiNNaker machine. """ __slots__ = [ - "_boards", - "_cabinet", - "_frame"] + "_boards"] def __init__(self, connection_data): """ - :param connection_data: The description of what to connect to. - :type connection_data: \ - :py:class:`spinnman.model.bmp_connection_data.BMPConnectionData` + :param BMPConnectionData connection_data: + The description of what to connect to. """ port = SCP_SCAMP_PORT if connection_data.port_num is None\ else connection_data.port_num - super(BMPConnection, self).__init__( + super().__init__( remote_host=connection_data.ip_address, remote_port=port) - self._cabinet = connection_data.cabinet - self._frame = connection_data.frame self._boards = connection_data.boards - @property - def cabinet(self): - """ The cabinet ID of the BMP - - :rtype: int - """ - return self._cabinet - - @property - def frame(self): - """ The frame ID of the BMP - - :rtype: int - """ - return self._frame - @property def boards(self): - """ The set of boards supported by the BMP + """ + The set of boards supported by the BMP. - :rtype: iterable of int + :rtype: iterable(int) """ return self._boards @property + @overrides(AbstractSCPConnection.chip_x, extend_doc=False) def chip_x(self): - """ Defined to satisfy the SCPSender - always 0 for a BMP + """ + Defined to satisfy the AbstractSCPConnection - always 0 for a BMP. """ return 0 @property + @overrides(AbstractSCPConnection.chip_y, extend_doc=False) def chip_y(self): - """ Defined to satisfy the SCPSender - always 0 for a BMP + """ + Defined to satisfy the AbstractSCPConnection - always 0 for a BMP. """ return 0 - @overrides(SCPSender.get_scp_data) + @overrides(AbstractSCPConnection.get_scp_data) def get_scp_data(self, scp_request): update_sdp_header_for_udp_send(scp_request.sdp_header, 0, 0) return _TWO_SKIP.pack() + scp_request.bytestring - @overrides(SCPReceiver.receive_scp_response) + @overrides(AbstractSCPConnection.receive_scp_response) def receive_scp_response(self, timeout=1.0): data = self.receive(timeout) result, sequence = _TWO_SHORTS.unpack_from(data, 10) return SCPResult(result), sequence, data, 2 - @overrides(SCPSender.send_scp_request) + @overrides(AbstractSCPConnection.send_scp_request) def send_scp_request(self, scp_request): self.send(self.get_scp_data(scp_request)) def __repr__(self): - return _REPR_TEMPLATE.format( - self._cabinet, self._frame, self._boards, - self.local_ip_address, self.local_port, self.remote_ip_address, - self.remote_port) + return ( + f"BMPConnection(" + f"boards={self._boards}, local_host={self.local_ip_address}, " + f"local_port={self.local_port}, " + f"remote_host={self.remote_ip_address}, " + f"remote_port={self.remote_port})") diff --git a/spinnman/connections/udp_packet_connections/boot_connection.py b/spinnman/connections/udp_packet_connections/boot_connection.py index 18bdd8ff7..6868aeb15 100644 --- a/spinnman/connections/udp_packet_connections/boot_connection.py +++ b/spinnman/connections/udp_packet_connections/boot_connection.py @@ -1,22 +1,18 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import time -from spinn_utilities.overrides import overrides -from spinnman.connections.abstract_classes import ( - SpinnakerBootSender, SpinnakerBootReceiver) from .udp_connection import UDPConnection from spinnman.messages.spinnaker_boot import SpinnakerBootMessage from spinnman.constants import UDP_BOOT_CONNECTION_DEFAULT_PORT @@ -24,55 +20,63 @@ _ANTI_FLOOD_DELAY = 0.1 -class BootConnection( - UDPConnection, SpinnakerBootSender, SpinnakerBootReceiver): - """ A connection to the SpiNNaker board that uses UDP to for booting +class BootConnection(UDPConnection): + """ + A connection to the SpiNNaker board that uses UDP to for booting. """ __slots__ = [] + _REPR_TEMPLATE = ( + "BootConnection(local_host={}, local_port={}, remote_host={}, " + "remote_port={})") - def __init__(self, local_host=None, local_port=None, remote_host=None, - remote_port=None): + def __init__(self, remote_host=None): """ - :param local_host: The local host name or IP address to bind to.\ - If not specified defaults to bind to all interfaces, unless\ - remote_host is specified, in which case binding is done to the\ - IP address that will be used to send packets. - :type local_host: str - :param local_port: The local port to bind to, between 1025 and 65535.\ - If not specified, defaults to a random unused local port - :type local_port: int - :param remote_host: The remote host name or IP address to send packets\ - to. If not specified, the socket will be available for listening\ - only, and will throw and exception if used for sending - :type remote_host: str - :param remote_port: The remote port to send packets to. If\ - remote_host is None, this is ignored. - :type remote_port: int - :raise spinnman.exceptions.SpinnmanIOException: \ + :param str remote_host: + The remote host name or IP address to send packets to. If not + specified, the socket will be available for listening only, and + will throw and exception if used for sending + :raise SpinnmanIOException: If there is an error setting up the communication channel """ + super().__init__(remote_host=remote_host, + remote_port=UDP_BOOT_CONNECTION_DEFAULT_PORT) - if remote_port is None: - remote_port = UDP_BOOT_CONNECTION_DEFAULT_PORT - - super(BootConnection, self).__init__( - local_host, local_port, remote_host, remote_port) - - @overrides(SpinnakerBootSender.send_boot_message) def send_boot_message(self, boot_message): + """ + Sends a SpiNNaker boot message using this connection. + + :param SpinnakerBootMessage boot_message: The message to be sent + :raise SpinnmanIOException: + If there is an error sending the message + """ self.send(boot_message.bytestring) # Sleep between messages to avoid flooding the machine time.sleep(_ANTI_FLOOD_DELAY) - @overrides(SpinnakerBootReceiver.receive_boot_message) def receive_boot_message(self, timeout=None): + """ + Receives a boot message from this connection. Blocks until a + message has been received, or a timeout occurs. + + :param int timeout: + The time in seconds to wait for the message to arrive; if not + specified, will wait forever, or until the connection is closed. + :return: a boot message + :rtype: SpinnakerBootMessage + :raise SpinnmanIOException: + If there is an error receiving the message + :raise SpinnmanTimeoutException: + If there is a timeout before a message is received + :raise SpinnmanInvalidPacketException: + If the received packet is not a valid SpiNNaker boot message + :raise SpinnmanInvalidParameterException: + If one of the fields of the SpiNNaker boot message is invalid + """ data = self.receive(timeout) return SpinnakerBootMessage.from_bytestring(data, 0) def __repr__(self): - return\ - "BootConnection(local_host={}, local_port={}, remote_host={},"\ - "remote_port={})".format( - self.local_ip_address, self.local_port, - self.remote_ip_address, self.remote_port) + return self._REPR_TEMPLATE.format( + self.local_ip_address, self.local_port, + self.remote_ip_address, self.remote_port) diff --git a/spinnman/connections/udp_packet_connections/eieio_connection.py b/spinnman/connections/udp_packet_connections/eieio_connection.py index c8949020a..dd3f74098 100644 --- a/spinnman/connections/udp_packet_connections/eieio_connection.py +++ b/spinnman/connections/udp_packet_connections/eieio_connection.py @@ -1,37 +1,54 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from .udp_connection import UDPConnection -from spinnman.connections.abstract_classes import ( - EIEIOReceiver, EIEIOSender, Listenable) +from spinnman.connections.abstract_classes import Listenable from spinnman.messages.eieio import ( read_eieio_command_message, read_eieio_data_message) +from spinn_utilities.overrides import overrides _ONE_SHORT = struct.Struct(". - -from .udp_connection import UDPConnection +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from contextlib import suppress from spinnman.constants import UDP_BOOT_CONNECTION_DEFAULT_PORT +from .udp_connection import UDPConnection _BOOTROM_SPINN_PORT = 54321 # Matches SPINN_PORT in spinnaker_bootROM class IPAddressesConnection(UDPConnection): - """ A connection that detects any UDP packet that is transmitted by\ - SpiNNaker boards prior to boot + """ + A connection that detects any UDP packet that is transmitted by + SpiNNaker boards prior to boot. """ __slots__ = [] def __init__(self, local_host=None, local_port=UDP_BOOT_CONNECTION_DEFAULT_PORT): - super(IPAddressesConnection, self).__init__( - local_host=local_host, local_port=local_port) - - def supports_sends_message(self, message): # @UnusedVariable - # pylint: disable=unused-argument - return False + super().__init__(local_host=local_host, local_port=local_port) def receive_ip_address(self, timeout=None): - try: + with suppress(Exception): (_, (ip_address, port)) = self.receive_with_address(timeout) if port == _BOOTROM_SPINN_PORT: return ip_address - except Exception: - pass return None def __repr__(self): diff --git a/spinnman/connections/udp_packet_connections/scamp_connection.py b/spinnman/connections/udp_packet_connections/scamp_connection.py index 4508fff7a..51728c669 100644 --- a/spinnman/connections/udp_packet_connections/scamp_connection.py +++ b/spinnman/connections/udp_packet_connections/scamp_connection.py @@ -1,33 +1,32 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinnman.constants import SCP_SCAMP_PORT from spinnman.messages.scp.enums import SCPResult -from .utils import update_sdp_header_for_udp_send from .sdp_connection import SDPConnection -from spinnman.connections.abstract_classes import SCPSender, SCPReceiver +from .utils import update_sdp_header_for_udp_send +from spinnman.connections.abstract_classes import AbstractSCPConnection +from spinn_utilities.overrides import overrides _TWO_SHORTS = struct.Struct("<2H") _TWO_SKIP = struct.Struct("<2x") -_REPR_TEMPLATE = "SCAMPConnection(chip_x={}, chip_y={}, local_host={}," \ - " local_port={}, remote_host={}, remote_port={})" -class SCAMPConnection(SDPConnection, SCPSender, SCPReceiver): - """ A UDP connection to SCAMP on the board. +class SCAMPConnection(SDPConnection, AbstractSCPConnection): + """ + A UDP connection to SCAMP on the board. """ __slots__ = [] @@ -35,38 +34,33 @@ def __init__( self, chip_x=255, chip_y=255, local_host=None, local_port=None, remote_host=None, remote_port=None): """ - - :param chip_x: \ + :param int chip_x: The x-coordinate of the chip on the board with this remote_host - :type chip_x: int - :param chip_y: \ + :param int chip_y: The y-coordinate of the chip on the board with this remote_host - :type chip_y: int - :param local_host: The optional IP address or host name of the local\ - interface to listen on - :type local_host: str - :param local_port: The optional local port to listen on - :type local_port: int - :param remote_host: The optional remote host name or IP address to\ - send messages to. If not specified, sending will not be possible\ + :param str local_host: The optional IP address or host name of the + local interface to listen on + :param int local_port: The optional local port to listen on + :param str remote_host: The optional remote host name or IP address to + send messages to. If not specified, sending will not be possible using this connection - :type remote_host: str - :param remote_port: The optional remote port number to send messages\ - to. If not specified, sending will not be possible using this\ - connection - :type remote_port: int + :param int remote_port: The optional remote port number to send + messages to. If not specified, sending will not be possible using + this connection """ # pylint: disable=too-many-arguments if remote_port is None: remote_port = SCP_SCAMP_PORT - super(SCAMPConnection, self).__init__( + super().__init__( chip_x, chip_y, local_host, local_port, remote_host, remote_port) @property + @overrides(AbstractSCPConnection.chip_x) def chip_x(self): return self._chip_x @property + @overrides(AbstractSCPConnection.chip_y) def chip_y(self): return self._chip_y @@ -74,7 +68,14 @@ def update_chip_coordinates(self, x, y): self._chip_x = x self._chip_y = y + @overrides(AbstractSCPConnection.get_scp_data, + additional_arguments=['x', 'y'], extend_defaults=True) def get_scp_data(self, scp_request, x=None, y=None): + """ + :param int x: Optional: x-coordinate of where to send to + :param int y: Optional: y-coordinate of where to send to + """ + # pylint: disable=arguments-differ if x is None: x = self._chip_x if y is None: @@ -82,6 +83,7 @@ def get_scp_data(self, scp_request, x=None, y=None): update_sdp_header_for_udp_send(scp_request.sdp_header, x, y) return _TWO_SKIP.pack() + scp_request.bytestring + @overrides(AbstractSCPConnection.receive_scp_response) def receive_scp_response(self, timeout=1.0): data = self.receive(timeout) result, sequence = _TWO_SHORTS.unpack_from(data, 10) @@ -92,6 +94,7 @@ def receive_scp_response_with_address(self, timeout=1.0): result, sequence = _TWO_SHORTS.unpack_from(data, 10) return SCPResult(result), sequence, data, 2, addr, port + @overrides(AbstractSCPConnection.send_scp_request) def send_scp_request(self, scp_request): self.send(self.get_scp_data(scp_request)) @@ -101,6 +104,8 @@ def send_scp_request_to(self, scp_request, x, y, ip_address): (str(ip_address), SCP_SCAMP_PORT)) def __repr__(self): - return _REPR_TEMPLATE.format( - self._chip_x, self._chip_y, self.local_ip_address, - self.local_port, self.remote_ip_address, self.remote_port) + return ( + f"SCAMPConnection(chip_x={self._chip_x}, chip_y={self._chip_y}, " + f"local_host={self.local_ip_address}, local_port={self.local_port}" + f", remote_host={self.remote_ip_address}, " + f"remote_port={self.remote_port})") diff --git a/spinnman/connections/udp_packet_connections/sdp_connection.py b/spinnman/connections/udp_packet_connections/sdp_connection.py index 6cec6df23..1255813f4 100644 --- a/spinnman/connections/udp_packet_connections/sdp_connection.py +++ b/spinnman/connections/udp_packet_connections/sdp_connection.py @@ -1,31 +1,31 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct +from spinn_utilities.overrides import overrides from spinnman.messages.sdp import SDPMessage, SDPFlag from .udp_connection import UDPConnection from .utils import update_sdp_header_for_udp_send -from spinnman.connections.abstract_classes import ( - SDPReceiver, SDPSender, Listenable) +from spinnman.connections.abstract_classes import Listenable _TWO_SKIP = struct.Struct("<2x") -_REPR_TEMPLATE = "SDPConnection(chip_x={}, chip_y={}, local_host={},"\ - " local_port={}, remote_host={}, remote_port={})" -class SDPConnection(UDPConnection, SDPReceiver, SDPSender, Listenable): +class SDPConnection(UDPConnection, Listenable): + """ + A connection that talks SpiNNaker Datagram Protocol. + """ __slots__ = [ "_chip_x", "_chip_y"] @@ -33,39 +33,57 @@ class SDPConnection(UDPConnection, SDPReceiver, SDPSender, Listenable): def __init__(self, chip_x=None, chip_y=None, local_host=None, local_port=None, remote_host=None, remote_port=None): """ - :param chip_x: The optional x-coordinate of the chip at the remote\ - end of the connection. If not specified, it will not be possible\ + :param int chip_x: The optional x-coordinate of the chip at the remote + end of the connection. If not specified, it will not be possible to send SDP messages that require a response with this connection. - :type chip_x: int - :param chip_y: The optional y-coordinate of the chip at the remote\ - end of the connection. If not specified, it will not be possible\ + :param int chip_y: The optional y-coordinate of the chip at the remote + end of the connection. If not specified, it will not be possible to send SDP messages that require a response with this connection. - :type chip_y: int - :param local_host: The optional IP address or host name of the local\ - interface to listen on - :type local_host: str - :param local_port: The optional local port to listen on - :type local_port: int - :param remote_host: The optional remote host name or IP address to\ - send messages to. If not specified, sending will not be possible\ + :param str local_host: The optional IP address or host name of the + local interface to listen on + :param int local_port: The optional local port to listen on + :param str remote_host: The optional remote host name or IP address to + send messages to. If not specified, sending will not be possible using this connection - :type remote_host: str - :param remote_port: The optional remote port number to send messages\ - to. If not specified, sending will not be possible using this\ - connection + :param int remote_port: The optional remote port number to send + messages to. If not specified, sending will not be possible using + this connection """ # pylint: disable=too-many-arguments - super(SDPConnection, self).__init__( - local_host, local_port, remote_host, remote_port) + super().__init__(local_host, local_port, remote_host, remote_port) self._chip_x = chip_x self._chip_y = chip_y def receive_sdp_message(self, timeout=None): + """ + Receives an SDP message from this connection. Blocks until the + message has been received, or a timeout occurs. + + :param int timeout: + The time in seconds to wait for the message to arrive; if not + specified, will wait forever, or until the connection is closed. + :return: The received SDP message + :rtype: SDPMessage + :raise SpinnmanIOException: + If there is an error receiving the message + :raise SpinnmanTimeoutException: + If there is a timeout before a message is received + :raise SpinnmanInvalidPacketException: + If the received packet is not a valid SDP message + :raise SpinnmanInvalidParameterException: + If one of the fields of the SDP message is invalid + """ data = self.receive(timeout) return SDPMessage.from_bytestring(data, 2) def send_sdp_message(self, sdp_message): + """ + Sends an SDP message down this connection. + :param SDPMessage sdp_message: The SDP message to be sent + :raise SpinnmanIOException: + If there is an error sending the message. + """ # If a reply is expected, the connection should if sdp_message.sdp_header.flags == SDPFlag.REPLY_EXPECTED: update_sdp_header_for_udp_send( @@ -74,10 +92,13 @@ def send_sdp_message(self, sdp_message): update_sdp_header_for_udp_send(sdp_message.sdp_header, 0, 0) self.send(_TWO_SKIP.pack() + sdp_message.bytestring) + @overrides(Listenable.get_receive_method) def get_receive_method(self): return self.receive_sdp_message def __repr__(self): - return _REPR_TEMPLATE.format( - self._chip_x, self._chip_y, self.local_ip_address, - self.local_port, self.remote_ip_address, self.remote_port) + return ( + f"SDPConnection(chip_x={self._chip_x}, chip_y={self._chip_y}, " + f"local_host={self.local_ip_address}, local_port={self.local_port}" + f", remote_host={self.remote_ip_address}, " + f"remote_port={self.remote_port})") diff --git a/spinnman/connections/udp_packet_connections/udp_connection.py b/spinnman/connections/udp_packet_connections/udp_connection.py index 53540a6a6..5d4e3144b 100644 --- a/spinnman/connections/udp_packet_connections/udp_connection.py +++ b/spinnman/connections/udp_packet_connections/udp_connection.py @@ -1,36 +1,51 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import logging import socket import select -from six import raise_from +from contextlib import suppress +from spinn_utilities.log import FormatAdapter from spinn_utilities.overrides import overrides -from spinnman.exceptions import SpinnmanIOException, SpinnmanTimeoutException +from spinn_utilities.ping import Ping +from spinnman.exceptions import (SpinnmanIOException, SpinnmanEOFException) from spinnman.connections.abstract_classes import Connection -from .utils import ( - bind_socket, connect_socket, get_socket, get_socket_address, ping, - resolve_host, set_receive_buffer_size) +from spinnman.utilities.socket_utils import ( + bind_socket, connect_socket, get_udp_socket, get_socket_address, + resolve_host, set_receive_buffer_size, receive_message, + receive_message_and_address, send_message, send_message_to_address) +from spinnman.connections.abstract_classes import Listenable -logger = logging.getLogger(__name__) +logger = FormatAdapter(logging.getLogger(__name__)) _RECEIVE_BUFFER_SIZE = 1048576 _PING_COUNT = 5 _REPR_TEMPLATE = "UDPConnection(local={}:{}, remote={}:{})" -class UDPConnection(Connection): +class UDPConnection(Connection, Listenable): + """ + A connection that routes messages via UDP to some remote host. + + Subclasses of this should be aware that UDP messages may be dropped, + reordered, or duplicated, and that there's no way to tell whether the + other end of the connection truly exists except by listening for + occasional messages from them. There is also an upper size limit on + messages, formally of 64kB, but usually of about 1500 bytes (the typical + maximum size of an Ethernet packet); + SDP messages have lower maximum lengths. + """ + __slots__ = [ "_can_send", "_local_ip_address", @@ -42,27 +57,24 @@ class UDPConnection(Connection): def __init__(self, local_host=None, local_port=None, remote_host=None, remote_port=None): """ - :param local_host: The local host name or IP address to bind to.\ - If not specified defaults to bind to all interfaces, unless\ - remote_host is specified, in which case binding is done to the\ + :param str local_host: The local host name or IP address to bind to. + If not specified defaults to bind to all interfaces, unless + remote_host is specified, in which case binding is done to the IP address that will be used to send packets - :type local_host: str or None - :param local_port: The local port to bind to, between 1025 and 65535.\ - If not specified, defaults to a random unused local port - :type local_port: int - :param remote_host: The remote host name or IP address to send packets\ - to. If not specified, the socket will be available for listening\ - only, and will throw and exception if used for sending - :type remote_host: str or None - :param remote_port: The remote port to send packets to. If\ - remote_host is None, this is ignored. If remote_host is specified\ - specified, this must also be specified for the connection to allow\ + :param int local_port: The local port to bind to, between 1025 and + 65535. If not specified, defaults to a random unused local port + :param str remote_host: The remote host name or IP address to send + packets to. If not specified, the socket will be available for + listening only, and will throw and exception if used for sending + :param int remote_port: The remote port to send packets to. If + remote_host is None, this is ignored. If remote_host is specified + specified, this must also be specified for the connection to allow sending - :raise spinnman.exceptions.SpinnmanIOException: \ + :raise SpinnmanIOException: If there is an error setting up the communication channel """ - self._socket = get_socket() + self._socket = get_udp_socket() set_receive_buffer_size(self._socket, _RECEIVE_BUFFER_SIZE) # Get the host and port to bind to locally @@ -83,6 +95,10 @@ def __init__(self, local_host=None, local_port=None, remote_host=None, connect_socket(self._socket, self._remote_ip_address, remote_port) self._can_send = True + # If things are closed here, it's a catastrophic problem + if self._socket._closed: + raise SpinnmanEOFException() + # Get the details of where the socket is connected self._local_ip_address, self._local_port = \ get_socket_address(self._socket) @@ -90,8 +106,28 @@ def __init__(self, local_host=None, local_port=None, remote_host=None, # Set a general timeout on the socket self._socket.settimeout(1.0) + @property + def __is_closed(self): + """ + Is the socket closed? + + .. note:: + Just because a socket is not closed doesn't mean that you're going + to be able to successfully write to it or read from it; some + failures are only detected on use. But closed sockets definitely + behave in certain ways! + + :rtype: bool + """ + # Reach into Python'#s guts + return self._socket._closed # pylint: disable=protected-access + @overrides(Connection.is_connected) def is_connected(self): + # Closed sockets are never connected! + if self.__is_closed: + return False + # If this is not a sending socket, it is not connected if not self._can_send: return False @@ -99,7 +135,7 @@ def is_connected(self): # check if machine is active and on the network for _ in range(_PING_COUNT): # Assume connected if ping works - if ping(self._remote_ip_address).returncode == 0: + if Ping.ping(self._remote_ip_address) == 0: return True # If the ping fails this number of times, the host cannot be contacted @@ -107,125 +143,127 @@ def is_connected(self): @property def local_ip_address(self): - """ The local IP address to which the connection is bound. + """ + The local IP address to which the connection is bound, + as a dotted string, e.g., `0.0.0.0`. - :return: The local IP address as a dotted string, e.g., 0.0.0.0 :rtype: str - :raise None: No known exceptions are thrown """ return self._local_ip_address @property def local_port(self): - """ The local port to which the connection is bound. + """ + The number of the local port to which the connection is bound. - :return: The local port number :rtype: int - :raise None: No known exceptions are thrown """ return self._local_port @property def remote_ip_address(self): - """ The remote IP address to which the connection is connected. + """ + The remote IP address to which the connection is connected, + or `None` if not connected remotely. - :return: The remote IP address as a dotted string, or None if not\ - connected remotely :rtype: str """ return self._remote_ip_address @property def remote_port(self): - """ The remote port to which the connection is connected. + """ + The remote port number to which the connection is connected, + or `None` if not connected remotely. - :return: The remote port, or None if not connected remotely :rtype: int """ return self._remote_port def receive(self, timeout=None): - """ Receive data from the connection + """ + Receive data from the connection. - :param timeout: The timeout in seconds, or None to wait forever - :type timeout: None or float - :return: The data received as a bytestring - :rtype: str - :raise SpinnmanTimeoutException: \ + :param float timeout: The timeout in seconds, or `None` to wait forever + :return: The data received as a byte-string + :rtype: bytes + :raise SpinnmanTimeoutException: If a timeout occurs before any data is received :raise SpinnmanIOException: If an error occurs receiving the data """ - try: - self._socket.settimeout(timeout) - return self._socket.recv(300) - except socket.timeout as e: - raise_from(SpinnmanTimeoutException("receive", timeout), e) - except Exception as e: - raise_from(SpinnmanIOException(str(e)), e) + if self.__is_closed: + raise SpinnmanEOFException() + return receive_message(self._socket, timeout, 300) def receive_with_address(self, timeout=None): - """ Receive data from the connection along with the address where the\ - data was received from + """ + Receive data from the connection along with the address where the + data was received from. - :param timeout: The timeout, or None to wait forever - :type timeout: None - :return: A tuple of the data received and a tuple of the\ + :param float timeout: The timeout, or `None` to wait forever + :return: A tuple of the data received and a tuple of the (address, port) received from - :rtype: str, (str, int) - :raise SpinnmanTimeoutException: \ + :rtype: tuple(bytes, tuple(str, int)) + :raise SpinnmanTimeoutException: If a timeout occurs before any data is received :raise SpinnmanIOException: If an error occurs receiving the data """ - try: - self._socket.settimeout(timeout) - return self._socket.recvfrom(300) - except socket.timeout as e: - raise_from(SpinnmanTimeoutException("receive", timeout), e) - except Exception as e: - raise_from(SpinnmanIOException(str(e)), e) + if self.__is_closed: + raise SpinnmanEOFException() + return receive_message_and_address(self._socket, timeout, 300) def send(self, data): - """ Send data down this connection + """ + Send data down this connection. :param data: The data to be sent - :type data: str + :type data: bytes or bytearray :raise SpinnmanIOException: If there is an error sending the data """ + if self.__is_closed: + raise SpinnmanEOFException() if not self._can_send: raise SpinnmanIOException( "Remote host and/or port not set - data cannot be sent with" " this connection") - try: - self._socket.send(data) - except Exception as e: - raise_from(SpinnmanIOException(str(e)), e) + while not send_message(self._socket, data): + if self.__is_closed: + raise SpinnmanEOFException() def send_to(self, data, address): - """ Send data down this connection + """ + Send data down this connection. - :param data: The data to be sent as a bytestring - :type data: str - :param address: A tuple of (address, port) to send the data to - :type address: (str, int) + :param data: The data to be sent as a byte-string + :type data: bytes or bytearray + :param tuple(str,int) address: + A tuple of (address, port) to send the data to :raise SpinnmanIOException: If there is an error sending the data """ - try: - self._socket.sendto(data, address) - except Exception as e: - raise_from(SpinnmanIOException(str(e)), e) + if self.__is_closed: + raise SpinnmanEOFException() + while not send_message_to_address(self._socket, data, address): + if self.__is_closed: + raise SpinnmanEOFException() @overrides(Connection.close) def close(self): - try: + if self.__is_closed: + return + with suppress(Exception): self._socket.shutdown(socket.SHUT_WR) - except Exception: - pass self._socket.close() def is_ready_to_receive(self, timeout=0): + if self.__is_closed: + return True return len(select.select([self._socket], [], [], timeout)[0]) == 1 def __repr__(self): return _REPR_TEMPLATE.format( self.local_ip_address, self.local_port, self.remote_ip_address, self.remote_port) + + @overrides(Listenable.get_receive_method) + def get_receive_method(self): + return self.receive diff --git a/spinnman/connections/udp_packet_connections/udp_listenable_connection.py b/spinnman/connections/udp_packet_connections/udp_listenable_connection.py deleted file mode 100644 index dc1a53c8d..000000000 --- a/spinnman/connections/udp_packet_connections/udp_listenable_connection.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from spinn_utilities.overrides import overrides -from spinnman.connections.abstract_classes import Listenable -from .udp_connection import UDPConnection - - -class UDPListenableConnection(UDPConnection, Listenable): - - def __init__(self, local_host=None, local_port=None, remote_host=None, - remote_port=None): - super(UDPListenableConnection, self).__init__( - local_host=local_host, local_port=local_port, - remote_host=remote_host, remote_port=remote_port) - - @overrides(Listenable.get_receive_method) - def get_receive_method(self): - return self.receive diff --git a/spinnman/connections/udp_packet_connections/utils.py b/spinnman/connections/udp_packet_connections/utils.py index a6dae325d..363396ef9 100644 --- a/spinnman/connections/udp_packet_connections/utils.py +++ b/spinnman/connections/udp_packet_connections/utils.py @@ -1,123 +1,30 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -import logging -import platform -import socket -import subprocess -from six import raise_from -from spinnman.exceptions import SpinnmanIOException - -logger = logging.getLogger(__name__) _SDP_SOURCE_PORT = 7 _SDP_SOURCE_CPU = 31 _SDP_TAG = 0xFF def update_sdp_header_for_udp_send(sdp_header, source_x, source_y): - """ Apply defaults to the SDP header for sending over UDP + """ + Apply defaults to the SDP header for sending over UDP. - :param sdp_header: The SDP header values - :type sdp_header:\ - :py:class:`spinnman.messages.sdp.sdp_header.SDPHeader` - :return: Nothing is returned + :param SDPHeader sdp_header: The SDP header values """ sdp_header.tag = _SDP_TAG sdp_header.source_port = _SDP_SOURCE_PORT sdp_header.source_cpu = _SDP_SOURCE_CPU sdp_header.source_chip_x = source_x sdp_header.source_chip_y = source_y - - -def get_socket(): - """ Wrapper round socket() system call. - """ - try: - # Create a UDP Socket - return socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - except Exception as exception: - raise_from(SpinnmanIOException( - "Error setting up socket: {}".format(exception)), exception) - - -def set_receive_buffer_size(sock, size): - """ Wrapper round setsockopt() system call. - """ - try: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, size) - except Exception: - # The OS said no, but we might still be able to work right with - # the defaults. Just warn and hope... - logger.warning("failed to configure UDP socket to have a large " - "receive buffer", exc_info=True) - - -def bind_socket(sock, host, port): - """ Wrapper round bind() system call. - """ - try: - # Bind the socket - sock.bind((str(host), int(port))) - except Exception as exception: - raise_from(SpinnmanIOException( - "Error binding socket to {}:{}: {}".format( - host, port, exception)), exception) - - -def resolve_host(host): - """ Wrapper round gethostbyname() system call. - """ - try: - return socket.gethostbyname(host) - except Exception as exception: - raise_from(SpinnmanIOException( - "Error getting IP address for {}: {}".format( - host, exception)), exception) - - -def connect_socket(sock, remote_address, remote_port): - """ Wrapper round connect() system call. - """ - try: - sock.connect((str(remote_address), int(remote_port))) - except Exception as exception: - raise_from(SpinnmanIOException( - "Error connecting to {}:{}: {}".format( - remote_address, remote_port, exception)), exception) - - -def get_socket_address(sock): - """ Wrapper round getsockname() system call. - """ - try: - addr, port = sock.getsockname() - # Ensure that a standard address is used for the INADDR_ANY - # hostname - if addr is None or addr == "": - addr = "0.0.0.0" - return addr, port - except Exception as exception: - raise_from(SpinnmanIOException("Error querying socket: {}".format( - exception)), exception) - - -def ping(address): - if platform.platform().lower().startswith("windows"): - cmd = "ping -n 1 -w 1 " + address - else: - cmd = "ping -c 1 -W 1 " + address - process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) - process.wait() - return process diff --git a/spinnman/constants.py b/spinnman/constants.py index e464f1563..1f890c144 100644 --- a/spinnman/constants.py +++ b/spinnman/constants.py @@ -1,201 +1,195 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum -# The default port of the connection +#: the amount of time to wait in seconds between powering off and powering +# on a SpiNNaker board. +POWER_CYCLE_WAIT_TIME_IN_SECONDS = 30 + +#: The default port of the connection SCP_SCAMP_PORT = 17893 -# The default port of the connection +#: The default port of the connection UDP_BOOT_CONNECTION_DEFAULT_PORT = 54321 -# The base address of the system variable structure in System ram +#: The base address of the system variable structure in System ram SYSTEM_VARIABLE_BASE_ADDRESS = 0xf5007f00 -# The base address of a routers diagnostic filter controls +#: The base address of a routers diagnostic filter controls ROUTER_REGISTER_BASE_ADDRESS = 0xe1000000 -# The base address of a routers p2p routing table +#: The base address of a routers p2p routing table ROUTER_REGISTER_P2P_ADDRESS = ROUTER_REGISTER_BASE_ADDRESS + 0x10000 -# offset for the router filter controls first register (one word each) +#: Offset for the router filter controls first register (one word each) ROUTER_FILTER_CONTROLS_OFFSET = 0x200 -# point where default filters finish and user set-able ones are available +#: Point where default filters finish and user set-able ones are available ROUTER_DEFAULT_FILTERS_MAX_POSITION = 11 -# size of a router diagnostic filter control register in bytes +#: Size of a router diagnostic filter control register in bytes ROUTER_DIAGNOSTIC_FILTER_SIZE = 4 -# number of router diagnostic filters +#: Number of router diagnostic filters NO_ROUTER_DIAGNOSTIC_FILTERS = 16 -# The size of the system variable structure in bytes +#: The size of the system variable structure in bytes SYSTEM_VARIABLE_BYTES = 256 -# The max size a UDP packet can be +#: The max size a UDP packet can be, excluding headers UDP_MESSAGE_MAX_SIZE = 256 -# the address of the start of the VCPU structure (copied from sark.h) +#: The address of the start of the VCPU structure (copied from sark.h) CPU_INFO_OFFSET = 0xe5007000 -# how many bytes the CPU info data takes up +#: How many bytes the CPU info data takes up CPU_INFO_BYTES = 128 -# the address at which user0 register starts -CPU_USER_0_START_ADDRESS = 112 - -# the address at which user1 register starts -CPU_USER_1_START_ADDRESS = 116 +#: The address at which user0 register starts +CPU_USER_START_ADDRESS = 112 -# the address at which user2 register starts -CPU_USER_2_START_ADDRESS = 120 +# The number of bytes the user start address moves each time +CPU_USER_OFFSET = 4 -# the address at which user3 register starts -CPU_USER_3_START_ADDRESS = 124 +# The largest user number +CPU_MAX_USER = 3 -# the address at which the iobuf address starts +#: The address at which the iobuf address starts CPU_IOBUF_ADDRESS_OFFSET = 88 -# max user requested tag value +#: Max user requested tag value MAX_TAG_ID = 7 -# The range of values the BMP's 12-bit ADCs can measure. +#: The range of values the BMP's 12-bit ADCs can measure. BMP_ADC_MAX = 1 << 12 -# Multiplier to convert from ADC value to volts for lines less than 2.5 V. +#: Multiplier to convert from ADC value to volts for lines less than 2.5 V. BMP_V_SCALE_2_5 = 2.5 / BMP_ADC_MAX -# Multiplier to convert from ADC value to volts for 3.3 V lines. +#: Multiplier to convert from ADC value to volts for 3.3 V lines. BMP_V_SCALE_3_3 = 3.75 / BMP_ADC_MAX -# Multiplier to convert from ADC value to volts for 12 V lines. +#: Multiplier to convert from ADC value to volts for 12 V lines. BMP_V_SCALE_12 = 15.0 / BMP_ADC_MAX -# Multiplier to convert from temperature probe values to degrees Celsius. +#: Multiplier to convert from temperature probe values to degrees Celsius. BMP_TEMP_SCALE = 1.0 / 256.0 -# Temperature value returned when a probe is not connected. +#: Temperature value returned when a probe is not connected. BMP_MISSING_TEMP = -0x8000 -# Fan speed value returned when a fan is absent. +#: Fan speed value returned when a fan is absent. BMP_MISSING_FAN = -1 -# Timeout for BMP power-on commands to reply. +#: Timeout for BMP power-on commands to reply. BMP_POWER_ON_TIMEOUT = 10.0 -# Timeout for other BMP commands to reply +#: Timeout for other BMP commands to reply BMP_TIMEOUT = 0.5 -# Time to sleep after powering on boards +#: Time to sleep after powering on boards BMP_POST_POWER_ON_SLEEP_TIME = 5.0 -# a listing of what SpiNNaker specific EIEIO commands there are. -EIEIO_COMMAND_IDS = Enum( - value="EIEIO_COMMAND_IDS", - names=[ - # Database handshake with external program - ("DATABASE_CONFIRMATION", 1), - - # Fill in buffer area with padding - ("EVENT_PADDING", 2), - - # End of all buffers, stop execution - ("EVENT_STOP", 3), - - # Stop complaining that there is SDRAM free space for buffers - ("STOP_SENDING_REQUESTS", 4), - - # Start complaining that there is SDRAM free space for buffers - ("START_SENDING_REQUESTS", 5), - - # Spinnaker requesting new buffers for spike source population - ("SPINNAKER_REQUEST_BUFFERS", 6), - - # Buffers being sent from host to SpiNNaker - ("HOST_SEND_SEQUENCED_DATA", 7), - - # Buffers available to be read from a buffered out vertex - ("SPINNAKER_REQUEST_READ_DATA", 8), - - # Host confirming data being read form SpiNNaker memory - ("HOST_DATA_READ", 9), - - # command for notifying the external devices that the simulation - # has stopped - ("STOP_PAUSE_NOTIFICATION", 10), - - # command for notifying the external devices that the simulation has - # started - ("START_RESUME_NOTIFICATION", 11), - - # Host confirming request to read data received - ("HOST_DATA_READ_ACK", 12) - - ] -) - -# the values used by the SCP IP tag time outs. These control how long to wait -# for any message request which requires a response, before raising an error. -# The value is calculated via the following formulae -# 10ms * 2^(tag_timeout_value - 1) -IPTAG_TIME_OUT_WAIT_TIMES = Enum( - value="IPTAG_TIME_OUT_WAIT_TIMES", - names=[ - ("TIMEOUT_10_ms", 1), - ("TIMEOUT_20_ms", 2), - ("TIMEOUT_40_ms", 3), - ("TIMEOUT_80_ms", 4), - ("TIMEOUT_160_ms", 5), - ("TIMEOUT_320_ms", 6), - ("TIMEOUT_640_ms", 7), - ("TIMEOUT_1280_ms", 8), - ("TIMEOUT_2560_ms", 9)] -) - -ROUTER_REGISTER_REGISTERS = Enum( - value="Registers", - names=[("LOC_MC", 0), - ("EXT_MC", 1), - ("LOC_PP", 2), - ("EXT_PP", 3), - ("LOC_NN", 4), - ("EXT_NN", 5), - ("LOC_FR", 6), - ("EXT_FR", 7), - ("DUMP_MC", 8), - ("DUMP_PP", 9), - ("DUMP_NN", 10), - ("DUMP_FR", 11), - ("USER_0", 12), - ("USER_1", 13), - ("USER_2", 14), - ("USER_3", 15)] -) -# the types of read available from SARK. These values are used to tell SARK how -# to read the data in a time efficient manner. -READ_TYPES = Enum( - value="Read_types", - names=[("BYTE", 0), - ("HALF_WORD", 1), - ("WORD", 2)] -) - -# This is a mapping between read address in the mapping between word byte -# position, the number of bytes you wish to read, and the type of time -# efficient way to read said amount of bytes via SARK + +class EIEIO_COMMAND_IDS(Enum): + """ + A listing of what SpiNNaker specific EIEIO commands there are. + """ + #: Database handshake with external program; not routed via SpiNNaker + DATABASE = 1 + #: Fill in buffer area with padding + EVENT_PADDING = 2 + #: End of all buffers, stop execution + EVENT_STOP = 3 + #: Stop complaining that there is SDRAM free space for buffers + STOP_SENDING_REQUESTS = 4 + #: Start complaining that there is SDRAM free space for buffers + START_SENDING_REQUESTS = 5 + #: Spinnaker requesting new buffers for spike source population + SPINNAKER_REQUEST_BUFFERS = 6 + #: Buffers being sent from host to SpiNNaker + HOST_SEND_SEQUENCED_DATA = 7 + #: Buffers available to be read from a buffered out vertex + SPINNAKER_REQUEST_READ_DATA = 8 + #: Host confirming data being read form SpiNNaker memory + HOST_DATA_READ = 9 + #: Command for notifying the external devices that the simulation + #: has stopped + STOP_PAUSE_NOTIFICATION = 10 + #: Command for notifying the external devices that the simulation has + #: started + START_RESUME_NOTIFICATION = 11 + #: Host confirming request to read data received + HOST_DATA_READ_ACK = 12 + + +class IPTAG_TIME_OUT_WAIT_TIMES(Enum): + """ + The values used by the SCP IP tag time outs. These control how long to wait + for any message request which requires a response, before raising an error. + + The value is calculated via the following formula: + + 10ms * 2^(`tag_timeout_value` - 1) + """ + TIMEOUT_10_ms = 1 + TIMEOUT_20_ms = 2 + TIMEOUT_40_ms = 3 + TIMEOUT_80_ms = 4 + TIMEOUT_160_ms = 5 + TIMEOUT_320_ms = 6 + TIMEOUT_640_ms = 7 + TIMEOUT_1280_ms = 8 + TIMEOUT_2560_ms = 9 + + +class ROUTER_REGISTER_REGISTERS(Enum): + """ + The indices to the router registers. + """ + LOC_MC = 0 + EXT_MC = 1 + LOC_PP = 2 + EXT_PP = 3 + LOC_NN = 4 + EXT_NN = 5 + LOC_FR = 6 + EXT_FR = 7 + DUMP_MC = 8 + DUMP_PP = 9 + DUMP_NN = 10 + DUMP_FR = 11 + USER_0 = 12 + USER_1 = 13 + USER_2 = 14 + USER_3 = 15 + + +class READ_TYPES(Enum): + """ + The types of read available from SARK. These values are used to tell + SARK how to read the data in a time efficient manner. + """ + BYTE = 0 + HALF_WORD = 1 + WORD = 2 + + +#: This is a mapping between read address in the mapping between word byte +#: position, the number of bytes you wish to read, and the type of time +#: efficient way to read said amount of bytes via SARK address_length_dtype = { (0, 0): READ_TYPES.WORD, (0, 1): READ_TYPES.BYTE, @@ -214,13 +208,13 @@ (3, 2): READ_TYPES.BYTE, (3, 3): READ_TYPES.BYTE} -# This is the default timeout when using SCP +#: This is the default timeout when using SCP SCP_TIMEOUT = 1.0 -# This is the default number of retries when using SCP +#: This is the default number of retries when using SCP N_RETRIES = 10 -# This is the number of retries during boot - this is different because -# otherwise boot takes too long (retrying on a non-booted machine will never -# work) +#: This is the number of retries during boot - this is different because +#: otherwise boot takes too long (retrying on a non-booted machine will never +#: work) BOOT_RETRIES = 3 diff --git a/spinnman/data/__init__.py b/spinnman/data/__init__.py new file mode 100644 index 000000000..efadbb270 --- /dev/null +++ b/spinnman/data/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) 2021 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .spinnman_data_view import SpiNNManDataView + + +__all__ = ["SpiNNManDataView"] diff --git a/spinnman/data/spinnman_data_view.py b/spinnman/data/spinnman_data_view.py new file mode 100644 index 000000000..5de275ed6 --- /dev/null +++ b/spinnman/data/spinnman_data_view.py @@ -0,0 +1,274 @@ +# Copyright (c) 2021 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from spinn_utilities.log import FormatAdapter +from spinn_machine.data import MachineDataView +from spinnman.utilities.appid_tracker import AppIdTracker + +logger = FormatAdapter(logging.getLogger(__name__)) +# pylint: disable=protected-access + + +class _SpiNNManDataModel(object): + """ + Singleton data model. + + This class should not be accessed directly please use the DataView and + DataWriter classes. + Accessing or editing the data held here directly is *not supported!* + + There may be other DataModel classes which sit next to this one and hold + additional data. The DataView and DataWriter classes will combine these + as needed. + + What data is held where and how can change without notice. + """ + + __singleton = None + + __slots__ = [ + # Data values cached + "_app_id", + "_app_id_tracker", + "_scamp_connection_selector", + "_transceiver", + ] + + def __new__(cls): + if cls.__singleton: + return cls.__singleton + obj = object.__new__(cls) + cls.__singleton = obj + obj._transceiver = None + obj._clear() + return obj + + def _clear(self): + """ + Clears out all data. + """ + self._hard_reset() + + def _hard_reset(self): + """ + Clears out all data that should change after a reset and graph change. + """ + self._app_id = None + self._app_id_tracker = None + self._soft_reset() + self._scamp_connection_selector = None + if self._transceiver: + try: + self._transceiver.close() + except Exception as ex: # pylint: disable=broad-except + logger.exception( + f"Error {ex} when closing the transceiver ignored") + self._transceiver = None + + def _soft_reset(self): + """ + Clears timing and other data that should changed every reset. + """ + # Holder for any later additions + + +class SpiNNManDataView(MachineDataView): + """ + Adds the extra Methods to the View for SpiNNMan level. + + See :py:class:`~spinn_utilities.data.UtilsDataView` for a more detailed + description. + + This class is designed to only be used directly within the SpiNNMan + repository as all methods are available to subclasses + """ + + __data = _SpiNNManDataModel() + __slots__ = [] + + # transceiver methods + + @classmethod + def has_transceiver(cls): + """ + Reports if a transceiver is currently set. + + :rtype: bool + """ + return cls.__data._transceiver is not None + + @classmethod + def get_transceiver(cls): + """ + The transceiver description. + + :rtype: ~spinnman.transceiver.Transceiver + :raises ~spinn_utilities.exceptions.SpiNNUtilsException: + If the transceiver is currently unavailable + """ + if cls.__data._transceiver is None: + raise cls._exception("transceiver") + return cls.__data._transceiver + + @classmethod + def read_memory(cls, x, y, base_address, length, cpu=0): + """ + Read some areas of memory (usually SDRAM) from the board. + + Syntactic sugar for `get_transceiver().read_memory()`. + + :param int x: + The x-coordinate of the chip where the memory is to be read from + :param int y: + The y-coordinate of the chip where the memory is to be read from + :param int base_address: + The address in SDRAM where the region of memory to be read starts + :param int length: The length of the data to be read in bytes + :param int cpu: + the core ID used to read the memory of; should usually be 0 when + reading from SDRAM, but may be other values when reading from DTCM. + :return: A bytearray of data read + :rtype: bytes + :raises ~spinn_utilities.exceptions.SpiNNUtilsException: + If the transceiver is currently unavailable + :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 one of `x`, `y`, `cpu`, `base_address` or `length` is invalid + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + try: + return cls.__data._transceiver.read_memory( + x, y, base_address, length, cpu) + except AttributeError as ex: + raise cls._exception("transceiver") from ex + + @classmethod + def write_memory(cls, x, y, base_address, data, n_bytes=None, offset=0, + cpu=0, is_filename=False): + """ + Write to the SDRAM on the board. + + Syntactic sugar for `get_transceiver().write_memory()`. + + :param int x: + The x-coordinate of the chip where the memory is to be written to + :param int y: + The y-coordinate of the chip where the memory is to be written to + :param int base_address: + The address in SDRAM where the region of memory is to be written + :param data: The data to write. Should be one of the following: + + * An instance of RawIOBase + * A bytearray/bytes + * A single integer - will be written in little-endian byte order + * A filename of a data file (in which case `is_filename` must be\ + set to True) + :type data: + ~io.RawIOBase or bytes or bytearray or int or str + :param int n_bytes: + The amount of data to be written in bytes. If not specified: + + * If `data` is an RawIOBase, an error is raised + * If `data` is a byte string (bytearray or bytes), the length of\ + the byte string will be used + * If `data` is an int, 4 will be used + * If `data` is a str, the length of the file will be used + :param int offset: The offset from which the valid data begins + :param int cpu: The optional CPU to write to + :param bool is_filename: True if `data` is a filename + :raises ~spinn_utilities.exceptions.SpiNNUtilsException: + If the transceiver is currently unavailable + :raise SpinnmanIOException: + * If there is an error communicating with the board + * If there is an error reading the data + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If `x, y` does not lead to a valid chip + * If a packet is received that has invalid parameters + * If `base_address` is not a positive integer + * If `data` is an RawIOBase but `n_bytes` is not specified + * If `data` is an int and `n_bytes` is more than 4 + * If `n_bytes` is less than 0 + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + if cls.__data._transceiver is None: + raise cls._exception("transceiver") + return cls.__data._transceiver.write_memory( + x, y, base_address, data, n_bytes, offset, cpu, is_filename) + + # app_id methods + + @classmethod + def get_app_id(cls): + """ + Gets the main app_id used by the transceiver. + + This method will create a new app_id if one has not yet been created. + + :rtype: int + """ + if cls.__data._app_id is None: + cls.__data._app_id = cls.get_new_id() + return cls.__data._app_id + + @classmethod + def get_new_id(cls): + """ + Gets a new id from the current `app_id_tracker` + + previously `get_transceiver().app_id_tracker().get_new_id()` + + :rtype: AppIdTracker + """ + if cls.__data._app_id_tracker is None: + cls.__data._app_id_tracker = AppIdTracker() + return cls.__data._app_id_tracker.get_new_id() + + @classmethod + def free_id(cls, app_id): + """ + Frees up an app_id. + + previously `get_transceiver().app_id_tracker().free_id(app_id)` + + :param int app_id: + """ + if cls.__data._app_id_tracker: + cls.__data._app_id_tracker.free_id(app_id) + + @classmethod + def get_scamp_connection_selector(cls): + """ + Gets the SCAMP connection selector from the transceiver. + + Syntactic sugar for `get_transceiver().scamp_connection_selector()` + + :rtype: MostDirectConnectionSelector + :raises ~spinn_utilities.exceptions.SpiNNUtilsException: + If the transceiver is currently unavailable + """ + if not cls.__data._scamp_connection_selector: + if cls.__data._transceiver is None: + raise cls._exception("transceiver") + cls.__data._scamp_connection_selector =\ + cls.__data._transceiver._scamp_connection_selector + return cls.__data._scamp_connection_selector diff --git a/spinnman/data/spinnman_data_writer.py b/spinnman/data/spinnman_data_writer.py new file mode 100644 index 000000000..2136f6247 --- /dev/null +++ b/spinnman/data/spinnman_data_writer.py @@ -0,0 +1,109 @@ +# Copyright (c) 2021 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from spinn_utilities.log import FormatAdapter +from spinn_utilities.overrides import overrides +from spinn_machine.data.machine_data_writer import MachineDataWriter +from spinnman.transceiver import Transceiver +from .spinnman_data_view import _SpiNNManDataModel, SpiNNManDataView + +logger = FormatAdapter(logging.getLogger(__name__)) +__temp_dir = None + +REPORTS_DIRNAME = "reports" +# pylint: disable=protected-access + + +class SpiNNManDataWriter(MachineDataWriter, SpiNNManDataView): + """ + See :py:class:`~spinn_utilities.data.UtilsDataWriter` + + This class is designed to only be used directly within the SpiNNMan + repository unit tests as all methods are available to subclasses + """ + __data = _SpiNNManDataModel() + __slots__ = [] + + def _spinnman_mock(self): + """ + Like :py:meth:`_mock` but does not call superclass `_mock`. + This method should only be called by `mock` via `_mock` + """ + self.__data._clear() + + @overrides(MachineDataWriter._mock) + def _mock(self): + MachineDataWriter._mock(self) + self._spinnman_mock() + + def _spinnman_setup(self): + """ + Like :py:meth:`_setup` but does not call superclass `_setup`. + + This method should only be called by `setup` (via `_setup`) + """ + self.__data._clear() + + @overrides(MachineDataWriter._setup) + def _setup(self): + MachineDataWriter._setup(self) + self._spinnman_setup() + + def _local_hard_reset(self): + """ + Puts spinnman data back into the state expected at graph changed and + `sim.reset`. + + Unlike hard_reset this method does not call super classes + + This method should only be called by hard_reset (via _hard_reset) + """ + + self.__data._hard_reset() + + @overrides(MachineDataWriter._hard_reset) + def _hard_reset(self): + MachineDataWriter._hard_reset(self) + self._local_hard_reset() + + def _local_soft_reset(self): + """ + Puts all data back into the state expected at `sim.reset` but not + graph changed. + + Unlike soft_reset this method does not call superclasses + + This method should only be called by soft_reset (via _soft_reset) + """ + self.__data._soft_reset() + + @overrides(MachineDataWriter._soft_reset) + def _soft_reset(self): + MachineDataWriter._soft_reset(self) + self._local_soft_reset() + + def set_transceiver(self, transceiver): + """ + Sets the transceiver object. + + :param Transceiver transceiver: + :raises TypeError: If the transceiver is not a Transceiver + """ + if not isinstance(transceiver, Transceiver): + raise TypeError("transceiver should be a Transceiver") + if self.__data._transceiver: + raise NotImplementedError( + "Over writing and existing transceiver not supported") + self.__data._transceiver = transceiver diff --git a/spinnman/exceptions.py b/spinnman/exceptions.py index 6c8cbf02b..6c984630e 100644 --- a/spinnman/exceptions.py +++ b/spinnman/exceptions.py @@ -1,309 +1,313 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import traceback -try: - from collections.abc import OrderedDict -except ImportError: - from collections import OrderedDict class SpinnmanException(Exception): - """ Superclass of exceptions that occur when dealing with communication\ - with SpiNNaker + """ + Superclass of exceptions that occur when dealing with communication + with SpiNNaker. """ class SpinnmanInvalidPacketException(SpinnmanException): - """ An exception that indicates that a packet was not in the expected\ - format + """ + An exception that indicates that a packet was not in the expected format. """ def __init__(self, packet_type, problem): """ - :param packet_type: The type of packet expected - :type packet_type: str - :param problem: The problem with the packet - :type problem: str + :param str packet_type: The type of packet expected + :param str problem: The problem with the packet """ - super(SpinnmanInvalidPacketException, self).__init__( - "Invalid packet of type {} received: {}".format( - packet_type, problem)) + super().__init__( + f"Invalid packet of type {packet_type} received: {problem}") self._packet_type = packet_type self._problem = problem @property def packet_type(self): - """ The packet type + """ + The packet type. + + :rtype: str """ return self._packet_type @property def problem(self): - """ The problem with the packet + """ + The problem with the packet. + + :rtype: str """ return self._problem class SpinnmanInvalidParameterException(SpinnmanException): - """ An exception that indicates that the value of one of the parameters\ - passed was invalid + """ + An exception that indicates that the value of one of the parameters + passed was invalid. """ def __init__(self, parameter, value, problem): """ - :param parameter: The name of the parameter that is invalid - :type parameter: str - :param value: The value of the parameter that is invalid - :type value: str - :param problem: The problem with the parameter - :type problem: str + :param str parameter: The name of the parameter that is invalid + :param str value: The value of the parameter that is invalid + :param str problem: The problem with the parameter """ - super(SpinnmanInvalidParameterException, self).__init__( - "Setting parameter {} to value {} is invalid: {}".format( - parameter, value, problem)) + super().__init__( + f"Setting parameter {parameter} to value {value} is invalid: " + f"{problem}") self._parameter = parameter self._value = value self._problem = problem @property def parameter(self): - """ The parameter with an invalid value + """ + The parameter with an invalid value. + + :rtype: str """ return self._parameter @property def value(self): - """ The value that is invalid + """ + The value that is invalid. """ return self._value @property def problem(self): - """ The problem with the parameter value + """ + The problem with the parameter value. + + :rtype: str """ return self._problem class SpinnmanInvalidParameterTypeException(SpinnmanException): - """ An exception that indicates that the type of one of the parameters\ - passed was invalid + """ + An exception that indicates that the type of one of the parameters + passed was invalid. """ def __init__(self, parameter, param_type, problem): """ - :param parameter: The name of the parameter that is invalid - :type parameter: str - :param param_type: The type of the parameter that is invalid - :type param_type: str - :param problem: The problem with the parameter - :type problem: str + :param str parameter: The name of the parameter that is invalid + :param str param_type: The type of the parameter that is invalid + :param str problem: The problem with the parameter """ - super(SpinnmanInvalidParameterTypeException, self).__init__( - "Parameter {} of type {} is invalid: {}".format( - parameter, param_type, problem)) + super().__init__( + f"Parameter {parameter} of type {param_type} is invalid: " + f"{problem}") self._parameter = parameter self._type = param_type self._problem = problem @property def parameter(self): - """ The parameter with an invalid value + """ + The parameter with an invalid value. + + :rtype: str """ return self._parameter @property def type(self): - """ The value that is invalid + """ + The value that is invalid. """ return self._type @property def problem(self): - """ The problem with the parameter value + """ + The problem with the parameter value. + + :rtype: str """ return self._problem class SpinnmanIOException(SpinnmanException): - """ An exception that something went wrong with the underlying IO + """ + An exception that something went wrong with the underlying IO. """ def __init__(self, problem): """ - :param problem: The problem with the IO - :type problem: str + :param str problem: The problem with the IO """ - super(SpinnmanIOException, self).__init__("IO Error: {}".format( - problem)) + super().__init__(f"IO Error: {problem}") self._problem = problem @property def problem(self): - """ The problem with IO + """ + The problem with IO. + + :rtype: str """ return self._problem +class SpinnmanEOFException(SpinnmanIOException): + """ + An exception that we're trying to do I/O on a closed socket. + That isn't going to work! + """ + + def __init__(self): + super().__init__("connection is closed") + + class SpinnmanTimeoutException(SpinnmanException): - """ An exception that indicates that a timeout occurred before an operation - could finish + """ + An exception that indicates that a timeout occurred before an operation + could finish. """ - def __init__(self, operation, timeout): + def __init__(self, operation, timeout, msg=None): """ - :param operation: The operation being performed - :type operation: str - :param timeout: The timeout value in seconds - :type timeout: int + :param str operation: The operation being performed + :param float timeout: The timeout value in seconds """ - super(SpinnmanTimeoutException, self).__init__( - "Operation {} timed out after {} seconds".format( - operation, timeout)) + if msg is None: + msg = f"Operation {operation} timed out after {timeout} seconds" + super().__init__(msg) self._operation = operation self._timeout = timeout @property def operation(self): - """ The operation that was performed + """ + The operation that was performed. + + :rtype: str """ return self._operation @property def timeout(self): - """ The timeout value in seconds + """ + The timeout value in seconds. + + :rtype: float """ return self._timeout class SpinnmanUnexpectedResponseCodeException(SpinnmanException): - """ Indicate that a response code returned from the board was unexpected\ - for the current operation + """ + Indicate that a response code returned from the board was unexpected + for the current operation. """ def __init__(self, operation, command, response): """ - :param operation: The operation being performed - :type operation: str - :param command: The command being executed - :type command: str - :param response: The response received in error - :type response: str + :param str operation: The operation being performed + :param str command: The command being executed + :param str response: The response received in error """ - super(SpinnmanUnexpectedResponseCodeException, self).__init__( - "Unexpected response {} while performing operation {} using" - " command {}".format(response, operation, command)) + super().__init__( + f"Unexpected response {response} while performing " + f"operation {operation} using command {command}") self._operation = operation self._command = command self._response = response @property def operation(self): - """ The operation being performed + """ + The operation being performed. + + :rtype: str """ return self._operation @property def command(self): - """ The command being executed + """ + The command being executed. """ return self._command @property def response(self): - """ The unexpected response """ - return self._response - + The unexpected response. -class _Group(object): - def __init__(self, trace_back): - self.trace_back = trace_back - self.chip_core = "[" - self._separator = "" - - def finalise(self): - self.chip_core += "]" - - def add_coord(self, sdp_header): - self.chip_core += "{}[{}:{}:{}]".format( - self._separator, - sdp_header.destination_chip_x, - sdp_header.destination_chip_y, - sdp_header.destination_cpu) - self._separator = "," - - @staticmethod - def group_exceptions(error_requests, exceptions, tracebacks): - """ Groups exceptions into a form usable by an exception. - - :param error_requests: the error requests - :param exceptions: the exceptions - :param tracebacks: the tracebacks - :return: a sorted exception pile - :rtype: dict(Exception,_Group) - """ - data = OrderedDict() - for error_request, exception, trace_back in zip( - error_requests, exceptions, tracebacks): - for stored_exception in data.keys(): - if isinstance(exception, type(stored_exception)): - found_exception = stored_exception - break - else: - data[exception] = _Group(trace_back) - found_exception = exception - data[found_exception].add_coord(error_request.sdp_header) - for exception in data: - data[exception].finalise() - return data.items() + :rtype: str + """ + return self._response class SpinnmanGroupedProcessException(SpinnmanException): - """ Encapsulates exceptions from processes which communicate with a\ - collection of cores/chips """ - def __init__(self, error_requests, exceptions, tracebacks): + Encapsulates exceptions from processes which communicate with a + collection of cores/chips. + """ + def __init__(self, error_requests, exceptions, tracebacks, connections): problem = "Exceptions found were:\n" - for exception, description in _Group.group_exceptions( - error_requests, exceptions, tracebacks): + for error_request, exception, trace_back, connection in zip( + error_requests, exceptions, tracebacks, connections): + sdp_header = error_request.sdp_header + phys_p = sdp_header.get_physical_cpu_id() + location = "board {} with ethernet chip {}:{} [{}:{}:{}{}]".format( + connection.remote_ip_address, connection.chip_x, + connection.chip_y, sdp_header.destination_chip_x, + sdp_header.destination_chip_y, + sdp_header.destination_cpu, phys_p) problem += \ " Received exception class: {}\n" \ " With message {}\n" \ " When sending to {}\n" \ " Stack trace: {}\n".format( exception.__class__.__name__, str(exception), - description.chip_core, - traceback.format_tb(description.trace_back)) - super(SpinnmanGroupedProcessException, self).__init__(problem) + location, + traceback.format_tb(trace_back)) + super().__init__(problem) class SpinnmanGenericProcessException(SpinnmanException): - """ Encapsulates exceptions from processes which communicate with some\ - core/chip """ - def __init__(self, exception, tb, x, y, p, tb2=None): + Encapsulates exceptions from processes which communicate with some + core/chip. + """ + def __init__(self, exception, tb, x, y, p, phys_p, tb2=None): + """ + :param Exception exception: + :param int x: + :param int y: + :param int p: + :param str phys_p: + """ # pylint: disable=too-many-arguments - super(SpinnmanGenericProcessException, self).__init__( - "\n Received exception class: {} \n" - " With message: {} \n" - " When sending to {}:{}:{}\n" - " Stack trace: {}\n".format( + super().__init__( + " Received exception class: {} \n" + " With message: {} \n" + " When sending to {}:{}:{}{}\n" + " Stack trace: {}\n".format( exception.__class__.__name__, str(exception), x, y, p, - traceback.format_tb(tb))) + phys_p, traceback.format_tb(tb))) self._stored_exception = exception if tb2 is not None: @@ -311,54 +315,88 @@ def __init__(self, exception, tb, x, y, p, tb2=None): @property def exception(self): + """ + :rtype: Exception + """ return self._stored_exception class SpinnmanUnsupportedOperationException(SpinnmanException): - """ An exception that indicates that the given operation is not supported + """ + An exception that indicates that the given operation is not supported. """ def __init__(self, operation): """ - :param operation: The operation being requested - :type operation: str + :param str operation: The operation being requested """ - super(SpinnmanUnsupportedOperationException, self).__init__( - "Operation {} is not supported".format(operation)) + super().__init__(f"Operation {operation} is not supported") self._operation = operation @property def operation(self): - """ The unsupported operation requested + """ + The unsupported operation requested. + + :rtype: str """ return self._operation class SpinnmanEIEIOPacketParsingException(SpinnmanException): - """ Unable to complete the parsing of the EIEIO packet received. - The routine used is invalid or the content of the packet is invalid + """ + Unable to complete the parsing of the EIEIO packet received. + The routine used is invalid or the content of the packet is invalid. """ def __init__(self, parsing_format, packet): - super(SpinnmanEIEIOPacketParsingException, self).__init__( - "The packet received is being parsed as an EIEIO {0:s} packet, " - "but the content of the packet is invalid".format(parsing_format)) + """ + :param str parsing_format: + :param bytes packet: + """ + super().__init__( + "The packet received is being parsed as an EIEIO " + f"{parsing_format} packet, " + "but the content of the packet is invalid") self._packet = packet @property def packet(self): + """ + :rtype: bytes + """ return self._packet class SpiNNManCoresNotInStateException(SpinnmanTimeoutException): - """ Cores failed to reach a given state within a timeout + """ + Cores failed to reach a given state within a timeout. """ def __init__(self, timeout, expected_states, failed_core_states): - msg = "waiting for cores to reach one of {}".format( - expected_states) - super(SpiNNManCoresNotInStateException, self).__init__(msg, timeout) + """ + :param float timeout: + :param set(CPUState) expected_states: + :param CPUInfos failed_core_states: + """ + n_cores = len(failed_core_states) + if n_cores > 10: + msg = (f"waiting for {n_cores} cores to reach " + f"one of {expected_states}") + else: + msg = (f"waiting for cores {failed_core_states} to reach " + f"one of {expected_states}") + super().__init__(msg, timeout, msg) self._failed_core_states = failed_core_states def failed_core_states(self): + """ + :rtype: CPUInfos + """ return self._failed_core_states + + +class SpallocException(SpinnmanException): + """ + Raised when there is a problem with the Spalloc session or job. + """ diff --git a/spinnman/extended/__init__.py b/spinnman/extended/__init__.py new file mode 100644 index 000000000..2cb63a7f3 --- /dev/null +++ b/spinnman/extended/__init__.py @@ -0,0 +1,22 @@ +# Copyright (c) 2023 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .bmp_set_led import BMPSetLed +from .de_alloc_sdram_process import DeAllocSDRAMProcess +from .read_adc import ReadADC +from .set_led import SetLED +from .write_memory_flood_process import WriteMemoryFloodProcess + +__all__ = ["BMPSetLed", "DeAllocSDRAMProcess", "ReadADC", "SetLED", + "WriteMemoryFloodProcess"] diff --git a/spinnman/messages/scp/impl/bmp_set_led.py b/spinnman/extended/bmp_set_led.py similarity index 50% rename from spinnman/messages/scp/impl/bmp_set_led.py rename to spinnman/extended/bmp_set_led.py index c52333314..c76f2d220 100644 --- a/spinnman/messages/scp/impl/bmp_set_led.py +++ b/spinnman/extended/bmp_set_led.py @@ -1,46 +1,45 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader from spinnman.messages.scp.abstract_messages import ( AbstractSCPRequest, BMPRequest) from spinnman.messages.scp.enums import SCPCommand -from .check_ok_response import CheckOKResponse +from spinnman.messages.scp.impl.check_ok_response import CheckOKResponse class BMPSetLed(BMPRequest): - """ Set the LED(s) of a board to either on, off or toggling + """ + Set the LED(s) of a board to either on, off or toggling. + + This class is currently deprecated and untested as there is no + known use except for Transceiver.set_led which is itself deprecated. """ __slots__ = [] def __init__(self, led, action, boards): """ - - :param led: Number of the LED or an iterable of LEDs to set the\ + :param led: Number of the LED or an iterable of LEDs to set the state of (0-7) - :type led: int or iterable of int - :param action: State to set the LED to, either on, off or toggle - :type action:\ - :py:class:`spinnman.messages.scp.enums.led_action.SCPLEDAction` - :param boards: Specifies the board to control the LEDs of. This may\ + :type led: int or list(int) + :param LEDAction action: + State to set the LED to, either on, off or toggle + :param boards: Specifies the board to control the LEDs of. This may also be an iterable of multiple boards (in the same frame). - :type boards: int or iterable of int - :rtype: None + :type boards: int or list(int) """ - # set up the led entry for arg1 if isinstance(led, int): leds = [led] @@ -54,13 +53,11 @@ def __init__(self, led, action, boards): arg2 = self.get_board_mask(boards) # initialise the request now - super(BMPSetLed, self).__init__( + super().__init__( boards, SCPRequestHeader(command=SCPCommand.CMD_LED), argument_1=arg1, argument_2=arg2) @overrides(AbstractSCPRequest.get_scp_response) def get_scp_response(self): - """ Get the response from the write FPGA register request - """ return CheckOKResponse("Set the LEDs of a board", "CMD_LED") diff --git a/spinnman/extended/de_alloc_sdram_process.py b/spinnman/extended/de_alloc_sdram_process.py new file mode 100644 index 000000000..916a2500d --- /dev/null +++ b/spinnman/extended/de_alloc_sdram_process.py @@ -0,0 +1,65 @@ + +# Copyright (c) 2015 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from spinnman.messages.scp.impl import SDRAMDeAlloc +from spinnman.processes.abstract_multi_connection_process import ( + AbstractMultiConnectionProcess) + + +class DeAllocSDRAMProcess(AbstractMultiConnectionProcess): + """ + .. warning:: + This class is currently deprecated and untested as there is no + known use except for Transceiver.free_sdram and free_sdram_by_app_id + which are both themselves deprecated. + """ + __slots__ = [ + "_no_blocks_freed"] + + def __init__(self, connection_selector): + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + """ + super().__init__(connection_selector) + self._no_blocks_freed = None + + def de_alloc_sdram(self, x, y, app_id, base_address=None): + """ + :param int x: + :param int y: + :param int app_id: + :param base_address: + :type base_address: int or None + """ + callback = None + # deallocate space in the SDRAM + if base_address is None: + callback = self._handle_sdram_alloc_response + self._send_request(SDRAMDeAlloc(x, y, app_id, base_address), + callback=callback) + self._finish() + self.check_for_error() + + def _handle_sdram_alloc_response(self, response): + self._no_blocks_freed = response.number_of_blocks_freed + + @property + def no_blocks_freed(self): + """ + :rtype: int + """ + return self._no_blocks_freed diff --git a/spinnman/extended/extended_transceiver.py b/spinnman/extended/extended_transceiver.py new file mode 100644 index 000000000..5ae7038f5 --- /dev/null +++ b/spinnman/extended/extended_transceiver.py @@ -0,0 +1,809 @@ +# Copyright (c) 2014 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# pylint: disable=too-many-arguments +from contextlib import contextmanager +import io +import os +import logging +from threading import Condition, RLock +import time +from spinn_utilities.log import FormatAdapter +from spinn_utilities.logger_utils import warn_once +from spinn_machine import CoreSubsets +from spinnman.constants import SYSTEM_VARIABLE_BASE_ADDRESS +from spinnman.transceiver import Transceiver +from spinnman.constants import ( + ROUTER_REGISTER_BASE_ADDRESS, ROUTER_FILTER_CONTROLS_OFFSET, + ROUTER_DIAGNOSTIC_FILTER_SIZE) +from spinnman.data import SpiNNManDataView +from spinnman.exceptions import SpinnmanException +from spinnman.extended import ( + BMPSetLed, DeAllocSDRAMProcess, ReadADC, SetLED, WriteMemoryFloodProcess) +from spinnman.model import DiagnosticFilter +from spinnman.model.enums import CPUState +from spinnman.messages.scp.enums import Signal +from spinnman.messages.scp.impl import ( + ReadMemory, ApplicationRun) +from spinnman.messages.spinnaker_boot import SystemVariableDefinition +from spinnman.connections.udp_packet_connections import ( + BMPConnection, BootConnection, SCAMPConnection) +from spinnman.processes import ( + GetHeapProcess, ReadMemoryProcess, SendSingleCommandProcess, + WriteMemoryProcess) +from spinnman.transceiver import _EXECUTABLE_ADDRESS, _ONE_BYTE, _ONE_WORD +from spinnman.utilities.utility_functions import ( + work_out_bmp_from_machine_details) + +logger = FormatAdapter(logging.getLogger(__name__)) + + +def create_transceiver_from_hostname( + hostname, version, bmp_connection_data=None, number_of_boards=None, + auto_detect_bmp=False): + """ + Create a Transceiver by creating a :py:class:`~.UDPConnection` to the + given hostname on port 17893 (the default SCAMP port), and a + :py:class:`~.BootConnection` on port 54321 (the default boot port), + optionally discovering any additional links using the UDPConnection, + and then returning the transceiver created with the conjunction of + the created UDPConnection and the discovered connections. + + :param hostname: The hostname or IP address of the board or `None` if + only the BMP connections are of interest + :type hostname: str or None + :param number_of_boards: a number of boards expected to be supported, or + ``None``, which defaults to a single board + :type number_of_boards: int or None + :param int version: the type of SpiNNaker board used within the SpiNNaker + machine being used. If a Spinn-5 board, then the version will be 5, + Spinn-3 would equal 3 and so on. + :param list(BMPConnectionData) bmp_connection_data: + the details of the BMP connections used to boot multi-board systems + :param bool auto_detect_bmp: + ``True`` if the BMP of version 4 or 5 boards should be + automatically determined from the board IP address + :param scamp_connections: + the list of connections used for SCAMP communications + :return: The created transceiver + :rtype: Transceiver + :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 a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + if hostname is not None: + logger.info("Creating transceiver for {}", hostname) + connections = list() + + # if no BMP has been supplied, but the board is a spinn4 or a spinn5 + # machine, then an assumption can be made that the BMP is at -1 on the + # final value of the IP address + if (version >= 4 and auto_detect_bmp is True and + (bmp_connection_data is None or not bmp_connection_data)): + bmp_connection_data = [ + work_out_bmp_from_machine_details(hostname, number_of_boards)] + + # handle BMP connections + if bmp_connection_data is not None: + bmp_ip_list = list() + for conn_data in bmp_connection_data: + bmp_connection = BMPConnection(conn_data) + connections.append(bmp_connection) + bmp_ip_list.append(bmp_connection.remote_ip_address) + logger.info("Transceiver using BMPs: {}", bmp_ip_list) + + connections.append(SCAMPConnection(remote_host=hostname)) + + # handle the boot connection + connections.append(BootConnection(remote_host=hostname)) + + return ExtendedTransceiver(version, connections=connections) + + +class ExtendedTransceiver(Transceiver): + """ + An encapsulation of various communications with the SpiNNaker board. + + The methods of this class are designed to be thread-safe (provided they do + not access a BMP, as access to those is never thread-safe); + thus you can make multiple calls to the same (or different) methods + from multiple threads and expect each call to work as if it had been + called sequentially, although the order of returns is not guaranteed. + + .. note:: + With multiple connections to the board, using multiple threads in this + way may result in an increase in the overall speed of operation, since + the multiple calls may be made separately over the set of given + connections. + """ + __slots__ = ["_flood_write_lock", "_nearest_neighbour_id", + "_nearest_neighbour_lock"] + + def __init__(self, version, connections=None): + """ + :param int version: The version of the board being connected to + :param list(Connection) connections: + An iterable of connections to the board. If not specified, no + communication will be possible until connections are found. + :raise SpinnmanIOException: + If there is an error communicating with the board, or if no + connections to the board can be found (if connections is ``None``) + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + super().__init__(version, connections) + + # A lock against multiple flood fill writes - needed as SCAMP cannot + # cope with this + self._flood_write_lock = Condition() + + # The nearest neighbour start ID and lock + self._nearest_neighbour_id = 1 + self._nearest_neighbour_lock = RLock() + + def send_scp_message(self, message, connection=None): + """ + Sends an SCP message, without expecting a response. + + :param message: The message to send + :type message: + spinnman.messages.scp.abstract_messages.AbstractSCPRequest + :param SCAMPConnection connection: + The connection to use (omit to pick a random one) + :raise SpinnmanTimeoutException: + If there is a timeout before a message is received + :raise SpinnmanInvalidParameterException: + If one of the fields of the received message is invalid + :raise SpinnmanInvalidPacketException: + * If the message is not a recognised packet type + * If a packet is received that is not a valid response + :raise SpinnmanUnsupportedOperationException: + If no connection can send the type of message given + :raise SpinnmanIOException: + If there is an error sending the message or receiving the response + :raise SpinnmanUnexpectedResponseCodeException: + If the response is not one of the expected codes + """ + if connection is None: + connection = self._get_random_connection(self._scamp_connections) + connection.send_scp_request(message) + + def get_connections(self): + """ + Get the currently known connections to the board, made up of those + passed in to the transceiver and those that are discovered during + calls to discover_connections. No further discovery is done here. + + :return: An iterable of connections known to the transceiver + :rtype: list(Connection) + """ + return self._all_connections + + def is_connected(self, connection=None): + """ + Determines if the board can be contacted. + + :param Connection connection: + The connection which is to be tested. If `None`, + all connections will be tested, and the board will be considered + to be connected if any one connection works. + :return: True if the board can be contacted, False otherwise + :rtype: bool + """ + if connection is not None: + return connection.is_connected() + return any(c.is_connected() for c in self._scamp_connections) + + def __set_watch_dog_on_chip(self, x, y, watch_dog): + """ + Enable, disable or set the value of the watch dog timer on a + specific chip. + + .. warning:: + This method is currently deprecated and untested as there is no + known use. Same functionality provided by ybug and bmpc. + Retained in case needed for hardware debugging. + + :param int x: chip X coordinate to write new watchdog parameter to + :param int y: chip Y coordinate to write new watchdog parameter to + :param watch_dog: + Either a boolean indicating whether to enable (True) or + disable (False) the watchdog timer, or an int value to set the + timer count to + :type watch_dog: bool or int + """ + # build what we expect it to be + warn_once(logger, "The set_watch_dog_on_chip method is deprecated " + "and untested due to no known use.") + value_to_set = watch_dog + watchdog = SystemVariableDefinition.software_watchdog_count + if isinstance(watch_dog, bool): + value_to_set = watchdog.default if watch_dog else 0 + + # build data holder + data = _ONE_BYTE.pack(value_to_set) + + # write data + address = SYSTEM_VARIABLE_BASE_ADDRESS + watchdog.offset + self.write_memory(x=x, y=y, base_address=address, data=data) + + def set_watch_dog(self, watch_dog): + """ + Enable, disable or set the value of the watch dog timer. + + .. warning:: + This method is currently deprecated and untested as there is no + known use. Same functionality provided by ybug and bmpc. + Retained in case needed for hardware debugging. + + :param watch_dog: + Either a boolean indicating whether to enable (True) or + disable (False) the watch dog timer, or an int value to set the + timer count to. + :type watch_dog: bool or int + """ + warn_once(logger, "The set_watch_dog method is deprecated and " + "untested due to no known use.") + for x, y in SpiNNManDataView.get_machine().chip_coordinates: + self.__set_watch_dog_on_chip(x, y, watch_dog) + + def get_iobuf_from_core(self, x, y, p): + """ + Get the contents of IOBUF for a given core. + + .. warning:: + This method is currently deprecated and likely to be removed. + + :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 IOBUF for + :return: An IOBUF buffer + :rtype: IOBuffer + :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 chip_and_cores contains invalid items + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + warn_once(logger, "The get_iobuf_from_core method is deprecated and " + "likely to be removed.") + core_subsets = CoreSubsets() + core_subsets.add_processor(x, y, p) + return next(self.get_iobuf(core_subsets)) + + @contextmanager + def _chip_execute_lock(self, x, y): + """ + Get a lock for executing an executable on a chip. + + .. warning:: + This method is currently deprecated and untested as there is no + known use except for execute, which is itself deprecated. + + :param int x: + :param int y: + """ + # Check if there is a lock for the given chip + with self._chip_execute_lock_condition: + chip_lock = self._chip_execute_locks[x, y] + # Acquire the lock for the chip + chip_lock.acquire() + + # Increment the lock counter (used for the flood lock) + with self._chip_execute_lock_condition: + self._n_chip_execute_locks += 1 + + try: + yield chip_lock + finally: + with self._chip_execute_lock_condition: + # Release the chip lock + chip_lock.release() + # Decrement the lock and notify + self._n_chip_execute_locks -= 1 + self._chip_execute_lock_condition.notify_all() + + def execute( + self, x, y, processors, executable, app_id, n_bytes=None, + wait=False, is_filename=False): + """ + Start an executable running on a single chip. + + .. warning:: + This method is currently deprecated and likely to be removed. + + :param int x: + The x-coordinate of the chip on which to run the executable + :param int y: + The y-coordinate of the chip on which to run the executable + :param list(int) processors: + The cores on the chip on which to run the application + :param executable: + The data that is to be executed. Should be one of the following: + + * An instance of RawIOBase + * A bytearray/bytes + * A filename of a file containing the executable (in which case + `is_filename` must be set to True) + :type executable: + ~io.RawIOBase or bytes or bytearray or str + :param int app_id: + The ID of the application with which to associate the executable + :param int n_bytes: + The size of the executable data in bytes. If not specified: + + * If executable is an RawIOBase, an error is raised + * If executable is a bytearray, the length of the bytearray will + be used + * If executable is an int, 4 will be used + * If executable is a str, the length of the file will be used + :param bool wait: + True if the binary should enter a "wait" state on loading + :param bool is_filename: True if executable is a filename + :raise SpinnmanIOException: + * If there is an error communicating with the board + * If there is an error reading the executable + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If x, y, p does not lead to a valid core + * If app_id is an invalid application ID + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + warn_once(logger, "The Transceiver's execute method is deprecated " + "likely to be removed.") + # Lock against updates + with self._chip_execute_lock(x, y): + # Write the executable + self.write_memory( + x, y, _EXECUTABLE_ADDRESS, executable, n_bytes, + is_filename=is_filename) + + # Request the start of the executable + process = SendSingleCommandProcess(self._scamp_connection_selector) + process.execute(ApplicationRun(app_id, x, y, processors, wait)) + + def execute_application(self, executable_targets, app_id): + """ + Execute a set of binaries that make up a complete application on + specified cores, wait for them to be ready and then start all of the + binaries. + + .. note:: + This will get the binaries into c_main but will not signal the + barrier. + + :param ExecutableTargets executable_targets: + The binaries to be executed and the cores to execute them on + :param int app_id: The app_id to give this application + """ + # Execute each of the binaries and get them in to a "wait" state + for binary in executable_targets.binaries: + core_subsets = executable_targets.get_cores_for_binary(binary) + self.execute_flood( + core_subsets, binary, app_id, wait=True, is_filename=True) + + # Sleep to allow cores to get going + time.sleep(0.5) + + # Check that the binaries have reached a wait state + count = self.get_core_state_count(app_id, CPUState.READY) + if count < executable_targets.total_processors: + cores_ready = self.get_cpu_infos( + executable_targets.all_core_subsets, [CPUState.READY], + include=False) + if len(cores_ready) > 0: + raise SpinnmanException( + f"Only {count} of {executable_targets.total_processors} " + "cores reached ready state: " + f"{cores_ready.get_status_string()}") + + # Send a signal telling the application to start + self.send_signal(app_id, Signal.START) + + def set_led(self, led, action, board): + """ + Set the LED state of a board in the machine. + + .. warning:: + This method is currently deprecated and untested as there is no + known use. Same functionality provided by ybug and bmpc. + Retained in case needed for hardware debugging. + + :param led: + Number of the LED or an iterable of LEDs to set the state of (0-7) + :type led: int or iterable(int) + :param LEDAction action: + State to set the LED to, either on, off or toggle + :param board: Specifies the board to control the LEDs of. This may + also be an iterable of multiple boards. The + command will actually be sent to the first board in the iterable. + :type board: int or iterable(int) + """ + warn_once(logger, "The set_led method is deprecated and " + "untested due to no known use.") + process = SendSingleCommandProcess(self._bmp_selector) + process.execute(BMPSetLed(led, action, board)) + + def read_adc_data(self, board): + """ + Read the BMP ADC data. + + .. warning:: + This method is currently deprecated and untested as there is no + known use. Same functionality provided by ybug and bmpc. + Retained in case needed for hardware debugging. + + :param int board: which board to request the ADC data from + :return: the FPGA's ADC data object + :rtype: ADCInfo + """ + warn_once(logger, "The read_adc_data method is deprecated and " + "untested due to no known use.") + process = SendSingleCommandProcess(self._bmp_selector) + response = process.execute(ReadADC(board)) + return response.adc_info # pylint: disable=no-member + + def write_neighbour_memory(self, x, y, link, base_address, data, + n_bytes=None, offset=0, cpu=0): + """ + Write to the memory of a neighbouring chip using a LINK_READ SCP + command. If sent to a BMP, this command can be used to communicate + with the FPGAs' debug registers. + + .. warning:: + This method is deprecated and untested due to no known use. + + :param int x: + The x-coordinate of the chip whose neighbour is to be written to + :param int y: + The y-coordinate of the chip whose neighbour is to be written to + :param int link: + The link index to send the request to (or if BMP, the FPGA number) + :param int base_address: + The address in SDRAM where the region of memory is to be written + :param data: The data to write. Should be one of the following: + + * An instance of RawIOBase + * A bytearray/bytes + * A single integer; will be written in little-endian byte order + :type data: + ~io.RawIOBase or bytes or bytearray or int + :param int n_bytes: + The amount of data to be written in bytes. If not specified: + + * If `data` is an RawIOBase, an error is raised + * If `data` is a bytearray, the length of the bytearray will be + used + * If `data` is an int, 4 will be used + :param int offset: + The offset where the valid data starts (if `data` is + an int then offset will be ignored and used 0) + :param int cpu: + The CPU to use, typically 0 (or if a BMP, the slot number) + :raise SpinnmanIOException: + * If there is an error communicating with the board + * If there is an error reading the data + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If `x, y` does not lead to a valid chip + * If a packet is received that has invalid parameters + * If `base_address` is not a positive integer + * If `data` is an RawIOBase but `n_bytes` is not specified + * If `data` is an int and `n_bytes` is more than 4 + * If `n_bytes` is less than 0 + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + warn_once(logger, "The write_neighbour_memory method is deprecated " + "and untested due to no known use.") + process = WriteMemoryProcess(self._scamp_connection_selector) + if isinstance(data, io.RawIOBase): + process.write_link_memory_from_reader( + x, y, cpu, link, base_address, data, n_bytes) + elif isinstance(data, int): + data_to_write = _ONE_WORD.pack(data) + process.write_link_memory_from_bytearray( + x, y, cpu, link, base_address, data_to_write, 0, 4) + else: + if n_bytes is None: + n_bytes = len(data) + process.write_link_memory_from_bytearray( + x, y, cpu, link, base_address, data, offset, n_bytes) + + def read_neighbour_memory(self, x, y, link, base_address, length, cpu=0): + """ + Read some areas of memory on a neighbouring chip using a LINK_READ + SCP command. If sent to a BMP, this command can be used to + communicate with the FPGAs' debug registers. + + .. warning:: + This method is currently deprecated and untested as there is no + known use. Same functionality provided by ybug and bmpc. + Retained in case needed for hardware debugging. + + :param int x: + The x-coordinate of the chip whose neighbour is to be read from + :param int y: + The y-coordinate of the chip whose neighbour is to be read from + :param int cpu: + The CPU to use, typically 0 (or if a BMP, the slot number) + :param int link: + The link index to send the request to (or if BMP, the FPGA number) + :param int base_address: + The address in SDRAM where the region of memory to be read starts + :param int length: The length of the data to be read in bytes + :return: An iterable of chunks of data read in order + :rtype: bytes + :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 one of `x`, `y`, `cpu`, `base_address` or `length` is invalid + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + try: + warn_once(logger, "The read_neighbour_memory method is deprecated " + "and untested due to no known use.") + process = ReadMemoryProcess(self._scamp_connection_selector) + return process.read_link_memory( + x, y, cpu, link, base_address, length) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise + + def _get_next_nearest_neighbour_id(self): + with self._nearest_neighbour_lock: + next_nearest_neighbour_id = (self._nearest_neighbour_id + 1) % 127 + self._nearest_neighbour_id = next_nearest_neighbour_id + return next_nearest_neighbour_id + + def write_memory_flood( + self, base_address, data, n_bytes=None, offset=0, + is_filename=False): + """ + Write to the SDRAM of all chips. + + :param int base_address: + The address in SDRAM where the region of memory is to be written + :param data: + The data that is to be written. Should be one of the following: + + * An instance of RawIOBase + * A byte-string + * A single integer + * A file name of a file to read (in which case `is_filename` + should be set to True) + :type data: + ~io.RawIOBase or bytes or bytearray or int or str + :param int n_bytes: + The amount of data to be written in bytes. If not specified: + + * If `data` is an RawIOBase, an error is raised + * If `data` is a bytearray or bytes, the length of the bytearray + will be used + * If `data` is an int, 4 will be used + * If `data` is a str, the size of the file will be used + :param int offset: + The offset where the valid data starts; if `data` is + an int, then the offset will be ignored and 0 is used. + :param bool is_filename: + True if `data` should be interpreted as a file name + :raise SpinnmanIOException: + * If there is an error communicating with the board + * If there is an error reading the executable + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If one of the specified chips is not valid + * If `app_id` is an invalid application ID + * If a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + process = WriteMemoryFloodProcess(self._scamp_connection_selector) + # Ensure only one flood fill occurs at any one time + with self._flood_write_lock: + # Start the flood fill + nearest_neighbour_id = self._get_next_nearest_neighbour_id() + if isinstance(data, io.RawIOBase): + process.write_memory_from_reader( + nearest_neighbour_id, base_address, data, n_bytes) + elif isinstance(data, str) and is_filename: + if n_bytes is None: + n_bytes = os.stat(data).st_size + with open(data, "rb") as reader: + process.write_memory_from_reader( + nearest_neighbour_id, base_address, reader, n_bytes) + elif isinstance(data, int): + data_to_write = _ONE_WORD.pack(data) + process.write_memory_from_bytearray( + nearest_neighbour_id, base_address, data_to_write, 0) + else: + if n_bytes is None: + n_bytes = len(data) + process.write_memory_from_bytearray( + nearest_neighbour_id, base_address, data, offset, n_bytes) + + def set_leds(self, x, y, cpu, led_states): + """ + Set LED states. + + .. warning:: + The set_leds is deprecated and untested due to no known use. + + :param int x: The x-coordinate of the chip on which to set the LEDs + :param int y: The x-coordinate of the chip on which to set the LEDs + :param int cpu: The CPU of the chip on which to set the LEDs + :param dict(int,int) led_states: + A dictionary mapping SetLED index to state with + 0 being off, 1 on and 2 inverted. + :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 a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + try: + warn_once(logger, "The set_leds is deprecated and " + "untested due to no known use.") + process = SendSingleCommandProcess(self._scamp_connection_selector) + process.execute(SetLED(x, y, cpu, led_states)) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise + + def free_sdram(self, x, y, base_address, app_id): + """ + Free allocated SDRAM. + + .. warning:: + This method is currently deprecated and likely to be removed. + + :param int x: The x-coordinate of the chip onto which to ask for memory + :param int y: The y-coordinate of the chip onto which to ask for memory + :param int base_address: The base address of the allocated memory + :param int app_id: The app ID of the allocated memory + """ + try: + warn_once(logger, "The free_sdram method is deprecated and " + "likely to be removed.") + process = DeAllocSDRAMProcess(self._scamp_connection_selector) + process.de_alloc_sdram(x, y, app_id, base_address) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise + + def free_sdram_by_app_id(self, x, y, app_id): + """ + Free all SDRAM allocated to a given app ID. + + .. warning:: + This method is currently deprecated and untested as there is no + known use. Same functionality provided by ybug and bmpc. + Retained in case needed for hardware debugging. + + :param int x: The x-coordinate of the chip onto which to ask for memory + :param int y: The y-coordinate of the chip onto which to ask for memory + :param int app_id: The app ID of the allocated memory + :return: The number of blocks freed + :rtype: int + """ + try: + warn_once(logger, "The free_sdram_by_app_id method is deprecated " + "and untested due to no known use.") + process = DeAllocSDRAMProcess(self._scamp_connection_selector) + process.de_alloc_sdram(x, y, app_id) + return process.no_blocks_freed + except Exception: + logger.info(self._where_is_xy(x, y)) + raise + + def get_router_diagnostic_filter(self, x, y, position): + """ + Gets a router diagnostic filter from a router. + + :param int x: + the X address of the router from which this filter is being + retrieved + :param int y: + the Y address of the router from which this filter is being + retrieved + :param int position: + the position in the list of filters to read the information from + :return: The diagnostic filter read + :rtype: DiagnosticFilter + :raise SpinnmanIOException: + * If there is an error communicating with the board + * If there is an error reading the data + :raise SpinnmanInvalidPacketException: + If a packet is received that is not in the valid format + :raise SpinnmanInvalidParameterException: + * If x, y does not lead to a valid chip + * If a packet is received that has invalid parameters + * If position is less than 0 or more than 15 + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange + """ + try: + memory_position = ( + ROUTER_REGISTER_BASE_ADDRESS + ROUTER_FILTER_CONTROLS_OFFSET + + position * ROUTER_DIAGNOSTIC_FILTER_SIZE) + + process = SendSingleCommandProcess( + self._scamp_connection_selector) + response = process.execute(ReadMemory(x, y, memory_position, 4)) + return DiagnosticFilter.read_from_int(_ONE_WORD.unpack_from( + response.data, response.offset)[0]) + # pylint: disable=no-member + except Exception: + logger.info(self._where_is_xy(x, y)) + raise + + @property + def number_of_boards_located(self): + """ + The number of boards currently configured. + + .. warning:: + This property is currently deprecated and likely to be removed. + + :rtype: int + """ + warn_once(logger, "The number_of_boards_located method is deprecated " + "and likely to be removed.") + if self._bmp_connection is not None: + return max(1, len(self._bmp_connection.boards)) + else: + # if no BMPs are available, then there's still at least one board + return 1 + + def get_heap(self, x, y, heap=SystemVariableDefinition.sdram_heap_address): + """ + Get the contents of the given heap on a given chip. + + :param int x: The x-coordinate of the chip + :param int y: The y-coordinate of the chip + :param SystemVariableDefinition heap: + The SystemVariableDefinition which is the heap to read + :rtype: list(HeapElement) + """ + try: + process = GetHeapProcess(self._scamp_connection_selector) + return process.get_heap((x, y), heap) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise diff --git a/spinnman/messages/scp/impl/read_adc.py b/spinnman/extended/read_adc.py similarity index 54% rename from spinnman/messages/scp/impl/read_adc.py rename to spinnman/extended/read_adc.py index 3d3714bfb..6fedf28e3 100644 --- a/spinnman/messages/scp/impl/read_adc.py +++ b/spinnman/extended/read_adc.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -23,17 +22,24 @@ class ReadADC(BMPRequest): - """ SCP Request for the data from the BMP including voltages and\ - temperature. + """ + SCP Request for the data from the BMP including voltages and + temperature. + + This class is currently deprecated and untested as there is no + known use except for Transceiver.read_adc_data which is itself + deprecated. + + .. note:: + The equivalent code in Java is *not* deprecated. """ __slots__ = [] def __init__(self, board): """ - :param board: which board to request the ADC register from - :rtype: None + :param int board: which board to request the ADC register from """ - super(ReadADC, self).__init__( + super().__init__( board, SCPRequestHeader(command=SCPCommand.CMD_BMP_INFO), argument_1=BMPInfo.ADC) @@ -44,13 +50,14 @@ def get_scp_response(self): class _SCPReadADCResponse(BMPResponse): - """ An SCP response to a request for ADC information + """ + An SCP response to a request for ADC information. """ __slots__ = [ "_adc_info"] def __init__(self): - super(_SCPReadADCResponse, self).__init__() + super().__init__() self._adc_info = None @overrides(AbstractSCPResponse.read_data_bytestring) @@ -63,6 +70,7 @@ def read_data_bytestring(self, data, offset): @property def adc_info(self): - """ The ADC information + """ + The ADC information. """ return self._adc_info diff --git a/spinnman/extended/set_led.py b/spinnman/extended/set_led.py new file mode 100644 index 000000000..220ae5695 --- /dev/null +++ b/spinnman/extended/set_led.py @@ -0,0 +1,55 @@ +# Copyright (c) 2014 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from spinn_utilities.overrides import overrides +from spinnman.messages.scp import SCPRequestHeader +from spinnman.messages.scp.abstract_messages import AbstractSCPRequest +from spinnman.messages.scp.enums import SCPCommand +from spinnman.messages.sdp import SDPFlag, SDPHeader +from spinnman.messages.scp.impl.check_ok_response import CheckOKResponse + + +class SetLED(AbstractSCPRequest): + """ + A request to change the state of an SetLED. + + This class is currently deprecated and untested as there is no + known use except for Transceiver.set_led which is itself deprecated. + """ + __slots__ = [] + + def __init__(self, x, y, cpu, led_states): + """ + :param int x: The x-coordinate of the chip, between 0 and 255 + :param int y: The y-coordinate of the chip, between 0 and 255 + :param int cpu: The CPU-number to use to set the SetLED. + :param dict(int,int) led_states: + A dictionary mapping SetLED index to state with + 0 being off, 1 on and 2 inverted. + """ + encoded_led_states = 0 + for led, state in led_states.items(): + encoded_led_states |= {0: 2, 1: 3, 2: 1}[state] << (2 * led) + + super().__init__( + SDPHeader( + flags=SDPFlag.REPLY_EXPECTED, destination_port=0, + destination_cpu=cpu, destination_chip_x=x, + destination_chip_y=y), + SCPRequestHeader(command=SCPCommand.CMD_LED), + argument_1=encoded_led_states) + + @overrides(AbstractSCPRequest.get_scp_response) + def get_scp_response(self): + return CheckOKResponse("Set SetLED", "CMD_LED") diff --git a/spinnman/processes/write_memory_flood_process.py b/spinnman/extended/write_memory_flood_process.py similarity index 63% rename from spinnman/processes/write_memory_flood_process.py rename to spinnman/extended/write_memory_flood_process.py index 6e0e2f3b7..4ef491bf7 100644 --- a/spinnman/processes/write_memory_flood_process.py +++ b/spinnman/extended/write_memory_flood_process.py @@ -1,30 +1,36 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import math from spinnman.messages.scp.impl import ( FloodFillEnd, FloodFillStart, FloodFillData) -from .abstract_multi_connection_process import AbstractMultiConnectionProcess +from spinnman.processes.abstract_multi_connection_process import ( + AbstractMultiConnectionProcess) from spinnman.constants import UDP_MESSAGE_MAX_SIZE class WriteMemoryFloodProcess(AbstractMultiConnectionProcess): - """ A process for writing memory on multiple SpiNNaker chips at once. + """ + A process for writing memory on multiple SpiNNaker chips at once. """ __slots__ = [] + def __init__(self, next_connection_selector): + AbstractMultiConnectionProcess.__init__( + self, next_connection_selector, n_channels=3, + intermediate_channel_waits=2) + def _start_flood_fill(self, n_bytes, nearest_neighbour_id): n_blocks = int(math.ceil(math.ceil(n_bytes / 4.0) / UDP_MESSAGE_MAX_SIZE)) @@ -40,6 +46,14 @@ def _end_flood_fill(self, nearest_neighbour_id): def write_memory_from_bytearray(self, nearest_neighbour_id, base_address, data, offset, n_bytes=None): + """ + :param int nearest_neighbour_id: + :param int base_address: + :param data: + :type data: bytes or bytearray + :param int offset: + :param int n_bytes: + """ # pylint: disable=too-many-arguments if n_bytes is None: n_bytes = len(data) @@ -66,6 +80,13 @@ def write_memory_from_bytearray(self, nearest_neighbour_id, base_address, def write_memory_from_reader(self, nearest_neighbour_id, base_address, reader, n_bytes): + """ + :param int nearest_neighbour_id: + :param int base_address: + :param reader: + :type reader: ~io.RawIOBase or ~io.BufferedIOBase + :param int n_bytes: + """ self._start_flood_fill(n_bytes, nearest_neighbour_id) offset = base_address diff --git a/spinnman/get_cores_in_run_state.py b/spinnman/get_cores_in_run_state.py index 5f7cb2e18..075cb166a 100644 --- a/spinnman/get_cores_in_run_state.py +++ b/spinnman/get_cores_in_run_state.py @@ -1,38 +1,46 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +This is a script used to check the state of a SpiNNaker machine. +""" + +import sys import argparse from spinnman.transceiver import create_transceiver_from_hostname -from board_test_configuration import BoardTestConfiguration from spinn_machine import CoreSubsets, CoreSubset +from spinnman.board_test_configuration import BoardTestConfiguration +from spinnman.config_setup import unittest_setup from spinnman.model.enums import CPUState - SCAMP_ID = 0 IGNORED_IDS = {SCAMP_ID, 16} # WHY 16? def get_cores_in_run_state(txrx, app_id, print_all_chips): + """ + :param Transceiver txrx: + :param int app_id: + :param bool print_all_chips: + """ count_finished = txrx.get_core_state_count(app_id, CPUState.FINISHED) count_run = txrx.get_core_state_count(app_id, CPUState.RUNNING) - print('running: {} finished: {}'.format(count_run, count_finished)) + print(f'running: {count_run} finished: {count_finished}') machine = txrx.get_machine_details() - print('machine max x: {} max y: {}'.format( - machine.max_chip_x, machine.max_chip_y)) + print(f'machine width: {machine.width} height: {machine.height}') if print_all_chips: - print('machine chips: {}'.format(list(machine.chips))) + print(f'machine chips: {list(machine.chips)}') all_cores = [] for chip in machine.chips: @@ -40,42 +48,76 @@ def get_cores_in_run_state(txrx, app_id, print_all_chips): all_cores = CoreSubsets(core_subsets=all_cores) - cores_finished = txrx.get_cores_in_state(all_cores, CPUState.FINISHED) - cores_running = txrx.get_cores_in_state(all_cores, CPUState.RUNNING) - cores_watchdog = txrx.get_cores_in_state(all_cores, CPUState.WATCHDOG) + cpu_infos = txrx.get_cpu_infos( + all_cores, + [CPUState.FINISHED, CPUState.RUNNING, CPUState.WATCHDOG], True) + cores_finished = cpu_infos.infos_for_state(CPUState.FINISHED) + cores_running = cpu_infos.infos_for_state(CPUState.RUNNING) + cores_watchdog = cpu_infos.infos_for_state(CPUState.WATCHDOG) for (x, y, p), _ in cores_running: if p not in IGNORED_IDS: - print('run core: {} {} {}'.format(x, y, p)) + print(f'run core: {x} {y} {p}') for (x, y, p), _ in cores_finished: - print('finished core: {} {} {}'.format(x, y, p)) + print(f'finished core: {x} {y} {p}') for (x, y, p), _ in cores_watchdog: - print('watchdog core: {} {} {}'.format(x, y, p)) - - -def make_transceiver(host=None): - config = BoardTestConfiguration() - config.set_up_remote_board() + print(f'watchdog core: {x} {y} {p}') + + +def _make_transceiver(host, version, bmp_names): + """ + :param host: + Host to use or `None` to use test configuration for all parameters + :type host: str or None + :param version: Board version to use (`None` defaults to 5 unless host is + 192.168.240.253 (spin 3) + :type version: int or None + :param bmp: bmp connection or `None` to auto detect (if applicable) + :type bmp: str or None + :rtype: Transceiver + """ if host is None: + config = BoardTestConfiguration() + config.set_up_remote_board() host = config.remotehost - - print("talking to SpiNNaker system at {}".format(host)) + version = config.board_version + bmp_names = config.bmp_names + auto_detect_bmp = config.auto_detect_bmp + else: + if version is None: + if host == "192.168.240.253": + version = 3 + else: + version = 5 + auto_detect_bmp = False + + print(f"talking to SpiNNaker system at {host}") return create_transceiver_from_hostname( - host, config.board_version, - ignore_cores=CoreSubsets(), - ignore_chips=CoreSubsets(core_subsets=[]), - bmp_connection_data=config.bmp_names, - auto_detect_bmp=config.auto_detect_bmp) + host, version, + bmp_connection_data=bmp_names, + auto_detect_bmp=auto_detect_bmp) -def main(): +def main(args): + """ + Runs the script. + """ + unittest_setup() ap = argparse.ArgumentParser( description="Check the state of a SpiNNaker machine.") ap.add_argument( "-a", "--appid", help="the application ID to check", type=int, default=17) + ap.add_argument( + "-v", "--version", help="the version of your boards", type=int, + default=None) + ap.add_argument( + "-b", "--bmp_names", + help="the hostname or IP address of the BMP of the SpiNNaker machine " + "to inspects", + type=str, default=None) ap.add_argument( "-n", "--noprintchips", action="store_true", default=False, help=("don't print all the chips out; avoids a great deal of " @@ -83,12 +125,12 @@ def main(): ap.add_argument( "host", default=None, nargs='?', help="the hostname or IP address of the SpiNNaker machine to inspect") - args = ap.parse_args() + args = ap.parse_args(args) # These ought to be parsed from command line arguments app_id = args.appid print_chips = not args.noprintchips - transceiver = make_transceiver(args.host) + transceiver = _make_transceiver(args.host, args.version, args.bmp_names) try: get_cores_in_run_state(transceiver, app_id, print_chips) finally: @@ -96,4 +138,4 @@ def main(): if __name__ == "__main__": # pragma: no cover - main() + main(sys.argv[1:]) diff --git a/spinnman/messages/__init__.py b/spinnman/messages/__init__.py index d358f58a8..05bfa339d 100644 --- a/spinnman/messages/__init__.py +++ b/spinnman/messages/__init__.py @@ -1,14 +1,13 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/spinnman/messages/eieio/__init__.py b/spinnman/messages/eieio/__init__.py index 1f8e76a65..90f1c3623 100644 --- a/spinnman/messages/eieio/__init__.py +++ b/spinnman/messages/eieio/__init__.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .eieio_message import AbstractEIEIOMessage from .eieio_prefix import EIEIOPrefix diff --git a/spinnman/messages/eieio/command_messages/__init__.py b/spinnman/messages/eieio/command_messages/__init__.py index c7bb4f826..bc0aaa51a 100644 --- a/spinnman/messages/eieio/command_messages/__init__.py +++ b/spinnman/messages/eieio/command_messages/__init__.py @@ -1,25 +1,25 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -from .database_confirmation import DatabaseConfirmation from .eieio_command_header import EIEIOCommandHeader from .eieio_command_message import EIEIOCommandMessage from .event_stop_request import EventStopRequest from .host_data_read import HostDataRead from .host_data_read_ack import HostDataReadAck from .host_send_sequenced_data import HostSendSequencedData +from .notification_protocol_db_location import ( + NotificationProtocolDatabaseLocation) from .notification_protocol_pause_stop import NotificationProtocolPauseStop from .notification_protocol_start_resume import NotificationProtocolStartResume from .padding_request import PaddingRequest @@ -28,8 +28,9 @@ from .start_requests import StartRequests from .stop_requests import StopRequests -__all__ = ["DatabaseConfirmation", "EIEIOCommandHeader", "EIEIOCommandMessage", +__all__ = ["EIEIOCommandHeader", "EIEIOCommandMessage", "EventStopRequest", "HostDataRead", "HostSendSequencedData", + "NotificationProtocolDatabaseLocation", "NotificationProtocolPauseStop", "NotificationProtocolStartResume", "PaddingRequest", "SpinnakerRequestBuffers", "HostDataReadAck", "SpinnakerRequestReadData", "StartRequests", "StopRequests"] diff --git a/spinnman/messages/eieio/command_messages/database_confirmation.py b/spinnman/messages/eieio/command_messages/database_confirmation.py deleted file mode 100644 index 5413432ed..000000000 --- a/spinnman/messages/eieio/command_messages/database_confirmation.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from .eieio_command_message import EIEIOCommandMessage -from .eieio_command_header import EIEIOCommandHeader -from spinnman.constants import EIEIO_COMMAND_IDS - - -class DatabaseConfirmation(EIEIOCommandMessage): - """ Packet which contains the path to the database created by the\ - toolchain which is to be used by any software which interfaces with\ - SpiNNaker. - """ - __slots__ = [ - "_database_path"] - - def __init__(self, database_path=None): - super(DatabaseConfirmation, self).__init__(EIEIOCommandHeader( - EIEIO_COMMAND_IDS.DATABASE_CONFIRMATION)) - self._database_path = None - if database_path is not None: - self._database_path = database_path.encode() - - @property - def database_path(self): - if self._database_path is not None: - return self._database_path.decode() - else: - return None - - @property - def bytestring(self): - data = super(DatabaseConfirmation, self).bytestring - if self._database_path is not None: - data += self._database_path - return data - - @staticmethod - def from_bytestring(command_header, data, offset): - database_path = None - if len(data) - offset > 0: - database_path = data[offset:] - return DatabaseConfirmation(database_path) diff --git a/spinnman/messages/eieio/command_messages/eieio_command_header.py b/spinnman/messages/eieio/command_messages/eieio_command_header.py index 4935d2155..b426c8c49 100644 --- a/spinnman/messages/eieio/command_messages/eieio_command_header.py +++ b/spinnman/messages/eieio/command_messages/eieio_command_header.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from enum import Enum @@ -21,7 +20,8 @@ class EIEIOCommandHeader(object): - """ EIEIO header for command packets + """ + EIEIO header for command packets. """ __slots__ = [ "_command"] @@ -41,18 +41,17 @@ def command(self): @staticmethod def from_bytestring(data, offset): - """ Read an EIEIO command header from a bytestring + """ + Read an EIEIO command header from a byte-string. - :param data: The bytestring to read the data from - :type data: str or bytes - :param offset: The offset where the valid data starts - :type offset: int + :param data: The byte-string to read the data from + :type data: bytes or bytearray + :param int offset: The offset where the valid data starts :return: an EIEIO command header - :rtype:\ - :py:class:`EIEIOCommandHeader` - :raise spinnman.exceptions.SpinnmanIOException: \ + :rtype: EIEIOCommandHeader + :raise SpinnmanIOException: If there is an error reading from the reader - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + :raise SpinnmanInvalidParameterException: If there is an error setting any of the values """ command_header = _ONE_SHORT.unpack_from(data, offset)[0] @@ -62,7 +61,8 @@ def from_bytestring(data, offset): @property def bytestring(self): - """ Get a bytestring of the header + """ + The byte-string of the header. :rtype: bytes """ diff --git a/spinnman/messages/eieio/command_messages/eieio_command_message.py b/spinnman/messages/eieio/command_messages/eieio_command_message.py index 0cf046636..a45370686 100644 --- a/spinnman/messages/eieio/command_messages/eieio_command_message.py +++ b/spinnman/messages/eieio/command_messages/eieio_command_message.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from spinn_utilities.overrides import overrides from spinnman.messages.eieio import AbstractEIEIOMessage class EIEIOCommandMessage(AbstractEIEIOMessage): - """ An EIEIO command message + """ + An EIEIO command message. """ __slots__ = [ "_data", @@ -26,13 +26,10 @@ class EIEIOCommandMessage(AbstractEIEIOMessage): def __init__(self, eieio_command_header, data=None, offset=0): """ - :param eieio_command_header: The header of the message - :type eieio_command_header:\ - :py:class:`spinnman.messages.eieio.command_messages.EIEIOCommandHeader` - :param data: Optional incoming data - :type data: str - :param offset: Offset into the data where valid data begins - :type offset: int + :param EIEIOCommandHeader eieio_command_header: + The header of the message + :param bytes data: Optional incoming data + :param int offset: Offset into the data where valid data begins """ # The header self._eieio_command_header = eieio_command_header @@ -42,7 +39,11 @@ def __init__(self, eieio_command_header, data=None, offset=0): self._offset = offset @property + @overrides(AbstractEIEIOMessage.eieio_header) def eieio_header(self): + """ + :rtype: EIEIOCommandHeader + """ return self._eieio_command_header @property @@ -58,6 +59,7 @@ def from_bytestring(command_header, data, offset): return EIEIOCommandMessage(command_header, data, offset) @property + @overrides(AbstractEIEIOMessage.bytestring) def bytestring(self): return self._eieio_command_header.bytestring @@ -66,7 +68,7 @@ def get_min_packet_length(): return 2 def __str__(self): - return "EIEIOCommandMessage:{}".format(self._eieio_command_header) + return f"EIEIOCommandMessage:{self._eieio_command_header}" def __repr__(self): return self.__str__() diff --git a/spinnman/messages/eieio/command_messages/event_stop_request.py b/spinnman/messages/eieio/command_messages/event_stop_request.py index f332356fe..886c0e7c4 100644 --- a/spinnman/messages/eieio/command_messages/event_stop_request.py +++ b/spinnman/messages/eieio/command_messages/event_stop_request.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinnman.constants import EIEIO_COMMAND_IDS from .eieio_command_message import EIEIOCommandMessage @@ -19,11 +18,11 @@ class EventStopRequest(EIEIOCommandMessage): - """ Packet used for the buffering input technique which causes the parser\ - of the input packet to terminate its execution + """ + Packet used for the buffering input technique which causes the parser + of the input packet to terminate its execution. """ __slots__ = [] def __init__(self): - super(EventStopRequest, self).__init__(EIEIOCommandHeader( - EIEIO_COMMAND_IDS.EVENT_STOP)) + super().__init__(EIEIOCommandHeader(EIEIO_COMMAND_IDS.EVENT_STOP)) diff --git a/spinnman/messages/eieio/command_messages/host_data_read.py b/spinnman/messages/eieio/command_messages/host_data_read.py index f45633733..a794e5e52 100644 --- a/spinnman/messages/eieio/command_messages/host_data_read.py +++ b/spinnman/messages/eieio/command_messages/host_data_read.py @@ -1,20 +1,18 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct -from past.builtins import xrange from spinnman.exceptions import ( SpinnmanInvalidPacketException, SpinnmanInvalidParameterTypeException) from .eieio_command_message import EIEIOCommandMessage @@ -26,10 +24,11 @@ class HostDataRead(EIEIOCommandMessage): - """ Packet sent by the host computer to the SpiNNaker system in the\ - context of the buffering output technique to signal that the host has\ - completed reading data from the output buffer, and that such space can\ - be considered free to use again + """ + Packet sent by the host computer to the SpiNNaker system in the + context of the buffering output technique to signal that the host has + completed reading data from the output buffer, and that such space can + be considered free to use again. """ __slots__ = [ "_acks", @@ -53,12 +52,10 @@ def __init__( raise SpinnmanInvalidPacketException( "SpinnakerRequestReadData", "The format for a SpinnakerRequestReadData packet is " - "invalid: {0:d} request(s), {1:d} space(s) read " - "defined, {2:d} region(s) defined, {3:d} channel(s) " - "defined".format( - n_requests, len(space_read), len(region_id), len(channel))) - super(HostDataRead, self).__init__(EIEIOCommandHeader( - EIEIO_COMMAND_IDS.HOST_DATA_READ)) + f"invalid: {n_requests} request(s), {len(space_read)} " + f"space(s) read defined, {len(region_id)} region(s) defined, " + f"{len(channel)} channel(s) defined") + super().__init__(EIEIOCommandHeader(EIEIO_COMMAND_IDS.HOST_DATA_READ)) self._header = _HostDataReadHeader(n_requests, sequence_no) self._acks = _HostDataReadAck(channel, region_id, space_read) @@ -93,7 +90,7 @@ def from_bytestring(command_header, data, offset): # @UnusedVariable region_id = list() space_read = list() - for _ in xrange(n_requests): + for _ in range(n_requests): channel_ack, region_id_ack, space_read_ack = \ _PATTERN_xxBBI.unpack_from(data, offset) channel.append(channel_ack) @@ -105,18 +102,19 @@ def from_bytestring(command_header, data, offset): # @UnusedVariable @property def bytestring(self): - byte_string = super(HostDataRead, self).bytestring + byte_string = super().bytestring n_requests = self.n_requests byte_string += _PATTERN_BB.pack(n_requests, self.sequence_no) - for i in xrange(n_requests): + for i in range(n_requests): byte_string += _PATTERN_xxBBI.pack( self.channel(i), self.region_id(i), self.space_read(i)) return byte_string class _HostDataReadHeader(object): - """ The HostDataRead contains itself on header with the number of requests\ - and a sequence number + """ + The HostDataRead contains itself on header with the number of requests + and a sequence number. """ __slots__ = [ "_n_requests", @@ -136,7 +134,8 @@ def n_requests(self): class _HostDataReadAck(object): - """ Contains a set of acks which refer to each of the channels read + """ + Contains a set of ACKs which refer to each of the channels read. """ __slots__ = [ "_channel", diff --git a/spinnman/messages/eieio/command_messages/host_data_read_ack.py b/spinnman/messages/eieio/command_messages/host_data_read_ack.py index fea1a1edb..ffad69c48 100644 --- a/spinnman/messages/eieio/command_messages/host_data_read_ack.py +++ b/spinnman/messages/eieio/command_messages/host_data_read_ack.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from .eieio_command_message import EIEIOCommandMessage @@ -22,16 +21,17 @@ class HostDataReadAck(EIEIOCommandMessage): - """ Packet sent by the host computer to the SpiNNaker system in the\ - context of the buffering output technique to signal that the host has\ - received a request to read data + """ + Packet sent by the host computer to the SpiNNaker system in the + context of the buffering output technique to signal that the host has + received a request to read data. """ __slots__ = [ "_sequence_no"] def __init__(self, sequence_no): - super(HostDataReadAck, self).__init__(EIEIOCommandHeader( - EIEIO_COMMAND_IDS.HOST_DATA_READ_ACK)) + super().__init__( + EIEIOCommandHeader(EIEIO_COMMAND_IDS.HOST_DATA_READ_ACK)) self._sequence_no = sequence_no @property @@ -46,6 +46,6 @@ def from_bytestring(command_header, data, offset): # @UnusedVariable @property def bytestring(self): - byte_string = super(HostDataReadAck, self).bytestring + byte_string = super().bytestring byte_string += _PATTERN_B.pack(self.sequence_no) return byte_string diff --git a/spinnman/messages/eieio/command_messages/host_send_sequenced_data.py b/spinnman/messages/eieio/command_messages/host_send_sequenced_data.py index e6d5793e9..542c94b60 100644 --- a/spinnman/messages/eieio/command_messages/host_send_sequenced_data.py +++ b/spinnman/messages/eieio/command_messages/host_send_sequenced_data.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from .eieio_command_message import EIEIOCommandMessage @@ -23,9 +22,10 @@ class HostSendSequencedData(EIEIOCommandMessage): - """ Packet sent from the host to the SpiNNaker system in the context of\ - buffering input mechanism to identify packet which needs to be stored\ - in memory for future use + """ + Packet sent from the host to the SpiNNaker system in the context of + buffering input mechanism to identify packet which needs to be stored + in memory for future use. """ __slots__ = [ "_eieio_data_message", @@ -33,7 +33,7 @@ class HostSendSequencedData(EIEIOCommandMessage): "_sequence_no"] def __init__(self, region_id, sequence_no, eieio_data_message): - super(HostSendSequencedData, self).__init__(EIEIOCommandHeader( + super().__init__(EIEIOCommandHeader( EIEIO_COMMAND_IDS.HOST_SEND_SEQUENCED_DATA)) self._region_id = region_id self._sequence_no = sequence_no @@ -64,6 +64,6 @@ def from_bytestring(command_header, data, offset): # @UnusedVariable @property def bytestring(self): - return (super(HostSendSequencedData, self).bytestring + + return (super().bytestring + _PATTERN_BB.pack(self._region_id, self._sequence_no) + self._eieio_data_message.bytestring) diff --git a/spinnman/messages/eieio/command_messages/notification_protocol_db_location.py b/spinnman/messages/eieio/command_messages/notification_protocol_db_location.py new file mode 100644 index 000000000..5dcafc945 --- /dev/null +++ b/spinnman/messages/eieio/command_messages/notification_protocol_db_location.py @@ -0,0 +1,62 @@ +# Copyright (c) 2015 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .eieio_command_message import EIEIOCommandMessage +from .eieio_command_header import EIEIOCommandHeader +from spinnman.constants import EIEIO_COMMAND_IDS + + +class NotificationProtocolDatabaseLocation(EIEIOCommandMessage): + """ + Packet which contains the path to the database created by the + toolchain which is to be used by any software which interfaces with + SpiNNaker. Also the acknowledgement of that message. + + This message is not sent to SpiNNaker boards but rather to an auxiliary + tool (e.g., data visualiser). + """ + __slots__ = [ + "_database_path"] + + def __init__(self, database_path=None): + """ + :param str database_path: + The location of the database. If ``None``, this is an + acknowledgement, stating that the database has now been read. + """ + super().__init__(EIEIOCommandHeader(EIEIO_COMMAND_IDS.DATABASE)) + self._database_path = None + if database_path is not None: + self._database_path = database_path.encode() + + @property + def database_path(self): + if self._database_path is not None: + return self._database_path.decode() + else: + return None + + @property + def bytestring(self): + data = super().bytestring + if self._database_path is not None: + data += self._database_path + return data + + @staticmethod + def from_bytestring(command_header, data, offset): + database_path = None + if len(data) - offset > 0: + database_path = data[offset:] + return NotificationProtocolDatabaseLocation(database_path) diff --git a/spinnman/messages/eieio/command_messages/notification_protocol_pause_stop.py b/spinnman/messages/eieio/command_messages/notification_protocol_pause_stop.py index cab341887..7226dc8fd 100644 --- a/spinnman/messages/eieio/command_messages/notification_protocol_pause_stop.py +++ b/spinnman/messages/eieio/command_messages/notification_protocol_pause_stop.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2016 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .eieio_command_message import EIEIOCommandMessage from .eieio_command_header import EIEIOCommandHeader @@ -19,19 +18,18 @@ class NotificationProtocolPauseStop(EIEIOCommandMessage): - """ Packet which indicates that the toolchain has paused or stopped + """ + Packet which indicates that the toolchain has paused or stopped. + + This message is not sent to SpiNNaker boards but rather to an auxiliary + tool (e.g., data visualiser). """ __slots__ = [] def __init__(self): - super(NotificationProtocolPauseStop, self).__init__(EIEIOCommandHeader( + super().__init__(EIEIOCommandHeader( EIEIO_COMMAND_IDS.STOP_PAUSE_NOTIFICATION)) - @property - def bytestring(self): - data = super(NotificationProtocolPauseStop, self).bytestring - return data - @staticmethod def from_bytestring(command_header, data, offset): return NotificationProtocolPauseStop() diff --git a/spinnman/messages/eieio/command_messages/notification_protocol_start_resume.py b/spinnman/messages/eieio/command_messages/notification_protocol_start_resume.py index 43c360ff6..d8e5e0556 100644 --- a/spinnman/messages/eieio/command_messages/notification_protocol_start_resume.py +++ b/spinnman/messages/eieio/command_messages/notification_protocol_start_resume.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2016 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .eieio_command_message import EIEIOCommandMessage from .eieio_command_header import EIEIOCommandHeader @@ -19,14 +18,17 @@ class NotificationProtocolStartResume(EIEIOCommandMessage): - """ Packet which indicates that the toolchain has started or resumed + """ + Packet which indicates that the toolchain has started or resumed. + + This message is not sent to SpiNNaker boards but rather to an auxiliary + tool (e.g., data visualiser). """ __slots__ = [] def __init__(self): - super(NotificationProtocolStartResume, self).__init__( - EIEIOCommandHeader( - EIEIO_COMMAND_IDS.START_RESUME_NOTIFICATION)) + super().__init__(EIEIOCommandHeader( + EIEIO_COMMAND_IDS.START_RESUME_NOTIFICATION)) @staticmethod def from_bytestring(command_header, data, offset): diff --git a/spinnman/messages/eieio/command_messages/padding_request.py b/spinnman/messages/eieio/command_messages/padding_request.py index c036160f3..9487e209a 100644 --- a/spinnman/messages/eieio/command_messages/padding_request.py +++ b/spinnman/messages/eieio/command_messages/padding_request.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinnman.constants import EIEIO_COMMAND_IDS from .eieio_command_message import EIEIOCommandMessage @@ -19,13 +18,13 @@ class PaddingRequest(EIEIOCommandMessage): - """ Packet used to pad space in the buffering area, if needed + """ + Packet used to pad space in the buffering area, if needed. """ __slots__ = [] def __init__(self): - super(PaddingRequest, self).__init__(EIEIOCommandHeader( - EIEIO_COMMAND_IDS.EVENT_PADDING)) + super().__init__(EIEIOCommandHeader(EIEIO_COMMAND_IDS.EVENT_PADDING)) @staticmethod def get_min_packet_length(): diff --git a/spinnman/messages/eieio/command_messages/spinnaker_request_buffers.py b/spinnman/messages/eieio/command_messages/spinnaker_request_buffers.py index 3adfb132f..b3e1e3990 100644 --- a/spinnman/messages/eieio/command_messages/spinnaker_request_buffers.py +++ b/spinnman/messages/eieio/command_messages/spinnaker_request_buffers.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from .eieio_command_message import EIEIOCommandMessage @@ -22,9 +21,10 @@ class SpinnakerRequestBuffers(EIEIOCommandMessage): - """ Message used in the context of the buffering input mechanism which is\ - sent by the SpiNNaker system to the host computer to ask for more data\ - to inject during the simulation + """ + Message used in the context of the buffering input mechanism which is + sent by the SpiNNaker system to the host computer to ask for more data + to inject during the simulation. """ __slots__ = [ "_region_id", @@ -34,7 +34,7 @@ class SpinnakerRequestBuffers(EIEIOCommandMessage): def __init__(self, x, y, p, region_id, sequence_no, space_available): # pylint: disable=too-many-arguments - super(SpinnakerRequestBuffers, self).__init__(EIEIOCommandHeader( + super().__init__(EIEIOCommandHeader( EIEIO_COMMAND_IDS.SPINNAKER_REQUEST_BUFFERS)) self._x = x self._y = y @@ -81,7 +81,6 @@ def from_bytestring(command_header, data, offset): # @UnusedVariable @property def bytestring(self): - return (super(SpinnakerRequestBuffers, self).bytestring + - _PATTERN_BBBxBBI.pack( - self._y, self._x, self._p << 3, self._region_id, - self._sequence_no, self._space_available)) + return (super().bytestring + _PATTERN_BBBxBBI.pack( + self._y, self._x, self._p << 3, self._region_id, + self._sequence_no, self._space_available)) diff --git a/spinnman/messages/eieio/command_messages/spinnaker_request_read_data.py b/spinnman/messages/eieio/command_messages/spinnaker_request_read_data.py index 0f27c6889..190045e8a 100644 --- a/spinnman/messages/eieio/command_messages/spinnaker_request_read_data.py +++ b/spinnman/messages/eieio/command_messages/spinnaker_request_read_data.py @@ -1,20 +1,18 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct -from past.builtins import xrange from spinnman.exceptions import ( SpinnmanInvalidPacketException, SpinnmanInvalidParameterTypeException) from .eieio_command_message import EIEIOCommandMessage @@ -29,9 +27,10 @@ class SpinnakerRequestReadData(EIEIOCommandMessage): - """ Message used in the context of the buffering output mechanism which is\ - sent from the SpiNNaker system to the host computer to signal that\ - some data is available to be read + """ + Message used in the context of the buffering output mechanism which is + sent from the SpiNNaker system to the host computer to signal that + some data is available to be read. """ __slots__ = [ "_header", @@ -65,7 +64,7 @@ def __init__(self, x, y, p, region_id, sequence_no, n_requests, n_requests, len(start_address), len(space_to_be_read), len(region_id), len(channel))) - super(SpinnakerRequestReadData, self).__init__(EIEIOCommandHeader( + super().__init__(EIEIOCommandHeader( EIEIO_COMMAND_IDS.SPINNAKER_REQUEST_READ_DATA)) self._header = _SpinnakerRequestReadDataHeader( x, y, p, n_requests, sequence_no) @@ -120,7 +119,7 @@ def from_bytestring(command_header, data, offset): start_address = list() space_to_be_read = list() - for i in xrange(n_requests): + for i in range(n_requests): if i == 0: request_channel, request_region_id, request_start_address, \ request_space_to_be_read = _PATTERN_BBII.unpack_from( @@ -141,12 +140,12 @@ def from_bytestring(command_header, data, offset): @property def bytestring(self): - byte_string = super(SpinnakerRequestReadData, self).bytestring + byte_string = super().bytestring byte_string += _PATTERN_BB.pack(self.x, self.y) n_requests = self.n_requests processor_and_request = (self.p << 3) | n_requests - for i in xrange(n_requests): + for i in range(n_requests): if i == 0: byte_string += _PATTERN_BBBBII.pack( processor_and_request, self.sequence_no, @@ -160,8 +159,9 @@ def bytestring(self): class _SpinnakerRequestReadDataHeader(object): - """ Contains the position of the core in the machine (x, y, p), the number\ - of requests and a sequence number + """ + Contains the position of the core in the machine (x, y, p), the number + of requests and a sequence number. """ __slots__ = [ "_n_requests", @@ -198,7 +198,8 @@ def n_requests(self): class _SpinnakerRequestReadDataRequest(object): - """ Contains a set of requests which refer to the channels used + """ + Contains a set of requests which refer to the channels used. """ __slots__ = [ "_channel", diff --git a/spinnman/messages/eieio/command_messages/start_requests.py b/spinnman/messages/eieio/command_messages/start_requests.py index b214d4870..14fc490ec 100644 --- a/spinnman/messages/eieio/command_messages/start_requests.py +++ b/spinnman/messages/eieio/command_messages/start_requests.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .eieio_command_message import EIEIOCommandMessage from .eieio_command_header import EIEIOCommandHeader @@ -19,12 +18,13 @@ class StartRequests(EIEIOCommandMessage): - """ Packet used in the context of buffering input for the host computer to\ - signal to the SpiNNaker system that, if needed, it is possible to\ - send more "SpinnakerRequestBuffers" packet + """ + Packet used in the context of buffering input for the host computer to + signal to the SpiNNaker system that, if needed, it is possible to + send more "SpinnakerRequestBuffers" packet. """ __slots__ = [] def __init__(self): - super(StartRequests, self).__init__(EIEIOCommandHeader( + super().__init__(EIEIOCommandHeader( EIEIO_COMMAND_IDS.START_SENDING_REQUESTS)) diff --git a/spinnman/messages/eieio/command_messages/stop_requests.py b/spinnman/messages/eieio/command_messages/stop_requests.py index 0470294fa..4c506b51a 100644 --- a/spinnman/messages/eieio/command_messages/stop_requests.py +++ b/spinnman/messages/eieio/command_messages/stop_requests.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .eieio_command_message import EIEIOCommandMessage from .eieio_command_header import EIEIOCommandHeader @@ -19,12 +18,13 @@ class StopRequests(EIEIOCommandMessage): - """ Packet used in the context of buffering input for the host computer to\ - signal to the SpiNNaker system that to stop sending\ - "SpinnakerRequestBuffers" packet + """ + Packet used in the context of buffering input for the host computer to + signal to the SpiNNaker system that to stop sending + "SpinnakerRequestBuffers" packet. """ __slots__ = [] def __init__(self): - super(StopRequests, self).__init__(EIEIOCommandHeader( + super().__init__(EIEIOCommandHeader( EIEIO_COMMAND_IDS.STOP_SENDING_REQUESTS)) diff --git a/spinnman/messages/eieio/create_eieio_command.py b/spinnman/messages/eieio/create_eieio_command.py index 3d972bba8..612dbd0d1 100644 --- a/spinnman/messages/eieio/create_eieio_command.py +++ b/spinnman/messages/eieio/create_eieio_command.py @@ -1,45 +1,42 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinnman.messages.eieio.command_messages import ( PaddingRequest, EventStopRequest, StopRequests, StartRequests, SpinnakerRequestBuffers, HostSendSequencedData, SpinnakerRequestReadData, HostDataRead, EIEIOCommandHeader, EIEIOCommandMessage, - DatabaseConfirmation) + NotificationProtocolDatabaseLocation) from spinnman.constants import EIEIO_COMMAND_IDS def read_eieio_command_message(data, offset): - """ Reads the content of an EIEIO command message and returns an object\ - identifying the command which was contained in the packet, including\ - any parameter, if required by the command + """ + Reads the content of an EIEIO command message and returns an object + identifying the command which was contained in the packet, including + any parameter, if required by the command. - :param data: data received from the network as a bytestring - :type data: str - :param offset: offset at which the parsing operation should start - :type offset: int - :return: an object which inherits from EIEIOCommandMessage which contains\ + :param bytes data: data received from the network as a byte-string + :param int offset: offset at which the parsing operation should start + :return: an object which inherits from EIEIOCommandMessage which contains parsed data received from the network - :rtype: \ - :py:class:`spinnman.messages.eieio.command_messages.EIEIOCommandMessage` + :rtype: EIEIOCommandMessage """ command_header = EIEIOCommandHeader.from_bytestring(data, offset) command_number = command_header.command - if command_number == EIEIO_COMMAND_IDS.DATABASE_CONFIRMATION.value: - return DatabaseConfirmation.from_bytestring( + if command_number == EIEIO_COMMAND_IDS.DATABASE.value: + return NotificationProtocolDatabaseLocation.from_bytestring( command_header, data, offset + 2) # Fill in buffer area with padding elif command_number == EIEIO_COMMAND_IDS.EVENT_PADDING.value: diff --git a/spinnman/messages/eieio/create_eieio_data.py b/spinnman/messages/eieio/create_eieio_data.py index 480871b65..9195787f4 100644 --- a/spinnman/messages/eieio/create_eieio_data.py +++ b/spinnman/messages/eieio/create_eieio_data.py @@ -1,34 +1,31 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinnman.messages.eieio.data_messages import ( EIEIODataMessage, EIEIODataHeader) def read_eieio_data_message(data, offset): - """ Reads the content of an EIEIO data message and returns an object\ - identifying the data which was contained in the packet + """ + Reads the content of an EIEIO data message and returns an object + identifying the data which was contained in the packet. - :param data: data received from the network as a bytestring - :type data: str - :param offset: offset at which the parsing operation should start - :type offset: int - :return: an object which inherits from EIEIODataMessage which contains\ + :param bytes data: data received from the network as a byte-string + :param int offset: offset at which the parsing operation should start + :return: an object which inherits from EIEIODataMessage which contains parsed data received from the network - :rtype:\ - :py:class:`spinnman.messages.eieio.data_messages.EIEIODataMessage` + :rtype: EIEIODataMessage """ eieio_header = EIEIODataHeader.from_bytestring(data, offset) offset += eieio_header.size diff --git a/spinnman/messages/eieio/data_messages/__init__.py b/spinnman/messages/eieio/data_messages/__init__.py index a7c260957..35b8d774d 100644 --- a/spinnman/messages/eieio/data_messages/__init__.py +++ b/spinnman/messages/eieio/data_messages/__init__.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .abstract_data_element import AbstractDataElement from .eieio_data_header import EIEIODataHeader diff --git a/spinnman/messages/eieio/data_messages/abstract_data_element.py b/spinnman/messages/eieio/data_messages/abstract_data_element.py index 26675cb9c..9a2e5dcbe 100644 --- a/spinnman/messages/eieio/data_messages/abstract_data_element.py +++ b/spinnman/messages/eieio/data_messages/abstract_data_element.py @@ -1,38 +1,35 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -from six import add_metaclass from spinn_utilities.abstract_base import AbstractBase, abstractmethod -@add_metaclass(AbstractBase) -class AbstractDataElement(object): - """ A marker interface for possible data elements in the EIEIO data packet +class AbstractDataElement(object, metaclass=AbstractBase): + """ + A marker interface for possible data elements in the EIEIO data packet. """ __slots__ = () @abstractmethod def get_bytestring(self, eieio_type): - """ Get a bytestring for the given type + """ + Get a byte-string for the given type - :param eieio_type: The type of the message being written - :type eieio_type:\ - :py:class:`spinnman.messages.eieio.eieio_type.EIEIOType` - :return: A bytestring for the element - :rtype: str - :raise SpinnmanInvalidParameterException: \ + :param EIEIOType eieio_type: The type of the message being written + :return: A byte-string for the element + :rtype: bytes + :raise SpinnmanInvalidParameterException: If the type is incompatible with the element """ diff --git a/spinnman/messages/eieio/data_messages/eieio_data_header.py b/spinnman/messages/eieio/data_messages/eieio_data_header.py index 0ebea9750..9ad254df2 100644 --- a/spinnman/messages/eieio/data_messages/eieio_data_header.py +++ b/spinnman/messages/eieio/data_messages/eieio_data_header.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinnman.messages.eieio import EIEIOType, EIEIOPrefix @@ -41,26 +40,22 @@ class EIEIODataHeader(object): def __init__(self, eieio_type, tag=0, prefix=None, prefix_type=EIEIOPrefix.LOWER_HALF_WORD, payload_base=None, is_time=False, count=0): - """ EIEIO header for data packets - - :param eieio_type: the type of message - :type eieio_type:\ - :py:class:`spinnman.spinnman.messages.eieio.EIEIOType` - :param tag: the tag of the message (0 by default) - :type tag: int - :param prefix: the key prefix of the message or None if not prefixed + """ + EIEIO header for data packets. + + :param EIEIOType eieio_type: the type of message + :param int tag: the tag of the message (0 by default) + :param prefix: the key prefix of the message or `None` if not prefixed :type prefix: int or None - :param prefix_type: the position of the prefix (upper or lower) - :type prefix_type:\ - :py:class:`spinnman.messages.eieio.EIEIOPrefix` - :param payload_base: \ - The base payload to be applied, or None if no base payload + :param EIEIOPrefix prefix_type: + the position of the prefix (upper or lower) + :param payload_base: + The base payload to be applied, or `None` if no base payload :type payload_base: int or None - :param is_time: True if the payloads should be taken to be timestamps,\ - or False otherwise - :type is_time: bool - :param count: Count of the number of items in the packet - :type count: int + :param bool is_time: + True if the payloads should be taken to be timestamps, or False + otherwise + :param int count: Count of the number of items in the packet """ # pylint: disable=too-many-arguments self._eieio_type = eieio_type @@ -111,16 +106,13 @@ def size(self): @staticmethod def get_header_size(eieio_type, is_prefix=False, is_payload_base=False): - """ Get the size of a header with the given parameters - - :param eieio_type: the type of message - :type eieio_type:\ - :py:class:`spinnman.spinnman.messages.eieio.EIEIOType` - :param is_prefix: True if there is a prefix, False otherwise - :type is_prefix: bool - :param is_payload_base: \ + """ + Get the size of a header with the given parameters. + + :param EIEIOType eieio_type: the type of message + :param bool is_prefix: True if there is a prefix, False otherwise + :param bool is_payload_base: True if there is a payload base, False otherwise - :type is_payload_base: bool :return: The size of the header in bytes :rtype: int """ @@ -139,12 +131,11 @@ def reset_count(self): @property def bytestring(self): - """ Get a bytestring of the header - - :return: The header as a bytestring - :rtype: str """ + The byte-string of the header. + :rtype: bytes + """ # Convert the flags to an int data = 0 @@ -191,17 +182,14 @@ def bytestring(self): @staticmethod def from_bytestring(data, offset): - """ Read an eieio data header from a bytestring + """ + Read an EIEIO data header from a byte-string. - :param data: The bytestring to be read - :type data: str - :param offset: The offset at which the data starts - :type offset: int + :param bytes data: The byte-string to be read + :param int offset: The offset at which the data starts :return: an EIEIO header - :rtype:\ - :py:class:`spinnman.messages.eieio.data_messages.EIEIODataHeader` + :rtype: EIEIODataHeader """ - (count, header_data) = _PATTERN_BB.unpack_from(data, offset) # Read the flags in the header @@ -219,6 +207,7 @@ def from_bytestring(data, offset): "The header indicates that this is a command header") # Convert the flags into types + # pylint: disable=no-value-for-parameter eieio_type = EIEIOType(message_type) prefix_type = EIEIOPrefix(format_flag) diff --git a/spinnman/messages/eieio/data_messages/eieio_data_message.py b/spinnman/messages/eieio/data_messages/eieio_data_message.py index 68e617bfb..7f90751d2 100644 --- a/spinnman/messages/eieio/data_messages/eieio_data_message.py +++ b/spinnman/messages/eieio/data_messages/eieio_data_message.py @@ -1,20 +1,19 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -import math import struct +from spinn_utilities.overrides import overrides from spinnman.exceptions import ( SpinnmanInvalidPacketException, SpinnmanInvalidParameterException) from spinnman.messages.eieio import ( @@ -31,7 +30,8 @@ class EIEIODataMessage(AbstractEIEIOMessage): - """ An EIEIO Data message + """ + An EIEIO Data message. """ __slots__ = [ "_data", @@ -42,15 +42,10 @@ class EIEIODataMessage(AbstractEIEIOMessage): def __init__(self, eieio_header, data=None, offset=0): """ - :param eieio_header: The header of the message - :type eieio_header:\ - :py:class:`spinnman.messages.eieio.data_messages.EIEIODataHeader` - :param data: Optional data contained within the packet - :type data: str - :param offset: Optional offset where the valid data starts - :type offset: int + :param EIEIODataHeader eieio_header: The header of the message + :param bytes data: Optional data contained within the packet + :param int offset: Optional offset where the valid data starts """ - # The header self._header = eieio_header @@ -67,16 +62,18 @@ def create( eieio_type, count=0, data=None, offset=0, key_prefix=None, payload_prefix=None, timestamp=None, prefix_type=EIEIOPrefix.LOWER_HALF_WORD): - """ Create a data message - - :param eieio_type: The EIEIOType of the message - :param count: The number of items in the message - :param data: The data in the message - :param offset: The offset in the data where the actual data starts - :param key_prefix: The prefix of the keys - :param payload_prefix: The prefix of the payload - :param timestamp: The timestamp of the packet - :param prefix_type: The type of the key prefix if 16-bits + """ + Create a data message. + + :param EIEIOType eieio_type: The type of the message + :param int count: The number of items in the message + :param bytes data: The data in the message + :param int offset: + The offset in the data where the actual data starts + :param int key_prefix: The prefix of the keys + :param int payload_prefix: The prefix of the payload + :param int timestamp: The timestamp of the packet + :param EIEIOPrefix prefix_type: The type of the key prefix if 16-bits """ # pylint: disable=too-many-arguments payload_base = payload_prefix @@ -90,24 +87,26 @@ def create( data=data, offset=offset) @property + @overrides(AbstractEIEIOMessage.eieio_header) def eieio_header(self): + """ + :rtype: EIEIODataHeader + """ return self._header @staticmethod def min_packet_length( eieio_type, is_prefix=False, is_payload_base=False, is_timestamp=False): - """ The minimum length of a message with the given header, in bytes - - :param eieio_type: the type of message - :type eieio_type:\ - :py:class:`spinnman.spinnman.messages.eieio.EIEIOType` - :param is_prefix: True if there is a prefix, False otherwise - :type is_prefix: bool - :param is_payload_base: \ + """ + The minimum length of a message with the given header, in bytes. + + :param EIEIOType eieio_type: the type of message + :param bool is_prefix: True if there is a prefix, False otherwise + :param bool is_payload_base: True if there is a payload base, False otherwise - :type is_payload_base: bool - :param is_timestamp: True if there is a timestamp, False otherwise + :param bool is_timestamp: + True if there is a timestamp, False otherwise :return: The minimum size of the packet in bytes :rtype: int """ @@ -116,49 +115,55 @@ def min_packet_length( return header_size + eieio_type.payload_bytes def get_min_packet_length(self): - """ Get the minimum length of a message instance in bytes + """ + Get the minimum length of a message instance in bytes. :rtype: int """ - return EIEIODataMessage.min_packet_length( + return self.min_packet_length( eieio_type=self._header.eieio_type, is_prefix=self._header.prefix is not None, is_payload_base=self._header.payload_base is not None) @property def max_n_elements(self): - """ The maximum number of elements that can fit in the packet + """ + The maximum number of elements that can fit in the packet. :rtype: int """ ty = self._header.eieio_type - return int(math.floor( - (UDP_MESSAGE_MAX_SIZE - self._header.size) / - (ty.key_bytes + ty.payload_bytes))) + return (UDP_MESSAGE_MAX_SIZE - self._header.size) // ( + ty.key_bytes + ty.payload_bytes) @property def n_elements(self): - """ The number of elements in the packet + """ + The number of elements in the packet. + + :rtype: int """ return self._header.count @property def size(self): - """ The size of the packet with the current contents + """ + The size of the packet with the current contents. + + :rtype: int """ ty = self._header.eieio_type return (self._header.size + (ty.key_bytes + ty.payload_bytes) * self._header.count) def add_key_and_payload(self, key, payload): - """ Adds a key and payload to the packet - - :param key: The key to add - :type key: int - :param payload: The payload to add - :type payload: int - :raise SpinnmanInvalidParameterException:\ - If the key or payload is too big for the format, or the format\ + """ + Adds a key and payload to the packet. + + :param int key: The key to add + :param int payload: The payload to add + :raise SpinnmanInvalidParameterException: + If the key or payload is too big for the format, or the format doesn't expect a payload """ if key > self._header.eieio_type.max_value: @@ -172,17 +177,16 @@ def add_key_and_payload(self, key, payload): "Larger than the maximum allowed of {}".format( self._header.eieio_type.max_value)) - EIEIODataMessage.add_element( - self, KeyPayloadDataElement( - key, payload, self._header.is_time)) + self.add_element(KeyPayloadDataElement( + key, payload, self._header.is_time)) def add_key(self, key): - """ Add a key to the packet + """ + Add a key to the packet. - :param key: The key to add - :type key: int - :raise SpinnmanInvalidParameterException:\ - If the key is too big for the format, or the format expects a\ + :param int key: The key to add + :raise SpinnmanInvalidParameterException: + If the key is too big for the format, or the format expects a payload """ if key > self._header.eieio_type.max_value: @@ -190,18 +194,17 @@ def add_key(self, key): "key", key, "Larger than the maximum allowed of {}".format( self._header.eieio_type.max_value)) - EIEIODataMessage.add_element(self, KeyDataElement(key)) + self.add_element(KeyDataElement(key)) def add_element(self, element): - """ Add an element to the message. The correct type of element must\ - be added, depending on the header values + """ + Add an element to the message. The correct type of element must + be added, depending on the header values. - :param element: The element to be added - :type element:\ - :py:class:`spinnman.messages.eieio.data_messages.AbstractDataElement` - :raise SpinnmanInvalidParameterException: \ + :param AbstractDataElement element: The element to be added + :raise SpinnmanInvalidParameterException: If the element is not compatible with the header - :raise SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If the message was created to read data """ if self._data is not None: @@ -213,10 +216,9 @@ def add_element(self, element): @property def is_next_element(self): - """ Determine if there is another element to be read + """ + Whether there is another element to be read. - :return: True if the message was created with data, and there are more\ - elements to be read :rtype: bool """ return (self._data is not None and @@ -224,11 +226,11 @@ def is_next_element(self): @property def next_element(self): - """ The next element to be read, or None if no more elements. The\ - exact type of element returned depends on the packet type + """ + The next element to be read, or `None` if no more elements. + The exact type of element returned depends on the packet type. - :rtype:\ - :py:class:`spinnman.messages.eieio.data_messages.AbstractDataElement` + :rtype: AbstractDataElement """ if not self.is_next_element: return None @@ -265,15 +267,14 @@ def next_element(self): return KeyPayloadDataElement(key, payload, self._header.is_time) @property + @overrides(AbstractEIEIOMessage.bytestring) def bytestring(self): return self._header.bytestring + self._elements def __str__(self): if self._data is not None: - return "EIEIODataMessage:{}:{}".format( - self._header, self._header.count) - return "EIEIODataMessage:{}:{}".format( - self._header, self._elements) + return f"EIEIODataMessage:{self._header}:{self._header.count}" + return f"EIEIODataMessage:{self._header}:{self._elements}" def __repr__(self): return self.__str__() diff --git a/spinnman/messages/eieio/data_messages/key_data_element.py b/spinnman/messages/eieio/data_messages/key_data_element.py index 4d3a848fc..b4455647d 100644 --- a/spinnman/messages/eieio/data_messages/key_data_element.py +++ b/spinnman/messages/eieio/data_messages/key_data_element.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinnman.exceptions import SpinnmanInvalidParameterException @@ -23,7 +22,8 @@ class KeyDataElement(AbstractDataElement): - """ A data element that contains just a key + """ + A data element that contains just a key. """ __slots__ = [ "_key"] @@ -50,7 +50,7 @@ def get_bytestring(self, eieio_type): "eieio_type", eieio_type, "Unknown type") def __str__(self): - return "KeyDataElement:{}".format(hex(self._key)) + return f"KeyDataElement:0x{self._key:x}" def __repr__(self): return self.__str__() diff --git a/spinnman/messages/eieio/data_messages/key_payload_data_element.py b/spinnman/messages/eieio/data_messages/key_payload_data_element.py index 3523ab04c..7a7289550 100644 --- a/spinnman/messages/eieio/data_messages/key_payload_data_element.py +++ b/spinnman/messages/eieio/data_messages/key_payload_data_element.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinnman.exceptions import SpinnmanInvalidParameterException @@ -23,7 +22,8 @@ class KeyPayloadDataElement(AbstractDataElement): - """ A data element that contains a key and a payload + """ + A data element that contains a key and a payload. """ __slots__ = [ "_key", @@ -62,8 +62,7 @@ def get_bytestring(self, eieio_type): "eieio_type", eieio_type, "Unknown type") def __str__(self): - return "KeyPayloadDataElement:{}:{}".format( - hex(self._key), hex(self._payload)) + return f"KeyPayloadDataElement:0x{self._key:x}:0x{self._payload:x}" def __repr__(self): return self.__str__() diff --git a/spinnman/messages/eieio/eieio_message.py b/spinnman/messages/eieio/eieio_message.py index 40bb2dc52..b8190783a 100644 --- a/spinnman/messages/eieio/eieio_message.py +++ b/spinnman/messages/eieio/eieio_message.py @@ -1,21 +1,36 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from spinn_utilities.abstract_base import abstractproperty, AbstractBase -class AbstractEIEIOMessage(object): - """ Marker interface for an EIEIOMessage +class AbstractEIEIOMessage(object, metaclass=AbstractBase): + """ + Interface for an EIEIOMessage. """ __slots__ = () + + @abstractproperty + def eieio_header(self): + """ + The header of the message. + """ + + @abstractproperty + def bytestring(self): + """ + The bytes of the message. + + :rtype: bytes + """ diff --git a/spinnman/messages/eieio/eieio_prefix.py b/spinnman/messages/eieio/eieio_prefix.py index 6f91bbfe6..866469313 100644 --- a/spinnman/messages/eieio/eieio_prefix.py +++ b/spinnman/messages/eieio/eieio_prefix.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class EIEIOPrefix(Enum): - """ Possible prefixing of keys in EIEIO packets + """ + Possible prefixing of keys in EIEIO packets. """ LOWER_HALF_WORD = (0, "apply prefix on lower half of the word") UPPER_HALF_WORD = (1, "apply prefix on top half of the word") diff --git a/spinnman/messages/eieio/eieio_type.py b/spinnman/messages/eieio/eieio_type.py index da07075d7..5a0f5f31d 100644 --- a/spinnman/messages/eieio/eieio_type.py +++ b/spinnman/messages/eieio/eieio_type.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class EIEIOType(Enum): - """ Possible types of EIEIO packets + """ + Possible types of EIEIO packets. """ KEY_16_BIT = (0, 2, 0, "Indicates that data is keys which are 16 bits") KEY_PAYLOAD_16_BIT = ( @@ -40,7 +40,8 @@ def __init__(self, value, key_bytes, payload_bytes, doc=""): @property def key_bytes(self): - """ The number of bytes used by each key element + """ + The number of bytes used by each key element. :rtype: int """ @@ -48,7 +49,8 @@ def key_bytes(self): @property def payload_bytes(self): - """ The number of bytes used by each payload element + """ + The number of bytes used by each payload element. :rtype: int """ @@ -56,7 +58,8 @@ def payload_bytes(self): @property def max_value(self): - """ The maximum value of the key or payload (if there is a payload) + """ + The maximum value of the key or payload (if there is a payload). :rtype: int """ diff --git a/spinnman/messages/multicast_message.py b/spinnman/messages/multicast_message.py index b6a761cb1..ab479c04c 100644 --- a/spinnman/messages/multicast_message.py +++ b/spinnman/messages/multicast_message.py @@ -1,53 +1,50 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. class MulticastMessage(object): - """ A SpiNNaker Multicast message + """ + A SpiNNaker Multicast message, comprising a key (determining the target + locations) and an optional payload. """ __slots__ = [ "_key", "_payload"] def __init__(self, key, payload=None): - """ A multicast message has a key (determining the target locations) \ - and an optional payload. - - :param key: The key of the packet - :type key: int - :param payload: The optional payload of the packet - :type payload: int - :raise None: No known exceptions are raised + """ + :param int key: The key of the packet + :param int payload: The optional payload of the packet """ self._key = key self._payload = payload @property def key(self): - """ The key of the packet + """ + The key of the packet. - :return: The key :rtype: int """ return self._key @property def payload(self): - """ The payload of the packet if there is one + """ + The payload of the packet if there is one, or `None` if there is no + payload. - :return: The payload, or None if there is no payload - :rtype: int + :rtype: int or None """ return self._payload diff --git a/spinnman/messages/scp/__init__.py b/spinnman/messages/scp/__init__.py index 73b21edf9..3ae36bdc8 100644 --- a/spinnman/messages/scp/__init__.py +++ b/spinnman/messages/scp/__init__.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .scp_request_header import SCPRequestHeader from .scp_response_header import SCPResponseHeader diff --git a/spinnman/messages/scp/abstract_messages/__init__.py b/spinnman/messages/scp/abstract_messages/__init__.py index b4f1ef882..1ca8daad5 100644 --- a/spinnman/messages/scp/abstract_messages/__init__.py +++ b/spinnman/messages/scp/abstract_messages/__init__.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .bmp_response import BMPResponse from .bmp_request import BMPRequest diff --git a/spinnman/messages/scp/abstract_messages/bmp_request.py b/spinnman/messages/scp/abstract_messages/bmp_request.py index 95333d824..342d08d88 100644 --- a/spinnman/messages/scp/abstract_messages/bmp_request.py +++ b/spinnman/messages/scp/abstract_messages/bmp_request.py @@ -1,24 +1,24 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .scp_request import AbstractSCPRequest from spinnman.messages.sdp import SDPFlag, SDPHeader class BMPRequest(AbstractSCPRequest): - """ An SCP request intended to be sent to a BMP. + """ + An SCP request intended to be sent to a BMP. """ __slots__ = [] @@ -26,25 +26,26 @@ def __init__(self, boards, scp_request_header, argument_1=None, argument_2=None, argument_3=None, data=None): """ :param boards: The board or boards to be addressed by this request - :type boards: int or iterable of int - :param scp_request_header: The SCP request header - :param argument_1: The optional first argument - :param argument_2: The optional second argument - :param argument_3: The optional third argument - :param data: The optional data to be sent + :type boards: int or list(int) or tuple(int) + :param SCPRequestHeader scp_request_header: The SCP request header + :param int argument_1: The optional first argument + :param int argument_2: The optional second argument + :param int argument_3: The optional third argument + :param bytes data: The optional data to be sent """ # pylint: disable=too-many-arguments sdp_header = SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=BMPRequest.get_first_board(boards), destination_chip_x=0, destination_chip_y=0) - super(BMPRequest, self).__init__( + super().__init__( sdp_header, scp_request_header, argument_1, argument_2, argument_3, data) @staticmethod def get_first_board(boards): - """ Get the first board ID given a board ID or collection of board IDs + """ + Get the first board ID given a board ID or collection of board IDs. """ if isinstance(boards, int): return boards @@ -52,7 +53,8 @@ def get_first_board(boards): @staticmethod def get_board_mask(boards): - """ Get the board mask given a board ID or collection of board IDs + """ + Get the board mask given a board ID or collection of board IDs. """ if isinstance(boards, int): return 1 << boards diff --git a/spinnman/messages/scp/abstract_messages/bmp_response.py b/spinnman/messages/scp/abstract_messages/bmp_response.py index b5ba1ef97..cd9dfaecb 100644 --- a/spinnman/messages/scp/abstract_messages/bmp_response.py +++ b/spinnman/messages/scp/abstract_messages/bmp_response.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .scp_response import AbstractSCPResponse class BMPResponse(AbstractSCPResponse): - """ Represents an SCP request thats tailored for the BMP connection. + """ + Represents an SCP request that's tailored for the BMP connection. """ __slots__ = () diff --git a/spinnman/messages/scp/abstract_messages/scp_request.py b/spinnman/messages/scp/abstract_messages/scp_request.py index 19cfeff97..2bf371bba 100644 --- a/spinnman/messages/scp/abstract_messages/scp_request.py +++ b/spinnman/messages/scp/abstract_messages/scp_request.py @@ -1,28 +1,26 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct -from six import add_metaclass from spinn_utilities.abstract_base import AbstractBase, abstractmethod _THREE_WORDS = struct.Struct(". +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -from six import add_metaclass from spinn_utilities.abstract_base import AbstractBase, abstractmethod from spinnman.messages.sdp import SDPHeader from spinnman.messages.scp import SCPResponseHeader @@ -27,28 +25,25 @@ _SCP_DATA_OFFSET = 12 -@add_metaclass(AbstractBase) -class AbstractSCPResponse(object): - """ Represents an abstract SCP Response +class AbstractSCPResponse(object, metaclass=AbstractBase): + """ + Represents an abstract SCP Response. """ __slots__ = [ "_scp_response_header", "_sdp_header"] def __init__(self): - """ - """ self._sdp_header = None self._scp_response_header = None def read_bytestring(self, data, offset): - """ Reads a packet from a bytestring of data + """ + Reads a packet from a byte-string of data. - :param data: The bytestring to be read - :type data: str - :param offset: \ + :param bytes data: The byte-string to be read + :param int offset: The offset in the data from which the response should be read - :type offset: int """ self._sdp_header = SDPHeader.from_bytestring(data, offset) self._scp_response_header = SCPResponseHeader.from_bytestring( @@ -57,31 +52,27 @@ def read_bytestring(self, data, offset): @abstractmethod def read_data_bytestring(self, data, offset): - """ Reads the remainder of the data following the header + """ + Reads the remainder of the data following the header. - :param data: The bytestring to read from - :type data: str - :param offset: The offset into the data after the headers - :type offset: int + :param bytes data: The byte-string to read from + :param int offset: The offset into the data after the headers """ @property def sdp_header(self): - """ The SDP header from the response + """ + The SDP header from the response. - :return: The SDP header - :rtype: :py:class:`spinnman.messages.sdp.sdp_header.SDPHeader` - :raise None: No known exceptions are raised + :rtype: SDPHeader """ return self._sdp_header @property def scp_response_header(self): - """ The SCP header from the response + """ + The SCP header from the response. - :return: The SCP header - :rtype:\ - :py:class:`spinnman.messages.scp.SCPResponseHeader` - :raise None: No known exceptions are raised + :rtype: SCPResponseHeader """ return self._scp_response_header diff --git a/spinnman/messages/scp/enums/__init__.py b/spinnman/messages/scp/enums/__init__.py index 711c8cabd..3f4ca389e 100644 --- a/spinnman/messages/scp/enums/__init__.py +++ b/spinnman/messages/scp/enums/__init__.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2016 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .alloc_free import AllocFree from .bmp_info import BMPInfo @@ -20,9 +19,9 @@ from .led_action import LEDAction from .power_command import PowerCommand from .scp_result import SCPResult -from .signal import Signal +from .signal import Signal, SignalType __all__ = [ "AllocFree", "BMPInfo", "SCPCommand", "IPTagCommand", "LEDAction", - "PowerCommand", "SCPResult", "Signal", ] + "PowerCommand", "SCPResult", "Signal", "SignalType"] diff --git a/spinnman/messages/scp/enums/alloc_free.py b/spinnman/messages/scp/enums/alloc_free.py index 2eddbc682..107f322ec 100644 --- a/spinnman/messages/scp/enums/alloc_free.py +++ b/spinnman/messages/scp/enums/alloc_free.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class AllocFree(Enum): - """ The SCP Allocation and Free codes + """ + The SCP Allocation and Free codes. """ ALLOC_SDRAM = (0, "Allocate SDRAM") diff --git a/spinnman/messages/scp/enums/bmp_info.py b/spinnman/messages/scp/enums/bmp_info.py index b093295dd..7359a0b36 100644 --- a/spinnman/messages/scp/enums/bmp_info.py +++ b/spinnman/messages/scp/enums/bmp_info.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class BMPInfo(Enum): - """ The SCP BMP Information Types + """ + The SCP BMP Information Types. """ SERIAL = (0, "Serial information") diff --git a/spinnman/messages/scp/enums/iptag_command.py b/spinnman/messages/scp/enums/iptag_command.py index e2200e50f..b5e4cc69b 100644 --- a/spinnman/messages/scp/enums/iptag_command.py +++ b/spinnman/messages/scp/enums/iptag_command.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class IPTagCommand(Enum): - """ SCP IP tag Commands + """ + SCP IP tag Commands. """ NEW = 0 SET = 1 diff --git a/spinnman/messages/scp/enums/led_action.py b/spinnman/messages/scp/enums/led_action.py index 644326d0b..1a07c5260 100644 --- a/spinnman/messages/scp/enums/led_action.py +++ b/spinnman/messages/scp/enums/led_action.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class LEDAction(Enum): - """ The SCP LED actions + """ + The SCP LED actions. """ TOGGLE = (1, "Toggle the LED status") diff --git a/spinnman/messages/scp/enums/power_command.py b/spinnman/messages/scp/enums/power_command.py index 094ec7f9e..17bf513cb 100644 --- a/spinnman/messages/scp/enums/power_command.py +++ b/spinnman/messages/scp/enums/power_command.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class PowerCommand(Enum): - """ The SCP Power Commands + """ + The SCP Power Commands. """ POWER_OFF = (0, "Power off the machine") diff --git a/spinnman/messages/scp/enums/scp_command.py b/spinnman/messages/scp/enums/scp_command.py index c0b04976a..d1cedd107 100644 --- a/spinnman/messages/scp/enums/scp_command.py +++ b/spinnman/messages/scp/enums/scp_command.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class SCPCommand(Enum): - """ The SCP Commands + """ + The SCP Commands. """ CMD_VER = (0, "Get SCAMP Version") CMD_RUN = 1 @@ -30,7 +30,7 @@ class SCPCommand(Enum): CMD_LINK_WRITE = (18, "Write neighbouring chip's memory.") CMD_AR = 19 CMD_NNP = (20, "Send a Nearest-Neighbour packet") - CMD_P2PC = 21 + CMD_APP_COPY_RUN = (21, "Copy a binary from an adjacent chip and start it") CMD_SIG = (22, "Send a Signal") CMD_FFD = (23, "Send Flood-Fill Data") CMD_AS = 24 @@ -41,6 +41,7 @@ class SCPCommand(Enum): CMD_RTR = (29, "Initialise the router") CMD_DPRI = (30, "Dropped Packet Reinjection setup") CMD_INFO = (31, "Get Chip Summary Information") + CMD_SYNC = (32, "Control sending of synchronization messages") CMD_BMP_INFO = (48, "Get BMP info structures") CMD_FLASH_COPY = 49 CMD_FLASH_ERASE = 50 diff --git a/spinnman/messages/scp/enums/scp_result.py b/spinnman/messages/scp/enums/scp_result.py index 90c44f955..757a96745 100644 --- a/spinnman/messages/scp/enums/scp_result.py +++ b/spinnman/messages/scp/enums/scp_result.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class SCPResult(Enum): - """ The SCP Result codes + """ + The SCP Result codes. """ RC_OK = (0x80, "SCPCommand completed OK") RC_LEN = (0x81, "Bad packet length") diff --git a/spinnman/messages/scp/enums/signal.py b/spinnman/messages/scp/enums/signal.py index 3b2ac7d53..775348f85 100644 --- a/spinnman/messages/scp/enums/signal.py +++ b/spinnman/messages/scp/enums/signal.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class SignalType(Enum): - """ The type of signal, determined by how it is transmitted + """ + The type of signal, determined by how it is transmitted. """ MULTICAST = 0 POINT_TO_POINT = 1 @@ -25,12 +25,13 @@ class SignalType(Enum): class Signal(Enum): - """ SCP Signals + """ + SCP Signals. """ INITIALISE = (0, SignalType.NEAREST_NEIGHBOUR) POWER_DOWN = (1, SignalType.NEAREST_NEIGHBOUR) STOP = (2, SignalType.NEAREST_NEIGHBOUR) - START = (3, SignalType.NEAREST_NEIGHBOUR) + START = (3, SignalType.MULTICAST) SYNC0 = (4, SignalType.MULTICAST) SYNC1 = (5, SignalType.MULTICAST) PAUSE = (6, SignalType.MULTICAST) @@ -50,11 +51,8 @@ def __new__(cls, value, signal_type, doc=""): def __init__(self, value, signal_type, doc=""): """ - - :param value: The value used for the signal - :type value: int - :param signal_type: The "type" of the signal - :type signal_type: :py:class:`.SignalType` + :param int value: The value used for the signal + :param SignalType signal_type: The "type" of the signal """ self._value_ = value self._signal_type = signal_type diff --git a/spinnman/messages/scp/impl/__init__.py b/spinnman/messages/scp/impl/__init__.py index 3a9af3c35..05c8fb667 100644 --- a/spinnman/messages/scp/impl/__init__.py +++ b/spinnman/messages/scp/impl/__init__.py @@ -1,24 +1,24 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .app_stop import AppStop +from .app_copy_run import AppCopyRun from .application_run import ApplicationRun from .bmp_get_version import BMPGetVersion -from .bmp_set_led import BMPSetLed from .check_ok_response import CheckOKResponse from .count_state import CountState +from .do_sync import DoSync from .fill_request import FillRequest from .flood_fill_data import FloodFillData from .flood_fill_end import FloodFillEnd @@ -30,7 +30,6 @@ from .iptag_get_info import IPTagGetInfo from .iptag_set import IPTagSet from .iptag_set_tto import IPTagSetTTO -from .read_adc import ReadADC from .read_fpga_register import ReadFPGARegister from .read_link import ReadLink from .read_memory import ReadMemory @@ -41,7 +40,6 @@ from .sdram_alloc import SDRAMAlloc from .sdram_de_alloc import SDRAMDeAlloc from .send_signal import SendSignal -from .set_led import SetLED from .set_power import SetPower from .write_fpga_register import WriteFPGARegister from .write_link import WriteLink @@ -49,15 +47,14 @@ from .fixed_route_init import FixedRouteInit from .fixed_route_read import FixedRouteRead -__all__ = ["AppStop", "ApplicationRun", - "BMPSetLed", "BMPGetVersion", - "CheckOKResponse", "GetChipInfo", "CountState", +__all__ = ["AppStop", "ApplicationRun", "AppCopyRun", + "BMPGetVersion", "CheckOKResponse", "GetChipInfo", "CountState", + "DoSync", "FloodFillData", "FillRequest", "FloodFillEnd", "FloodFillStart", "IPTagClear", "IPTagGet", "IPTagGetInfo", - "IPTagSet", "IPTagSetTTO", "SetLED", - "SetPower", "ReadADC", - "ReadFPGARegister", "ReadLink", + "IPTagSet", "IPTagSetTTO", + "SetPower", "ReadFPGARegister", "ReadLink", "ReadMemory", "ReverseIPTagSet", "RouterAlloc", "RouterClear", "RouterInit", "SDRAMAlloc", diff --git a/spinnman/messages/scp/impl/app_copy_run.py b/spinnman/messages/scp/impl/app_copy_run.py new file mode 100644 index 000000000..a5f92eef0 --- /dev/null +++ b/spinnman/messages/scp/impl/app_copy_run.py @@ -0,0 +1,79 @@ +# Copyright (c) 2017 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from spinn_utilities.overrides import overrides +from spinnman.exceptions import SpinnmanInvalidParameterException +from spinnman.messages.scp import SCPRequestHeader +from spinnman.messages.scp.abstract_messages import AbstractSCPRequest +from spinnman.messages.scp.enums import SCPCommand +from spinnman.messages.sdp import SDPFlag, SDPHeader +from spinnman.messages.scp.impl.check_ok_response import CheckOKResponse + + +_WAIT_FLAG = 0x1 << 18 + + +class AppCopyRun(AbstractSCPRequest): + """ + An SCP request to copy an application and start it. + """ + __slots__ = ["__link"] + + def __init__(self, x, y, link, size, app_id, processors, chksum, + wait=False): + """ + :param int x: + The x-coordinate of the chip to read from, between 0 and 255 + :param int y: + The y-coordinate of the chip to read from, between 0 and 255 + :param int link: The ID of the link from which to copy + :param int size: The number of bytes to read, must be divisible by 4 + :param int app_id: The app to associate the copied binary with + :param list(int) processors: The processors to start on the chip + :param int chksum: The checksum of the data to copy + :param bool wait: Whether to start in wait mode or not + """ + # pylint: disable=too-many-arguments + if size % 4 != 0: + raise SpinnmanInvalidParameterException( + "size", size, "The size must be a multiple of 4") + + processor_mask = 0 + if processors is not None: + for processor in processors: + processor_mask |= (1 << processor) + + processor_mask |= (app_id << 24) + if wait: + processor_mask |= _WAIT_FLAG + self.__link = link + + arg1 = ((chksum & 0x1FFFFFFF) << 3) | link + + super().__init__( + SDPHeader( + flags=SDPFlag.REPLY_EXPECTED, destination_port=0, + destination_cpu=0, destination_chip_x=x, + destination_chip_y=y), + SCPRequestHeader(command=SCPCommand.CMD_APP_COPY_RUN), + argument_1=arg1, argument_2=size, argument_3=processor_mask) + + def __repr__(self): + return f"{super(AppCopyRun, self).__repr__()} (Link {self.__link})" + + @overrides(AbstractSCPRequest.get_scp_response) + def get_scp_response(self): + return CheckOKResponse( + f"Application Copy Run (Link {self.__link})", + SCPCommand.CMD_APP_COPY_RUN) diff --git a/spinnman/messages/scp/impl/app_stop.py b/spinnman/messages/scp/impl/app_stop.py index 6044494a4..8da09aae5 100644 --- a/spinnman/messages/scp/impl/app_stop.py +++ b/spinnman/messages/scp/impl/app_stop.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -30,16 +29,16 @@ def _get_data(app_id, signal): class AppStop(AbstractSCPRequest): - """ An SCP Request to stop an application + """ + An SCP Request to stop an application. """ __slots__ = [] def __init__(self, app_id): """ - :param app_id: The ID of the application, between 0 and 255 - :type app_id: int + :param int app_id: The ID of the application, between 0 and 255 """ - super(AppStop, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, diff --git a/spinnman/messages/scp/impl/application_run.py b/spinnman/messages/scp/impl/application_run.py index 3c617a144..643e50717 100644 --- a/spinnman/messages/scp/impl/application_run.py +++ b/spinnman/messages/scp/impl/application_run.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp.abstract_messages import AbstractSCPRequest @@ -24,25 +23,21 @@ class ApplicationRun(AbstractSCPRequest): - """ An SCP request to run an application loaded on a chip + """ + An SCP request to run an application loaded on a chip. """ __slots__ = [] def __init__(self, app_id, x, y, processors, wait=False): """ - :param app_id: The ID of the application to run, between 16 and 255 - :type app_id: int - :param x: The x-coordinate of the chip to run on, between 0 and 255 - :type x: int - :param y: The y-coordinate of the chip to run on, between 0 and 255 - :type y: int - :param processors: \ - The processors on the chip where the executable should be \ + :param int app_id: The ID of the application to run, between 16 and 255 + :param int x: The x-coordinate of the chip to run on, between 0 and 255 + :param int y: The y-coordinate of the chip to run on, between 0 and 255 + :param list(int) processors: + The processors on the chip where the executable should be started, between 1 and 17 - :type processors: list of int - :param wait: \ + :param bool wait: True if the processors should enter a "wait" state on starting - :type wait: bool """ # pylint: disable=too-many-arguments processor_mask = 0 @@ -54,7 +49,7 @@ def __init__(self, app_id, x, y, processors, wait=False): if wait: processor_mask |= _WAIT_FLAG - super(ApplicationRun, self).__init__( + super().__init__( SDPHeader(flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, destination_chip_y=y), diff --git a/spinnman/messages/scp/impl/bmp_get_version.py b/spinnman/messages/scp/impl/bmp_get_version.py index 650609d3f..fef2a6783 100644 --- a/spinnman/messages/scp/impl/bmp_get_version.py +++ b/spinnman/messages/scp/impl/bmp_get_version.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp.abstract_messages import ( @@ -22,18 +21,18 @@ class BMPGetVersion(BMPRequest): - """ An SCP request to read the version of software running on a core + """ + An SCP request to read the version of software running on a core. """ def __init__(self, board): """ - :param board: The board to get the version from - :type board: int - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :param int board: The board to get the version from + :raise SpinnmanInvalidParameterException: * If the chip coordinates are out of range * If the processor is out of range """ - super(BMPGetVersion, self).__init__( + super().__init__( board, SCPRequestHeader(command=SCPCommand.CMD_VER)) @overrides(AbstractSCPRequest.get_scp_response) diff --git a/spinnman/messages/scp/impl/check_ok_response.py b/spinnman/messages/scp/impl/check_ok_response.py index e46bec818..f9101e505 100644 --- a/spinnman/messages/scp/impl/check_ok_response.py +++ b/spinnman/messages/scp/impl/check_ok_response.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp.abstract_messages import AbstractSCPResponse @@ -20,7 +19,8 @@ class CheckOKResponse(AbstractSCPResponse): - """ An SCP response to a request which returns nothing other than OK + """ + An SCP response to a request which returns nothing other than OK. """ __slots__ = [ "_command", @@ -28,12 +28,11 @@ class CheckOKResponse(AbstractSCPResponse): def __init__(self, operation, command): """ - :param operation: The operation being performed - :type operation: str + :param str operation: The operation being performed :param command: The command that was sent - :type command: str + :type command: str or ~enum.Enum or int """ - super(CheckOKResponse, self).__init__() + super().__init__() self._operation = operation self._command = command @@ -42,4 +41,4 @@ def read_data_bytestring(self, data, offset): result = self.scp_response_header.result if result != SCPResult.RC_OK: raise SpinnmanUnexpectedResponseCodeException( - self._operation, self._command, result.name) + self._operation, str(self._command), result.name) diff --git a/spinnman/messages/scp/impl/count_state.py b/spinnman/messages/scp/impl/count_state.py index 23cb7c719..59dcf3749 100644 --- a/spinnman/messages/scp/impl/count_state.py +++ b/spinnman/messages/scp/impl/count_state.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -35,19 +34,17 @@ def _get_data(app_id, state): class CountState(AbstractSCPRequest): - """ An SCP Request to get a count of the cores in a particular state + """ + An SCP Request to get a count of the cores in a particular state. """ __slots__ = [] def __init__(self, app_id, state): """ - - :param app_id: The ID of the application, between 0 and 255 - :type app_id: int - :param state: The state to count - :type state: :py:class:`spinnman.model.cpu_state.CPUState` + :param int app_id: The ID of the application, between 0 and 255 + :param CPUState state: The state to count """ - super(CountState, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, diff --git a/spinnman/messages/scp/impl/count_state_response.py b/spinnman/messages/scp/impl/count_state_response.py index 92e0b9acd..b55912b04 100644 --- a/spinnman/messages/scp/impl/count_state_response.py +++ b/spinnman/messages/scp/impl/count_state_response.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinn_utilities.overrides import overrides @@ -23,13 +22,14 @@ class CountStateResponse(AbstractSCPResponse): - """ An SCP response to a request for the number of cores in a given state + """ + An SCP response to a request for the number of cores in a given state. """ __slots__ = [ "_count"] def __init__(self): - super(CountStateResponse, self).__init__() + super().__init__() self._count = None @overrides(AbstractSCPResponse.read_data_bytestring) @@ -42,7 +42,8 @@ def read_data_bytestring(self, data, offset): @property def count(self): - """ The count of the number of cores with the requested state + """ + The count of the number of cores with the requested state. :rtype: int """ diff --git a/spinnman/messages/scp/impl/do_sync.py b/spinnman/messages/scp/impl/do_sync.py new file mode 100644 index 000000000..95ff44e51 --- /dev/null +++ b/spinnman/messages/scp/impl/do_sync.py @@ -0,0 +1,44 @@ +# Copyright (c) 2017 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from spinn_utilities.overrides import overrides +from spinnman.messages.scp import SCPRequestHeader +from spinnman.messages.scp.abstract_messages import AbstractSCPRequest +from spinnman.messages.scp.enums import SCPCommand +from spinnman.messages.sdp import SDPFlag, SDPHeader +from .check_ok_response import CheckOKResponse + + +class DoSync(AbstractSCPRequest): + """ + An SCP Request to control synchronization. + """ + __slots__ = [] + + def __init__(self, do_sync): + """ + :param bool do_sync: Whether to synchronize or not + """ + super().__init__( + SDPHeader( + flags=SDPFlag.REPLY_EXPECTED, destination_port=0, + destination_cpu=0, + destination_chip_x=self.DEFAULT_DEST_X_COORD, + destination_chip_y=self.DEFAULT_DEST_Y_COORD), + SCPRequestHeader(command=SCPCommand.CMD_SYNC), + argument_1=int(do_sync)) + + @overrides(AbstractSCPRequest.get_scp_response) + def get_scp_response(self): + return CheckOKResponse("Control Synchronization", "CMD_SYNC") diff --git a/spinnman/messages/scp/impl/fill_request.py b/spinnman/messages/scp/impl/fill_request.py index 55aa040c9..03fed247d 100644 --- a/spinnman/messages/scp/impl/fill_request.py +++ b/spinnman/messages/scp/impl/fill_request.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2016 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.sdp import SDPFlag, SDPHeader @@ -22,25 +21,24 @@ class FillRequest(AbstractSCPRequest): - """ An SCP request to fill a region of memory on a chip with repeated data + """ + An SCP request to fill a region of memory on a chip with repeated data """ __slots__ = [] def __init__(self, x, y, base_address, data, size): """ - :param x: The x-coordinate of the chip to read from, between 0 and 255 - :type x: int - :param y: The y-coordinate of the chip to read from, between 0 and 255 - :type y: int - :param base_address: The positive base address to start the fill from - :type base_address: int - :param data: The data to fill in the space with - :type data: int - :param size: The number of bytes to fill in - :type size: int + :param int x: + The x-coordinate of the chip to read from, between 0 and 255 + :param int y: + The y-coordinate of the chip to read from, between 0 and 255 + :param int base_address: + The positive base address to start the fill from + :param int data: The data to fill in the space with + :param int size: The number of bytes to fill in """ # pylint: disable=too-many-arguments - super(FillRequest, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, diff --git a/spinnman/messages/scp/impl/fixed_route_init.py b/spinnman/messages/scp/impl/fixed_route_init.py index d27acf303..7482e820d 100644 --- a/spinnman/messages/scp/impl/fixed_route_init.py +++ b/spinnman/messages/scp/impl/fixed_route_init.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -22,27 +21,26 @@ class FixedRouteInit(AbstractSCPRequest): + """ + Sets a fixed route entry. + """ __slots__ = [] def __init__(self, x, y, entry, app_id): - """ Sets a fixed route entry - - :param x: The x-coordinate of the chip, between 0 and 255,\ + """ + :param int x: The x-coordinate of the chip, between 0 and 255, this is not checked due to speed restrictions - :type x: int - :param y: The y-coordinate of the chip, between 0 and 255,\ + :param int y: The y-coordinate of the chip, between 0 and 255, this is not checked due to speed restrictions - :type y: int - :param entry: the fixed route entry converted for writing - :param app_id: The ID of the application with which to associate the\ - routes. If not specified, defaults to 0. - :type app_id: int - :raise spinnman.exceptions.SpinnmanInvalidParameterException:\ + :param int entry: the fixed route entry converted for writing + :param int app_id: + The ID of the application with which to associate the routes. + If not specified, defaults to 0. + :raise SpinnmanInvalidParameterException: * If x is out of range * If y is out of range """ - - super(FixedRouteInit, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, diff --git a/spinnman/messages/scp/impl/fixed_route_read.py b/spinnman/messages/scp/impl/fixed_route_read.py index 6c0f1dcaa..d7d8bf720 100644 --- a/spinnman/messages/scp/impl/fixed_route_read.py +++ b/spinnman/messages/scp/impl/fixed_route_read.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinn_utilities.overrides import overrides @@ -27,25 +26,26 @@ class FixedRouteRead(AbstractSCPRequest): + """ + Gets a fixed route entry. + """ + __slots__ = [] def __init__(self, x, y, app_id): - """ Sets a fixed route entry - - :param x: The x-coordinate of the chip, between 0 and 255,\ + """ + :param int x: The x-coordinate of the chip, between 0 and 255, this is not checked due to speed restrictions - :type x: int - :param y: The y-coordinate of the chip, between 0 and 255\ + :param int y: The y-coordinate of the chip, between 0 and 255, this is not checked due to speed restrictions - :type y: int - :param app_id: The ID of the application with which to associate the\ - routes. If not specified, defaults to 0. - :type app_id: int - :raise spinnman.exceptions.SpinnmanInvalidParameterException:\ + :param int app_id: + The ID of the application with which to associate the routes. + If not specified, defaults to 0. + :raise SpinnmanInvalidParameterException: * If x is out of range * If y is out of range """ - super(FixedRouteRead, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, @@ -67,7 +67,7 @@ class _FixedRouteResponse(AbstractSCPResponse): "_route"] def __init__(self): - super(_FixedRouteResponse, self).__init__() + super().__init__() self._route = None @overrides(AbstractSCPResponse.read_data_bytestring) diff --git a/spinnman/messages/scp/impl/flood_fill_data.py b/spinnman/messages/scp/impl/flood_fill_data.py index d568b4691..bd527cd5d 100644 --- a/spinnman/messages/scp/impl/flood_fill_data.py +++ b/spinnman/messages/scp/impl/flood_fill_data.py @@ -1,19 +1,17 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -from __future__ import division from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader from spinnman.messages.scp.abstract_messages import AbstractSCPRequest @@ -21,12 +19,13 @@ from spinnman.messages.sdp import SDPFlag, SDPHeader from .check_ok_response import CheckOKResponse -_NNP_FORWARD_RETRY = (0x3f << 24) | (0x1A << 16) +_NNP_FORWARD_RETRY = (0x3f << 24) | (0x1E << 16) _NNP_FLOOD_FILL_START = 6 class FloodFillData(AbstractSCPRequest): - """ A request to start a flood fill of data + """ + A request to start a flood fill of data. """ __slots__ = [ "_data_to_write", @@ -36,15 +35,14 @@ class FloodFillData(AbstractSCPRequest): def __init__(self, nearest_neighbour_id, block_no, base_address, data, offset=0, length=None): """ - :param nearest_neighbour_id: The ID of the packet, between 0 and 127 - :type nearest_neighbour_id: int - :param block_no: Which block this block is, between 0 and 255 - :type block_no: int - :param base_address: The base address where the data is to be loaded - :type base_address: int - :param data: The data to load, between 4 and 256 bytes and the size\ - must be divisible by 4 - :type data: bytearray + :param int nearest_neighbour_id: + The ID of the packet, between 0 and 127 + :param int block_no: Which block this block is, between 0 and 255 + :param int base_address: + The base address where the data is to be loaded + :param bytes data: + The data to load, between 4 and 256 bytes and the size must be + divisible by 4 """ # pylint: disable=too-many-arguments self._size = length @@ -56,7 +54,7 @@ def __init__(self, nearest_neighbour_id, block_no, base_address, data, argument_1 = _NNP_FORWARD_RETRY | nearest_neighbour_id argument_2 = (block_no << 16) | (((self._size // 4) - 1) << 8) - super(FloodFillData, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, @@ -68,7 +66,7 @@ def __init__(self, nearest_neighbour_id, block_no, base_address, data, @property def bytestring(self): - datastring = super(FloodFillData, self).bytestring + datastring = super().bytestring data = self._data_to_write[self._offset:self._offset + self._size] return datastring + bytes(data) diff --git a/spinnman/messages/scp/impl/flood_fill_end.py b/spinnman/messages/scp/impl/flood_fill_end.py index 7ecae5a41..8b911f6bb 100644 --- a/spinnman/messages/scp/impl/flood_fill_end.py +++ b/spinnman/messages/scp/impl/flood_fill_end.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -20,13 +19,14 @@ from spinnman.messages.sdp import SDPFlag, SDPHeader from .check_ok_response import CheckOKResponse -_NNP_FORWARD_RETRY = (0x3f << 8) | 0x18 +_NNP_FORWARD_RETRY = (0x3f << 8) | 0x1A _NNP_FLOOD_FILL_END = 15 _WAIT_FLAG = 0x1 << 18 class FloodFillEnd(AbstractSCPRequest): - """ A request to start a flood fill of data + """ + A request to start a flood fill of data. """ __slots__ = [] @@ -34,18 +34,15 @@ def __init__( self, nearest_neighbour_id, app_id=0, processors=None, wait=False): """ - :param nearest_neighbour_id: The ID of the packet, between 0 and 127 - :type nearest_neighbour_id: int - :param app_id: The application ID to start using the data, between 16\ - and 255. If not specified, no application is started - :type app_id: int - :param processors: A list of processors on which to start the\ - application, each between 1 and 17. If not specified, no\ - application is started. - :type processors: iterable of int - :param wait: \ + :param int nearest_neighbour_id: + The ID of the packet, between 0 and 127 + :param int app_id: The application ID to start using the data, between + 16 and 255. If not specified, no application is started + :param list(int) processors: + A list of processors on which to start the application, each + between 1 and 17. If not specified, no application is started. + :param bool wait: True if the binary should go into a "wait" state before executing - :type wait: bool """ processor_mask = 0 if processors is not None: @@ -57,7 +54,7 @@ def __init__( if wait: data = data | _WAIT_FLAG - super(FloodFillEnd, self).__init__( + super().__init__( SDPHeader(flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=self.DEFAULT_DEST_X_COORD, diff --git a/spinnman/messages/scp/impl/flood_fill_start.py b/spinnman/messages/scp/impl/flood_fill_start.py index 2cc219de5..2c8bd809f 100644 --- a/spinnman/messages/scp/impl/flood_fill_start.py +++ b/spinnman/messages/scp/impl/flood_fill_start.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -20,28 +19,26 @@ from spinnman.messages.sdp import SDPFlag, SDPHeader from .check_ok_response import CheckOKResponse -_NNP_FORWARD_RETRY = (1 << 31) | (0x3f << 8) | 0x18 +_NNP_FORWARD_RETRY = (1 << 31) | (0x3f << 8) | 0x1A _NNP_FLOOD_FILL_START = 6 class FloodFillStart(AbstractSCPRequest): - """ A request to start a flood fill of data + """ + A request to start a flood fill of data. """ __slots__ = [] def __init__(self, nearest_neighbour_id, n_blocks, x=None, y=None): """ - :param nearest_neighbour_id: The ID of the packet, between 0 and 127 - :type nearest_neighbour_id: int - :param n_blocks: The number of blocks of data that will be sent,\ - between 0 and 255 - :type n_blocks: int - :param x: The x-coordinate of the chip to load the data on to. If not\ - specified, the data will be loaded on to all chips - :type x: int - :param y: The y-coordinate of the chip to load the data on to. If not\ - specified, the data will be loaded on to all chips - :type y: int + :param int nearest_neighbour_id: + The ID of the packet, between 0 and 127 + :param int n_blocks: + The number of blocks of data that will be sent, between 0 and 255 + :param int x: The x-coordinate of the chip to load the data on to. If + not specified, the data will be loaded on to all chips + :param int y: The y-coordinate of the chip to load the data on to. If + not specified, the data will be loaded on to all chips """ key = ((_NNP_FLOOD_FILL_START << 24) | (nearest_neighbour_id << 16) | (n_blocks << 8)) @@ -51,7 +48,7 @@ def __init__(self, nearest_neighbour_id, n_blocks, x=None, y=None): data = (((x & 0xfc) << 24) + ((y & 0xfc) << 16) + (3 << 16) + (1 << m)) - super(FloodFillStart, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, diff --git a/spinnman/messages/scp/impl/get_chip_info.py b/spinnman/messages/scp/impl/get_chip_info.py index ba074b120..2d554e931 100644 --- a/spinnman/messages/scp/impl/get_chip_info.py +++ b/spinnman/messages/scp/impl/get_chip_info.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2016 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -22,18 +21,19 @@ class GetChipInfo(AbstractSCPRequest): - """ An SCP request to read the chip information from a core + """ + An SCP request to read the chip information from a core. """ __slots__ = [] def __init__(self, x, y, with_size=False): """ - :param x: The x-coordinate of the chip to read from, between 0 and 255 - :type x: int - :param y: The y-coordinate of the chip to read from, between 0 and 255 - :type y: int - :param with_size: Whether the size should be included in the response - :type with_size: bool + :param int x: + The x-coordinate of the chip to read from, between 0 and 255 + :param int y: + The y-coordinate of the chip to read from, between 0 and 255 + :param bool with_size: + Whether the size should be included in the response """ # Bits 0-4 + bit 6 = all information except size argument_1 = 0x5F @@ -42,7 +42,7 @@ def __init__(self, x, y, with_size=False): # Bits 0-6 = all information including size argument_1 = 0x7F - super(GetChipInfo, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, diff --git a/spinnman/messages/scp/impl/get_chip_info_response.py b/spinnman/messages/scp/impl/get_chip_info_response.py index 48f76d74a..c34e913c4 100644 --- a/spinnman/messages/scp/impl/get_chip_info_response.py +++ b/spinnman/messages/scp/impl/get_chip_info_response.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp.abstract_messages import AbstractSCPResponse @@ -21,13 +20,14 @@ class GetChipInfoResponse(AbstractSCPResponse): - """ An SCP response to a request for the version of software running + """ + An SCP response to a request for the version of software running. """ __slots__ = [ "_chip_info"] def __init__(self): - super(GetChipInfoResponse, self).__init__() + super().__init__() self._chip_info = None @overrides(AbstractSCPResponse.read_data_bytestring) @@ -42,8 +42,9 @@ def read_data_bytestring(self, data, offset): @property def chip_info(self): - """ The chip information received + """ + The chip information received. - :rtype: :py:class:`spinnman.model.chip_summary_info.ChipSummaryInfo` + :rtype: ChipSummaryInfo """ return self._chip_info diff --git a/spinnman/messages/scp/impl/get_version.py b/spinnman/messages/scp/impl/get_version.py index 7df8d1461..b32d9a232 100644 --- a/spinnman/messages/scp/impl/get_version.py +++ b/spinnman/messages/scp/impl/get_version.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -22,25 +21,24 @@ class GetVersion(AbstractSCPRequest): - """ An SCP request to read the version of software running on a core + """ + An SCP request to read the version of software running on a core. """ __slots__ = [] def __init__(self, x, y, p): """ - - :param x: The x-coordinate of the chip to read from, between 0 and 255 - :type x: int - :param y: The y-coordinate of the chip to read from, between 0 and 255 - :type y: int - :param p: The ID of the processor to read the version from,\ + :param int x: + The x-coordinate of the chip to read from, between 0 and 255 + :param int y: + The y-coordinate of the chip to read from, between 0 and 255 + :param int p: The ID of the processor to read the version from, between 0 and 31 - :type p: int - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If the chip coordinates are out of range * If the processor is out of range """ - super(GetVersion, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=p, destination_chip_x=x, diff --git a/spinnman/messages/scp/impl/get_version_response.py b/spinnman/messages/scp/impl/get_version_response.py index 055f2fd85..ea2d26069 100644 --- a/spinnman/messages/scp/impl/get_version_response.py +++ b/spinnman/messages/scp/impl/get_version_response.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp.abstract_messages import AbstractSCPResponse @@ -21,13 +20,14 @@ class GetVersionResponse(AbstractSCPResponse): - """ An SCP response to a request for the version of software running + """ + An SCP response to a request for the version of software running. """ __slots__ = [ "_version_info"] def __init__(self): - super(GetVersionResponse, self).__init__() + super().__init__() self._version_info = None @overrides(AbstractSCPResponse.read_data_bytestring) @@ -40,8 +40,9 @@ def read_data_bytestring(self, data, offset): @property def version_info(self): - """ The version information received + """ + The version information received. - :rtype: :py:class:`spinnman.model.version_info.VersionInfo` + :rtype: VersionInfo """ return self._version_info diff --git a/spinnman/messages/scp/impl/iptag_clear.py b/spinnman/messages/scp/impl/iptag_clear.py index f442489f1..db7bf4706 100644 --- a/spinnman/messages/scp/impl/iptag_clear.py +++ b/spinnman/messages/scp/impl/iptag_clear.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -24,20 +23,18 @@ class IPTagClear(AbstractSCPRequest): - """ An SCP Request to clear an IP Tag + """ + An SCP Request to clear an IP Tag. """ __slots__ = [] def __init__(self, x, y, tag): """ - :param x: The x-coordinate of a chip, between 0 and 255 - :type x: int - :param y: The y-coordinate of a chip, between 0 and 255 - :type y: int - :param tag: The tag, between 0 and 7 - :type tag: int + :param int x: The x-coordinate of a chip, between 0 and 255 + :param int y: The y-coordinate of a chip, between 0 and 255 + :param int tag: The tag, between 0 and 7 """ - super(IPTagClear, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, diff --git a/spinnman/messages/scp/impl/iptag_get.py b/spinnman/messages/scp/impl/iptag_get.py index ab60072ac..5000e47ef 100644 --- a/spinnman/messages/scp/impl/iptag_get.py +++ b/spinnman/messages/scp/impl/iptag_get.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -30,22 +29,19 @@ class IPTagGet(AbstractSCPRequest): - """ An SCP Request to get an IP tag + """ + An SCP Request to get an IP tag. """ __slots__ = [] def __init__(self, x, y, tag): """ - :param x: The x-coordinate of a chip, between 0 and 255 - :type x: int - :param y: The y-coordinate of a chip, between 0 and 255 - :type y: int - :param tag: The tag to get details of, between 0 and 7 - :type tag: int - :param tag: The tag, between 0 and 7 - :type tag: int + :param int x: The x-coordinate of a chip, between 0 and 255 + :param int y: The y-coordinate of a chip, between 0 and 255 + :param int tag: The tag to get details of, between 0 and 7 + :param int tag: The tag, between 0 and 7 """ - super(IPTagGet, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, @@ -60,7 +56,8 @@ def get_scp_response(self): class _SCPIPTagGetResponse(AbstractSCPResponse): - """ An SCP response to a request for an IP tags + """ + An SCP response to a request for an IP tags. """ __slots__ = [ "_count", @@ -78,7 +75,7 @@ class _SCPIPTagGetResponse(AbstractSCPResponse): def __init__(self): """ """ - super(_SCPIPTagGetResponse, self).__init__() + super().__init__() self._ip_address = None self._mac_address = None self._port = None @@ -108,7 +105,8 @@ def read_data_bytestring(self, data, offset): @property def ip_address(self): - """ The IP address of the tag, as a bytearray of 4 bytes + """ + The IP address of the tag, as a bytearray of 4 bytes. :rtype: bytearray """ @@ -116,7 +114,8 @@ def ip_address(self): @property def mac_address(self): - """ The MAC address of the tag, as a bytearray of 6 bytes + """ + The MAC address of the tag, as a bytearray of 6 bytes. :rtype: bytearray """ @@ -124,7 +123,8 @@ def mac_address(self): @property def port(self): - """ The port of the tag + """ + The port of the tag. :rtype: int """ @@ -132,7 +132,8 @@ def port(self): @property def timeout(self): - """ The timeout of the tag + """ + The timeout of the tag. :rtype: int """ @@ -140,7 +141,8 @@ def timeout(self): @property def flags(self): - """ The flags of the tag + """ + The flags of the tag. :rtype: int """ @@ -148,7 +150,8 @@ def flags(self): @property def in_use(self): - """ True if the tag is marked as being in use + """ + Whether the tag is marked as being in use. :rtype: bool """ @@ -156,7 +159,8 @@ def in_use(self): @property def is_temporary(self): - """ True if the tag is temporary + """ + Whether the tag is temporary. :rtype: bool """ @@ -164,8 +168,12 @@ def is_temporary(self): @property def is_arp(self): - """ True if the tag is in the ARP state (where the MAC address is\ - being looked up - transient state so unlikely) + """ + Whether the tag is in the ARP state (where the MAC address is + being looked up). + + .. note:: + This is a transient state; it is unlikely to be observed. :rtype: bool """ @@ -173,7 +181,8 @@ def is_arp(self): @property def is_reverse(self): - """ True if the tag is a reverse tag + """ + Whether the tag is a reverse tag. :rtype: bool """ @@ -181,7 +190,8 @@ def is_reverse(self): @property def strip_sdp(self): - """ True if the tag is to strip the SDP header + """ + Whether the tag is to strip the SDP header. :rtype: bool """ @@ -189,7 +199,8 @@ def strip_sdp(self): @property def count(self): - """ The count of the number of packets that have been sent with the tag + """ + The count of the number of packets that have been sent with the tag. :rtype: int """ @@ -197,7 +208,8 @@ def count(self): @property def rx_port(self): - """ The receive port of the tag + """ + The receive port of the tag. :rtype: int """ @@ -205,7 +217,8 @@ def rx_port(self): @property def spin_chip_x(self): - """ The x-coordinate of the chip on which the tag is defined + """ + The X-coordinate of the chip on which the tag is defined. :rtype: int """ @@ -213,7 +226,8 @@ def spin_chip_x(self): @property def spin_chip_y(self): - """ The y-coordinate of the chip on which the tag is defined + """ + The Y-coordinate of the chip on which the tag is defined. :rtype: int """ @@ -221,7 +235,8 @@ def spin_chip_y(self): @property def spin_port(self): - """ The spin-port of the IP tag + """ + The spin-port of the IP tag. :rtype: int """ @@ -229,7 +244,8 @@ def spin_port(self): @property def spin_cpu(self): - """ The CPU ID of the IP tag + """ + The CPU ID of the IP tag. :rtype: int """ diff --git a/spinnman/messages/scp/impl/iptag_get_info.py b/spinnman/messages/scp/impl/iptag_get_info.py index dd078b953..2cd944395 100644 --- a/spinnman/messages/scp/impl/iptag_get_info.py +++ b/spinnman/messages/scp/impl/iptag_get_info.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -25,18 +24,17 @@ class IPTagGetInfo(AbstractSCPRequest): - """ An SCP Request information about IP tags + """ + An SCP Request information about IP tags. """ __slots__ = [] def __init__(self, x, y): """ - :param x: The x-coordinate of a chip, between 0 and 255 - :type x: int - :param y: The y-coordinate of a chip, between 0 and 255 - :type y: int + :param int x: The x-coordinate of a chip, between 0 and 255 + :param int y: The y-coordinate of a chip, between 0 and 255 """ - super(IPTagGetInfo, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, diff --git a/spinnman/messages/scp/impl/iptag_get_info_response.py b/spinnman/messages/scp/impl/iptag_get_info_response.py index 28c8d2b71..2a4d0e84d 100644 --- a/spinnman/messages/scp/impl/iptag_get_info_response.py +++ b/spinnman/messages/scp/impl/iptag_get_info_response.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinn_utilities.overrides import overrides @@ -23,7 +22,8 @@ class IPTagGetInfoResponse(AbstractSCPResponse): - """ An SCP response to a request for information about IP tags + """ + An SCP response to a request for information about IP tags. """ __slots__ = [ "_fixed_size", @@ -31,7 +31,7 @@ class IPTagGetInfoResponse(AbstractSCPResponse): "_tto"] def __init__(self): - super(IPTagGetInfoResponse, self).__init__() + super().__init__() self._tto = None self._pool_size = None self._fixed_size = None @@ -48,7 +48,8 @@ def read_data_bytestring(self, data, offset): @property def transient_timeout(self): - """ The timeout for transient IP tags (i.e. responses to SCP commands) + """ + The timeout for transient IP tags (i.e. responses to SCP commands). :rtype: int """ @@ -56,7 +57,8 @@ def transient_timeout(self): @property def pool_size(self): - """ The count of the IP tag pool size + """ + The count of the IP tag pool size. :rtype: int """ @@ -64,7 +66,8 @@ def pool_size(self): @property def fixed_size(self): - """ The count of the number of fixed IP tag entries + """ + The count of the number of fixed IP tag entries. :rtype: int """ diff --git a/spinnman/messages/scp/impl/iptag_set.py b/spinnman/messages/scp/impl/iptag_set.py index 8b2e90cac..77f17b01a 100644 --- a/spinnman/messages/scp/impl/iptag_set.py +++ b/spinnman/messages/scp/impl/iptag_set.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -24,31 +23,27 @@ class IPTagSet(AbstractSCPRequest): - """ An SCP Request to set an IP Tag + """ + An SCP Request to set an IP Tag. """ __slots__ = [] def __init__(self, x, y, host, port, tag, strip, use_sender=False): """ - :param x: The x-coordinate of a chip, between 0 and 255 - :type x: int - :param y: The y-coordinate of a chip, between 0 and 255 - :type y: int + :param int x: The x-coordinate of a chip, between 0 and 255 + :param int y: The y-coordinate of a chip, between 0 and 255 :param host: The host address, as an array of 4 bytes - :type host: bytearray - :param port: The port, between 0 and 65535 - :type port: int - :param tag: The tag, between 0 and 7 - :type tag: int - :param strip: if the SDP header should be striped from the packet. - :type strip: bool - :param use_sender: if the sender ip address and port should be used - :type: bool + :type host: bytearray or list(int) + :param int port: The port, between 0 and 65535 + :param int tag: The tag, between 0 and 7 + :param bool strip: if the SDP header should be striped from the packet + :param bool use_sender: + if the sender IP address and port should be used """ # pylint: disable=too-many-arguments strip_value = int(bool(strip)) sender_value = int(bool(use_sender)) - super(IPTagSet, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, diff --git a/spinnman/messages/scp/impl/iptag_set_tto.py b/spinnman/messages/scp/impl/iptag_set_tto.py index 82e7b1682..14674922f 100644 --- a/spinnman/messages/scp/impl/iptag_set_tto.py +++ b/spinnman/messages/scp/impl/iptag_set_tto.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -24,27 +23,23 @@ class IPTagSetTTO(AbstractSCPRequest): - """ An SCP request to set the transient timeout for future SCP requests + """ + An SCP request to set the transient timeout for future SCP requests. """ __slots__ = [] def __init__(self, x, y, tag_timeout): """ - - :param x: The x-coordinate of the chip to run on, between 0 and 255 - :type x: int - :param y: The y-coordinate of the chip to run on, between 0 and 255 - :type y: int - :param tag_timeout: The timeout value, via the\ - IPTAG_TIME_OUT_WAIT_TIMES enum located in spinnman.constants + :param int x: The x-coordinate of the chip to run on, between 0 and 255 + :param int y: The y-coordinate of the chip to run on, between 0 and 255 + :param IPTAG_TIME_OUT_WAIT_TIMES tag_timeout: The timeout value """ - - super(IPTagSetTTO, self).__init__( + super().__init__( SDPHeader(flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, destination_chip_y=y), SCPRequestHeader(command=SCPCommand.CMD_IPTAG), - argument_1=_IPTAG_TTO, argument_2=tag_timeout) + argument_1=_IPTAG_TTO, argument_2=tag_timeout.value) @overrides(AbstractSCPRequest.get_scp_response) def get_scp_response(self): diff --git a/spinnman/messages/scp/impl/read_fpga_register.py b/spinnman/messages/scp/impl/read_fpga_register.py index 4e2bfa9a6..85122845c 100644 --- a/spinnman/messages/scp/impl/read_fpga_register.py +++ b/spinnman/messages/scp/impl/read_fpga_register.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinn_utilities.overrides import overrides @@ -25,25 +24,26 @@ class ReadFPGARegister(BMPRequest): - """ Requests the data from a FPGA's register + """ + Requests the data from a FPGA's register. """ __slots__ = [] def __init__(self, fpga_num, register, board): - """ Sets up a read FPGA register request. + """ + Sets up a read FPGA register request. - :param fpga_num: FPGA number (0, 1 or 2) to communicate with. - :param register: Register address to read to (will be rounded down to\ + :param int fpga_num: FPGA number (0, 1 or 2) to communicate with. + :param int register: + Register address to read to (will be rounded down to the nearest 32-bit word boundary). - :param board: which board to request the FPGA register from - :rtype: None + :param int board: which board to request the FPGA register from """ - - # check to stop people asking for none word aligned memory addresses + # check to stop people asking for non-word aligned memory addresses # inverses all bits of a value, so is basically a inverse mask for the # value entered. arg1 = register & (~0x3) - super(ReadFPGARegister, self).__init__( + super().__init__( board, SCPRequestHeader(command=SCPCommand.CMD_LINK_READ), argument_1=arg1, argument_2=4, argument_3=fpga_num) @@ -54,13 +54,14 @@ def get_scp_response(self): class _SCPReadFPGARegisterResponse(BMPResponse): - """ An SCP response to a request for the version of software running + """ + An SCP response to a request for the version of software running. """ __slots__ = [ "_fpga_register"] def __init__(self): - super(_SCPReadFPGARegisterResponse, self).__init__() + super().__init__() self._fpga_register = None @overrides(AbstractSCPResponse.read_data_bytestring) @@ -74,7 +75,8 @@ def read_data_bytestring(self, data, offset): @property def fpga_register(self): - """ The register information received + """ + The register information received. :rtype: int """ diff --git a/spinnman/messages/scp/impl/read_link.py b/spinnman/messages/scp/impl/read_link.py index 1b193bc49..307671819 100644 --- a/spinnman/messages/scp/impl/read_link.py +++ b/spinnman/messages/scp/impl/read_link.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -23,28 +22,27 @@ class ReadLink(AbstractSCPRequest): - """ An SCP request to read a region of memory via a link on a chip + """ + An SCP request to read a region of memory via a link on a chip. """ __slots__ = [] def __init__(self, x, y, link, base_address, size, cpu=0): """ - :param x: The x-coordinate of the chip to read from, between 0 and 255 - :type x: int - :param y: The y-coordinate of the chip to read from, between 0 and 255 - :type y: int - :param cpu: The CPU core to use, normally 0 (or if a BMP, the board \ - slot number) - :type cpu: int - :param link: The ID of the link down which to send the query - :type link: int - :param base_address: The positive base address to start the read from - :type base_address: int - :param size: The number of bytes to read, between 1 and 256 - :type size: int + :param int x: + The x-coordinate of the chip to read from, between 0 and 255 + :param int y: + The y-coordinate of the chip to read from, between 0 and 255 + :param int link: The ID of the link down which to send the query + :param int base_address: + The positive base address to start the read from + :param int size: The number of bytes to read, between 1 and 256 + :param int cpu: + The CPU core to use, normally 0 + (or if a BMP, the board slot number) """ # pylint: disable=too-many-arguments - super(ReadLink, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=cpu, destination_chip_x=x, @@ -58,8 +56,9 @@ def get_scp_response(self): class _SCPReadLinkResponse(AbstractSCPResponse): - """ An SCP response to a request to read a region of memory via a link on\ - a chip + """ + An SCP response to a request to read a region of memory via a link on + a chip. """ __slots__ = [ "_data", @@ -67,7 +66,7 @@ class _SCPReadLinkResponse(AbstractSCPResponse): "_offset"] def __init__(self): - super(_SCPReadLinkResponse, self).__init__() + super().__init__() self._data = None self._offset = None self._length = None @@ -84,15 +83,17 @@ def read_data_bytestring(self, data, offset): @property def data(self): - """ The data read + """ + The data read. - :rtype: bytearray + :rtype: bytes """ return self._data @property def offset(self): - """ The offset where the valid data starts + """ + The offset where the valid data starts. :rtype: int """ @@ -100,7 +101,8 @@ def offset(self): @property def length(self): - """ The length of the valid data + """ + The length of the valid data. :rtype: int """ diff --git a/spinnman/messages/scp/impl/read_memory.py b/spinnman/messages/scp/impl/read_memory.py index a498640cc..9647c4a77 100644 --- a/spinnman/messages/scp/impl/read_memory.py +++ b/spinnman/messages/scp/impl/read_memory.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -24,27 +23,27 @@ class ReadMemory(AbstractSCPRequest): - """ An SCP request to read a region of memory on a chip + """ + An SCP request to read a region of memory on a chip. """ __slots__ = [] def __init__(self, x, y, base_address, size, cpu=0): """ - :param x: The x-coordinate of the chip to read from, between 0 and 255 - :type x: int - :param y: The y-coordinate of the chip to read from, between 0 and 255 - :type y: int - :param base_address: The positive base address to start the read from - :type base_address: int - :param size: The number of bytes to read, between 1 and 256 - :type size: int - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :param int x: + The x-coordinate of the chip to read from, between 0 and 255 + :param int y: + The y-coordinate of the chip to read from, between 0 and 255 + :param int base_address: + The positive base address to start the read from + :param int size: The number of bytes to read, between 1 and 256 + :raise SpinnmanInvalidParameterException: * If the chip coordinates are out of range * If the base address is not a positive number * If the size is out of range """ # pylint: disable=too-many-arguments - super(ReadMemory, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=cpu, destination_chip_x=x, @@ -60,7 +59,8 @@ def get_scp_response(self): class _SCPReadMemoryResponse(AbstractSCPResponse): - """ An SCP response to a request to read a region of memory on a chip + """ + An SCP response to a request to read a region of memory on a chip. """ __slots__ = [ "_data", @@ -68,7 +68,7 @@ class _SCPReadMemoryResponse(AbstractSCPResponse): "_offset"] def __init__(self): - super(_SCPReadMemoryResponse, self).__init__() + super().__init__() self._data = None self._length = None self._offset = None @@ -84,7 +84,11 @@ def read_data_bytestring(self, data, offset): @property def data(self): - """ The data read - note that the data starts at offset + """ + The data read. + + .. note:: + The data starts at offset. :rtype: bytearray """ @@ -92,7 +96,8 @@ def data(self): @property def offset(self): - """ The offset where the valid data starts + """ + The offset where the valid data starts. :rtype: int """ @@ -100,7 +105,8 @@ def offset(self): @property def length(self): - """ The length of the valid data + """ + The length of the valid data. :rtype: int """ diff --git a/spinnman/messages/scp/impl/reverse_iptag_set.py b/spinnman/messages/scp/impl/reverse_iptag_set.py index 9fc7f811c..1d07e0d51 100644 --- a/spinnman/messages/scp/impl/reverse_iptag_set.py +++ b/spinnman/messages/scp/impl/reverse_iptag_set.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -24,30 +23,24 @@ class ReverseIPTagSet(AbstractSCPRequest): - """ An SCP Request to set an IP Tag + """ + An SCP Request to set an IP Tag. """ __slots__ = [] def __init__(self, x, y, destination_x, destination_y, destination_p, port, tag, sdp_port): """ - :param x: The x-coordinate of a chip, between 0 and 255 - :type x: int - :param y: The y-coordinate of a chip, between 0 and 255 - :type y: int - :param destination_x: \ + :param int x: The x-coordinate of a chip, between 0 and 255 + :param int y: The y-coordinate of a chip, between 0 and 255 + :param int destination_x: The x-coordinate of the destination chip, between 0 and 255 - :type destination_x: int - :param destination_y: \ + :param int destination_y: The y-coordinate of the destination chip, between 0 and 255 - :type destination_y: int - :param destination_p: \ + :param int destination_p: The ID of the destination processor, between 0 and 17 - :type destination_p: int - :param port: The port, between 0 and 65535 - :type port: int - :param tag: The tag, between 0 and 7 - :type tag: int + :param int port: The port, between 0 and 65535 + :param int tag: The tag, between 0 and 7 """ # pylint: disable=too-many-arguments strip_value = 1 @@ -58,7 +51,7 @@ def __init__(self, x, y, destination_x, destination_y, destination_p, port, tag) arg2 = ((destination_x << 24) | (destination_y << 16) | port) - super(ReverseIPTagSet, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, diff --git a/spinnman/messages/scp/impl/router_alloc.py b/spinnman/messages/scp/impl/router_alloc.py index 6076bdca3..8c0145c71 100644 --- a/spinnman/messages/scp/impl/router_alloc.py +++ b/spinnman/messages/scp/impl/router_alloc.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinn_utilities.overrides import overrides @@ -27,24 +26,22 @@ class RouterAlloc(AbstractSCPRequest): - """ An SCP Request to allocate space for routing entries + """ + An SCP Request to allocate space for routing entries. """ __slots__ = [] def __init__(self, x, y, app_id, n_entries): """ - :param x: \ + :param int x: The x-coordinate of the chip to allocate on, between 0 and 255 - :type x: int - :param y: \ + :param int y: The y-coordinate of the chip to allocate on, between 0 and 255 - :type y: int - :param app_id: The ID of the application, between 0 and 255 - :type app_id: int - :param n_entries: The number of entries to allocate - :type n_entries: int + :param int app_id: The ID of the application, between 0 and 255 + :param int n_entries: The number of entries to allocate """ - super(RouterAlloc, self).__init__( + # pylint: disable=unsupported-binary-operation + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, @@ -61,7 +58,8 @@ def get_scp_response(self): class _SCPRouterAllocResponse(AbstractSCPResponse): - """ An SCP response to a request to allocate router entries + """ + An SCP response to a request to allocate router entries. """ __slots__ = [ "_base_address"] @@ -69,7 +67,7 @@ class _SCPRouterAllocResponse(AbstractSCPResponse): def __init__(self): """ """ - super(_SCPRouterAllocResponse, self).__init__() + super().__init__() self._base_address = None @overrides(AbstractSCPResponse.read_data_bytestring) @@ -82,7 +80,8 @@ def read_data_bytestring(self, data, offset): @property def base_address(self): - """ The base address allocated, or 0 if none + """ + The base address allocated, or 0 if none. :rtype: int """ diff --git a/spinnman/messages/scp/impl/router_clear.py b/spinnman/messages/scp/impl/router_clear.py index 2bc046e6e..9a42279d2 100644 --- a/spinnman/messages/scp/impl/router_clear.py +++ b/spinnman/messages/scp/impl/router_clear.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -22,23 +21,20 @@ class RouterClear(AbstractSCPRequest): - """ A request to clear the router on a chip + """ + A request to clear the router on a chip. """ __slots__ = [] def __init__(self, x, y): """ - :param x: The x-coordinate of the chip, between 0 and 255; \ - this is not checked due to speed restrictions - :type x: int - :param y: The y-coordinate of the chip, between 0 and 255; \ - this is not checked due to speed restrictions - :type y: int - :raise spinnman.exceptions.SpinnmanInvalidParameterException:\ + :param int x: The x-coordinate of the chip, between 0 and 255 + :param int y: The y-coordinate of the chip, between 0 and 255 + :raise SpinnmanInvalidParameterException: * If x is out of range * If y is out of range """ - super(RouterClear, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, diff --git a/spinnman/messages/scp/impl/router_init.py b/spinnman/messages/scp/impl/router_init.py index b6a91acf1..1806c995d 100644 --- a/spinnman/messages/scp/impl/router_init.py +++ b/spinnman/messages/scp/impl/router_init.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.exceptions import SpinnmanInvalidParameterException @@ -23,28 +22,22 @@ class RouterInit(AbstractSCPRequest): - """ A request to initialize the router on a chip + """ + A request to initialize the router on a chip. """ __slots__ = [] def __init__(self, x, y, n_entries, table_address, base_address, app_id): """ - :param x: The x-coordinate of the chip, between 0 and 255,\ - this is not checked due to speed restrictions - :type x: int - :param y: The y-coordinate of the chip, between 0 and 255,\ - this is not checked due to speed restrictions - :type y: int - :param n_entries: The number of entries in the table, more than 0 - :type n_entries: int - :param table_address: The allocated table address - :type table_address: int - :param base_address: The base_address containing the entries - :type base_address: int - :param app_id: The ID of the application with which to associate the\ + :param int x: The x-coordinate of the chip, between 0 and 255 + :param int y: The y-coordinate of the chip, between 0 and 255 + :param int n_entries: The number of entries in the table, more than 0 + :param int table_address: The allocated table address + :param int base_address: The base_address containing the entries + :param int app_id: + The ID of the application with which to associate the routes. If not specified, defaults to 0. - :type app_id: int - :raise spinnman.exceptions.SpinnmanInvalidParameterException:\ + :raise SpinnmanInvalidParameterException: * If x is out of range * If y is out of range * If n_entries is 0 or less @@ -64,7 +57,7 @@ def __init__(self, x, y, n_entries, table_address, base_address, app_id): "table_address", str(table_address), "Must be a positive integer") - super(RouterInit, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, diff --git a/spinnman/messages/scp/impl/sdram_alloc.py b/spinnman/messages/scp/impl/sdram_alloc.py index 9448326f7..b758b9a7c 100644 --- a/spinnman/messages/scp/impl/sdram_alloc.py +++ b/spinnman/messages/scp/impl/sdram_alloc.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinn_utilities.overrides import overrides @@ -25,46 +24,54 @@ _ONE_WORD = struct.Struct(". +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinn_utilities.overrides import overrides @@ -26,28 +25,26 @@ class SDRAMDeAlloc(AbstractSCPRequest): - """ An SCP Request to free space in the SDRAM + """ + An SCP Request to free space in the SDRAM. """ __slots__ = [ "_read_n_blocks_freed"] def __init__(self, x, y, app_id, base_address=None): """ - :param x: \ + :param int x: The x-coordinate of the chip to allocate on, between 0 and 255 - :type x: int - :param y: \ + :param int y: The y-coordinate of the chip to allocate on, between 0 and 255 - :type y: int - :param app_id: The ID of the application, between 0 and 255 - :type app_id: int - :param base_address: The start address in SDRAM to which the block\ - needs to be deallocated, or none if deallocating via app_id + :param int app_id: The ID of the application, between 0 and 255 + :param base_address: The start address in SDRAM to which the block + needs to be deallocated, or `None` if deallocating via app_id :type base_address: int or None """ - + # pylint: disable=unsupported-binary-operation if base_address is not None: - super(SDRAMDeAlloc, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, @@ -59,7 +56,7 @@ def __init__(self, x, y, app_id, base_address=None): argument_2=base_address) self._read_n_blocks_freed = False else: - super(SDRAMDeAlloc, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, destination_chip_x=x, @@ -77,7 +74,8 @@ def get_scp_response(self): class _SCPSDRAMDeAllocResponse(AbstractSCPResponse): - """ An SCP response to a request to deallocate SDRAM + """ + An SCP response to a request to deallocate SDRAM. """ __slots__ = [ "_number_of_blocks_freed", @@ -86,7 +84,7 @@ class _SCPSDRAMDeAllocResponse(AbstractSCPResponse): def __init__(self, read_n_blocks_freed=False): """ """ - super(_SCPSDRAMDeAllocResponse, self).__init__() + super().__init__() self._number_of_blocks_freed = None self._read_n_blocks_freed = read_n_blocks_freed @@ -109,8 +107,9 @@ def read_data_bytestring(self, data, offset): @property def number_of_blocks_freed(self): - """ The number of allocated blocks that have been freed from the\ - app_id given + """ + The number of allocated blocks that have been freed from the + app_id given. :rtype: int """ diff --git a/spinnman/messages/scp/impl/send_signal.py b/spinnman/messages/scp/impl/send_signal.py index ecb897e7a..32c0c78f7 100644 --- a/spinnman/messages/scp/impl/send_signal.py +++ b/spinnman/messages/scp/impl/send_signal.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.exceptions import SpinnmanInvalidParameterException @@ -32,25 +31,23 @@ def _get_data(app_id, signal): class SendSignal(AbstractSCPRequest): - """ An SCP Request to send a signal to cores + """ + An SCP Request to send a signal to cores. """ __slots__ = [] def __init__(self, app_id, signal): """ - :param app_id: The ID of the application, between 0 and 255 - :type app_id: int - :param signal: The signal to send - :type signal: :py:class:`spinnman.messages.scp.scp_signal.SCPSignal` - :raise spinnman.exceptions.SpinnmanInvalidParameterException: If\ - app_id is out of range + :param int app_id: The ID of the application, between 0 and 255 + :param Signal signal: The signal to send + :raise SpinnmanInvalidParameterException: If app_id is out of range """ if app_id < 0 or app_id > 255: raise SpinnmanInvalidParameterException( "app_id", str(app_id), "Must be between 0 and 255") - super(SendSignal, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=0, diff --git a/spinnman/messages/scp/impl/set_led.py b/spinnman/messages/scp/impl/set_led.py deleted file mode 100644 index 503180b13..000000000 --- a/spinnman/messages/scp/impl/set_led.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from spinn_utilities.overrides import overrides -from spinnman.messages.scp import SCPRequestHeader -from spinnman.messages.scp.abstract_messages import AbstractSCPRequest -from spinnman.messages.scp.enums import SCPCommand -from spinnman.messages.sdp import SDPFlag, SDPHeader -from .check_ok_response import CheckOKResponse - - -class SetLED(AbstractSCPRequest): - """ A request to change the state of an SetLED - """ - __slots__ = [] - - def __init__(self, x, y, cpu, led_states): - """ - :param x: The x-coordinate of the chip, between 0 and 255 - :type x: int - :param y: The y-coordinate of the chip, between 0 and 255 - :type y: int - :param cpu: The CPU-number to use to set the SetLED. - :type cpu: int - :param led_states: \ - A dictionary mapping SetLED index to state with\ - 0 being off, 1 on and 2 inverted. - :type led_states: dict - """ - encoded_led_states = 0 - for led, state in led_states.items(): - encoded_led_states |= {0: 2, 1: 3, 2: 1}[state] << (2 * led) - - super(SetLED, self).__init__( - SDPHeader( - flags=SDPFlag.REPLY_EXPECTED, destination_port=0, - destination_cpu=cpu, destination_chip_x=x, - destination_chip_y=y), - SCPRequestHeader(command=SCPCommand.CMD_LED), - argument_1=encoded_led_states) - - @overrides(AbstractSCPRequest.get_scp_response) - def get_scp_response(self): - return CheckOKResponse("Set SetLED", "CMD_LED") diff --git a/spinnman/messages/scp/impl/set_power.py b/spinnman/messages/scp/impl/set_power.py index 48e4706ab..19e2fb32b 100644 --- a/spinnman/messages/scp/impl/set_power.py +++ b/spinnman/messages/scp/impl/set_power.py @@ -1,19 +1,19 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import logging +from spinn_utilities.log import FormatAdapter from spinn_utilities.overrides import overrides from spinnman.messages.scp.abstract_messages import ( AbstractSCPRequest, BMPRequest) @@ -21,33 +21,34 @@ from spinnman.messages.scp import SCPRequestHeader from .check_ok_response import CheckOKResponse -logger = logging.getLogger(__name__) +logger = FormatAdapter(logging.getLogger(__name__)) class SetPower(BMPRequest): - """ An SCP request for the BMP to power on or power off a rack of boards + """ + An SCP request for the BMP to power on or power off a rack of boards. """ __slots__ = [] def __init__(self, power_command, boards, delay=0.0, board_to_send_to=0): """ .. note:: - There is currently a bug in the BMP that means some boards don't\ - respond to power commands not sent to BMP 0. Thus changing the\ + There is currently a bug in the BMP that means some boards don't + respond to power commands not sent to BMP 0. Thus changing the board_to_send_to parameter is not recommended! - :param power_command: The power command being sent - :type power_command:\ - :py:class:`spinnman.messages.scp.scp_power_command.SCPPowerCommand` + :param PowerCommand power_command: The power command being sent :param boards: The boards on the same backplane to power on or off - :type boards: int or iterable of int - :param delay: Number of seconds delay between power state changes of\ + :type boards: int or list(int) + :param float delay: + Number of seconds delay between power state changes of the different boards. - :type delay: int - :param board_to_send_to: The optional board to send the command to if\ - this is to be sent to a frame of boards. - :type: board_to_send_to: 0 - :rtype: None + :param int board_to_send_to: + The optional board to send the command to if this is to be sent + to a frame of boards. + + .. note:: + Leave this at the default because of hardware bugs. """ if board_to_send_to != 0: @@ -59,13 +60,11 @@ def __init__(self, power_command, boards, delay=0.0, board_to_send_to=0): arg1 = (int(delay * 1000) << 16) | power_command.value arg2 = self.get_board_mask(boards) - super(SetPower, self).__init__( + super().__init__( board_to_send_to, SCPRequestHeader(command=SCPCommand.CMD_BMP_POWER), argument_1=arg1, argument_2=arg2) @overrides(AbstractSCPRequest.get_scp_response) def get_scp_response(self): - """ Get the response from the powering message - """ return CheckOKResponse("powering request", "CMD_BMP_POWER") diff --git a/spinnman/messages/scp/impl/write_fpga_register.py b/spinnman/messages/scp/impl/write_fpga_register.py index f636ee71b..e5123829c 100644 --- a/spinnman/messages/scp/impl/write_fpga_register.py +++ b/spinnman/messages/scp/impl/write_fpga_register.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinn_utilities.overrides import overrides @@ -25,30 +24,26 @@ class WriteFPGARegister(BMPRequest): - """ A request for writing data to a FPGA register """ - __slots__ = [] - - def __init__(self, fpga_num, addr, value, board): - """ Write the value of an FPGA (SPI) register. + A request for writing a word to a FPGA (SPI) register. - See the SpI/O project's spinnaker_fpga design's `README`_ for a listing - of FPGA registers. The SpI/O project can be found on GitHub at: - https://github.com/SpiNNakerManchester/spio/ + See the SpI/O project's spinnaker_fpga design's `README`_ for a listing + of FPGA registers. The SpI/O project can be found on GitHub at: + https://github.com/SpiNNakerManchester/spio/ - .. _README: https://github.com/SpiNNakerManchester/spio/\ - blob/master/designs/spinnaker_fpgas/README.md#spi-interface + .. _README: https://github.com/SpiNNakerManchester/spio/\ + blob/master/designs/spinnaker_fpgas/README.md#spi-interface + """ + __slots__ = [] - :param fpga_num: FPGA number (0, 1 or 2) to communicate with. - :type fpga_num: int - :param addr: Register address to read or write to (will be rounded + def __init__(self, fpga_num, addr, value, board): + """ + :param int fpga_num: FPGA number (0, 1 or 2) to communicate with. + :param int addr: Register address to read or write to (will be rounded down to the nearest 32-bit word boundary). - :type addr: int - :param value: A 32-bit int value to write to the register - :type value: int + :param int value: A 32-bit int value to write to the register """ - - super(WriteFPGARegister, self).__init__( + super().__init__( board, SCPRequestHeader(command=SCPCommand.CMD_LINK_WRITE), argument_1=addr & (~0x3), argument_2=4, argument_3=fpga_num, diff --git a/spinnman/messages/scp/impl/write_link.py b/spinnman/messages/scp/impl/write_link.py index d5bddd526..9b416cc58 100644 --- a/spinnman/messages/scp/impl/write_link.py +++ b/spinnman/messages/scp/impl/write_link.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.messages.scp import SCPRequestHeader @@ -22,32 +21,27 @@ class WriteLink(AbstractSCPRequest): - """ A request to write memory on a neighbouring chip + """ + A request to write memory on a neighbouring chip. """ __slots__ = [ "_data_to_write"] def __init__(self, x, y, link, base_address, data, cpu=0): """ - :param x: The x-coordinate of the chip whose neighbour will be written\ - to, between 0 and 255 - :type x: int - :param y: The y-coordinate of the chip whose neighbour will be written\ - to, between 0 and 255 - :type y: int - :param cpu: The CPU core to use, normally 0 (or if a BMP, the board \ - slot number) - :type cpu: int - :param link: The link number to write to between 0 and 5 (or if a BMP,\ - the FPGA between 0 and 2) - :type link: int - :param base_address: The base_address to start writing to - :type base_address: int - :param data: Up to 256 bytes of data to write - :type data: bytearray + :param int x: The x-coordinate of the chip whose neighbour will be + written to, between 0 and 255 + :param int y: The y-coordinate of the chip whose neighbour will be + written to, between 0 and 255 + :param int cpu: The CPU core to use, normally 0 + (or if a BMP, the board slot number) + :param int link: The link number to write to between 0 and 5 + (or if a BMP, the FPGA between 0 and 2) + :param int base_address: The base_address to start writing to + :param bytes data: Up to 256 bytes of data to write """ # pylint: disable=too-many-arguments - super(WriteLink, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=cpu, destination_chip_x=x, @@ -59,8 +53,7 @@ def __init__(self, x, y, link, base_address, data, cpu=0): @property def bytestring(self): - datastring = super(WriteLink, self).bytestring - return datastring + bytes(self._data_to_write) + return super().bytestring + bytes(self._data_to_write) @overrides(AbstractSCPRequest.get_scp_response) def get_scp_response(self): diff --git a/spinnman/messages/scp/impl/write_memory.py b/spinnman/messages/scp/impl/write_memory.py index ab7872a17..2d394681d 100644 --- a/spinnman/messages/scp/impl/write_memory.py +++ b/spinnman/messages/scp/impl/write_memory.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from spinnman.constants import address_length_dtype @@ -23,29 +22,27 @@ class WriteMemory(AbstractSCPRequest): - """ A request to write memory on a chip + """ + A request to write memory on a chip. """ __slots__ = [ "_data_to_write"] def __init__(self, x, y, base_address, data, cpu=0): """ - :param x: The x-coordinate of the chip, between 0 and 255;\ + :param int x: The x-coordinate of the chip, between 0 and 255; this is not checked due to speed restrictions - :type x: int - :param y: The y-coordinate of the chip, between 0 and 255;\ + :param int y: The y-coordinate of the chip, between 0 and 255; this is not checked due to speed restrictions - :type y: int - :param base_address: The base_address to start writing to \ + :param int base_address: The base_address to start writing to the base address is not checked to see if its not valid - :type base_address: int - :param data: between 1 and 256 bytes of data to write;\ + :param data: between 1 and 256 bytes of data to write; this is not checked due to speed restrictions - :type data: bytearray or string + :type data: bytearray or bytes """ # pylint: disable=too-many-arguments size = len(data) - super(WriteMemory, self).__init__( + super().__init__( SDPHeader( flags=SDPFlag.REPLY_EXPECTED, destination_port=0, destination_cpu=cpu, destination_chip_x=x, @@ -59,8 +56,7 @@ def __init__(self, x, y, base_address, data, cpu=0): @property def bytestring(self): - datastring = super(WriteMemory, self).bytestring - return datastring + bytes(self._data_to_write) + return super().bytestring + bytes(self._data_to_write) @overrides(AbstractSCPRequest.get_scp_response) def get_scp_response(self): diff --git a/spinnman/messages/scp/scp_request_header.py b/spinnman/messages/scp/scp_request_header.py index 3a6e6cf1c..043bf16ad 100644 --- a/spinnman/messages/scp/scp_request_header.py +++ b/spinnman/messages/scp/scp_request_header.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct @@ -19,10 +18,11 @@ class SCPRequestHeader(object): - """ Represents the header of an SCP Request - Each optional parameter in the constructor can be set to a value other\ - than None once, after which it is immutable. It is an error to set a\ - parameter that is not currently None. + """ + Represents the header of an SCP Request + Each optional parameter in the constructor can be set to a value other + than `None` once, after which it is immutable. It is an error to set a + parameter that is not currently `None`. """ __slots__ = [ "_command", @@ -30,13 +30,11 @@ class SCPRequestHeader(object): def __init__(self, command, sequence=0): """ - - :param command: The SCP command - :type command: :py:class:`spinnman.messages.scp.scp_command.SCPCommand` - :param sequence: The number of the SCP packet in order of all packets\ + :param SCPCommand command: The SCP command + :param int sequence: + The number of the SCP packet in order of all packets sent or received, between 0 and 65535 - :type sequence: int - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + :raise SpinnmanInvalidParameterException: If one of the parameters is incorrect """ self._command = command @@ -44,40 +42,38 @@ def __init__(self, command, sequence=0): @property def command(self): - """ The command of the SCP packet + """ + The command of the SCP packet. - :return: The command - :rtype: :py:class:`spinnman.messages.scp.scp_command.SCPCommand` + :rtype: SCPCommand """ return self._command @property def sequence(self): - """ The sequence number of the SCP packet + """ + The sequence number of the SCP packet, between 0 and 65535. - :return: The sequence number of the packet, between 0 and 65535 :rtype: int """ return self._sequence @sequence.setter def sequence(self, sequence): - """ Set the sequence number of the SCP packet + """ + Set the sequence number of the SCP packet. - :param sequence: The sequence number to set, between 0 and 65535 - :type sequence: int - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + :param int sequence: The sequence number to set, between 0 and 65535 + :raise SpinnmanInvalidParameterException: If the sequence is out of range, or if it has already been set """ self._sequence = sequence @property def bytestring(self): - """ The header as a bytestring + """ + The header as a byte-string. - :return: The header as a bytestring - :rtype: str + :rtype: bytes """ return _TWO_SHORTS.pack(self._command.value, self._sequence) diff --git a/spinnman/messages/scp/scp_response_header.py b/spinnman/messages/scp/scp_response_header.py index 367eebd6c..47a9cf126 100644 --- a/spinnman/messages/scp/scp_response_header.py +++ b/spinnman/messages/scp/scp_response_header.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinnman.messages.scp.enums import SCPResult @@ -20,7 +19,8 @@ class SCPResponseHeader(object): - """ Represents the header of an SCP Response + """ + Represents the header of an SCP Response. """ __slots__ = [ "_result", @@ -28,35 +28,37 @@ class SCPResponseHeader(object): def __init__(self, result=None, sequence=None): """ + :param SCPResult result: + :param int sequence: """ self._result = result self._sequence = sequence @property def result(self): - """ The result of the SCP response + """ + The result of the SCP response. - :return: The result - :rtype: :py:class:`spinnman.messages.scp.scp_result.SCPResult` + :rtype: SCPResult """ return self._result @property def sequence(self): - """ The sequence number of the SCP response + """ + The sequence number of the SCP response, between 0 and 65535. - :return: The sequence number of the packet, between 0 and 65535 :rtype: int """ return self._sequence @staticmethod def from_bytestring(data, offset): - """ Read a header from a bytestring + """ + Read a header from a byte-string. - :param data: The bytestring to read from - :type data: str - :param offset: + :param bytes data: The byte-string to read from + :param int offset: """ result, sequence = _TWO_SHORTS.unpack_from(data, offset) return SCPResponseHeader(SCPResult(result), sequence) diff --git a/spinnman/messages/sdp/__init__.py b/spinnman/messages/sdp/__init__.py index 5415f2dc6..e148fd164 100644 --- a/spinnman/messages/sdp/__init__.py +++ b/spinnman/messages/sdp/__init__.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .sdp_flag import SDPFlag from .sdp_header import SDPHeader diff --git a/spinnman/messages/sdp/sdp_flag.py b/spinnman/messages/sdp/sdp_flag.py index a1f62cb9c..bf1b3956d 100644 --- a/spinnman/messages/sdp/sdp_flag.py +++ b/spinnman/messages/sdp/sdp_flag.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class SDPFlag(Enum): - """ SDPFlag for the message + """ + SDPFlag for the message. """ REPLY_NOT_EXPECTED = (0x07, "Indicates that a reply is not expected") REPLY_EXPECTED = (0x87, "Indicates that a reply is expected") diff --git a/spinnman/messages/sdp/sdp_header.py b/spinnman/messages/sdp/sdp_header.py index e01b88a89..6b0a27ec1 100644 --- a/spinnman/messages/sdp/sdp_header.py +++ b/spinnman/messages/sdp/sdp_header.py @@ -1,30 +1,31 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from .sdp_flag import SDPFlag +from spinnman.data import SpiNNManDataView N_BYTES = 8 _EIGHT_BYTES = struct.Struct("<8B") class SDPHeader(object): - """ Represents the header of an SDP message. - Each optional parameter in the constructor can be set to a value other\ - than None once, after which it is immutable. It is an error to set a\ - parameter that is not currently None. + """ + Represents the header of an SDP message. + Each optional parameter in the constructor can be set to a value other + than `None` once, after which it is immutable. It is an error to set a + parameter that is not currently `None`. """ __slots__ = [ "_destination_chip_x", @@ -44,34 +45,30 @@ def __init__(self, flags=None, tag=None, source_port=None, source_cpu=None, source_chip_x=None, source_chip_y=None): """ - :param flags: Any flags for the packet - :type flags: :py:class:`spinnman.messages.sdp.sdp_flag.SDPFlag` - :param tag: The IP tag of the packet between 0 and 255, or None if it\ + :param SDPFlag flags: Any flags for the packet + :param int tag: + The IP tag of the packet between 0 and 255, or `None` if it is to be set later - :type tag: int - :param destination_port: \ + :param int destination_port: The destination port of the packet between 0 and 7 - :type destination_port: int - :param destination_cpu: \ + :param int destination_cpu: The destination processor ID within the chip between 0 and 31 - :type destination_cpu: int - :param destination_chip_x: \ + :param int destination_chip_x: The x-coordinate of the destination chip between 0 and 255 - :type destination_chip_x: int - :param destination_chip_y: \ + :param int destination_chip_y: The y-coordinate of the destination chip between 0 and 255 - :type destination_chip_y: int - :param source_port: The source port of the packet between 0 and 7, or\ - None if it is to be set later - :type source_port: int - :param source_cpu: The source processor ID within the chip between 0\ - and 31, or None if it is to be set later - :type source_cpu: int - :param source_chip_x: The x-coordinate of the source chip between 0\ - and 255, or None if it is to be set later - :type source_chip_x: int - :param source_chip_y: The y-coordinate of the source chip between 0\ - and 255, or None if it is to be set later + :param int source_port: + The source port of the packet between 0 and 7, or + `None` if it is to be set later + :param int source_cpu: + The source processor ID within the chip between 0 and 31, + or `None` if it is to be set later + :param int source_chip_x: + The x-coordinate of the source chip between 0 and 255, + or `None` if it is to be set later + :param int source_chip_y: + The y-coordinate of the source chip between 0 and 255, + or `None` if it is to be set later """ # pylint: disable=too-many-arguments self._flags = flags @@ -87,190 +84,200 @@ def __init__(self, flags=None, tag=None, @property def flags(self): - """ The flags of the packet + """ + The flags of the packet (settable). - :return: The flags of the packet - :rtype: :py:class:`spinnman.messages.sdp.sdp_flag.SDPFlag` + :rtype: SDPFlag """ return self._flags @flags.setter def flags(self, flags): - """ Set the flags of the packet + """ + Set the flags of the packet. - :param flags: The flags to set - :type flags: :py:class:`spinnman.messages.sdp.sdp_flag.SDPFlag` + :param SDPFlag flags: The flags to set """ self._flags = flags @property def tag(self): - """ The tag of the packet + """ + The tag of the packet, between 0 and 255 (settable). - :return: The tag of the packet between 0 and 255 :rtype: int """ return self._tag @tag.setter def tag(self, tag): - """ Set the tag of the packet + """ + Set the tag of the packet. - :param tag: The tag to set, between 0 and 255 - :type tag: int + :param int tag: The tag to set, between 0 and 255 """ self._tag = tag @property def destination_port(self): - """ The destination port of the packet + """ + The destination SDP port of the packet, between 0 and 7 (settable). - :return: The destination port of the packet between 0 and 7 :rtype: int """ return self._destination_port @destination_port.setter def destination_port(self, destination_port): - """ Set the destination port of the packet + """ + Set the destination port of the packet. - :param destination_port: The destination port to set, between 0 and 7 - :type destination_port: int + :param int destination_port: + The destination port to set, between 0 and 7 """ self._destination_port = destination_port @property def destination_cpu(self): - """ The core on the destination chip + """ + The core on the destination chip, between 0 and 31 (settable). - :return: The core on the destination chip, between 0 and 31 :rtype: int """ return self._destination_cpu @destination_cpu.setter def destination_cpu(self, destination_cpu): - """ Set the ID of the destination processor of the packet + """ + Set the ID of the destination processor of the packet. - :param destination_cpu: The processor ID to set, between 0 and 31 - :type destination_cpu: int + :param int destination_cpu: + The processor ID to set, between 0 and 31 """ self._destination_cpu = destination_cpu @property def destination_chip_x(self): - """ The x-coordinate of the destination chip of the packet + """ + The x-coordinate of the destination chip of the packet, between + 0 and 255 (settable). - :return: The x-coordinate of the chip, between 0 and 255 :rtype: int """ return self._destination_chip_x @destination_chip_x.setter def destination_chip_x(self, destination_chip_x): - """ Set the x-coordinate of the destination chip of the packet + """ + Set the x-coordinate of the destination chip of the packet. - :param destination_chip_x: The x-coordinate to set, between 0 and 255 - :type destination_chip_x: int + :param int destination_chip_x: + The x-coordinate to set, between 0 and 255 """ self._destination_chip_x = destination_chip_x @property def destination_chip_y(self): - """ The y-coordinate of the destination chip of the packet + """ + The y-coordinate of the destination chip of the packet, between + 0 and 255 (settable). - :return: The y-coordinate of the chip, between 0 and 255 :rtype: int """ return self._destination_chip_y @destination_chip_y.setter def destination_chip_y(self, destination_chip_y): - """ Set the y-coordinate of the destination chip of the packet + """ + Set the y-coordinate of the destination chip of the packet. - :param destination_chip_y: The y-coordinate to set, between 0 and 255 - :type destination_chip_y: int + :param int destination_chip_y: + The y-coordinate to set, between 0 and 255 """ self._destination_chip_y = destination_chip_y @property def source_port(self): - """ The source port of the packet + """ + The source SDP port of the packet, between 0 and 7 (settable). - :return: The source port of the packet between 0 and 7 :rtype: int """ return self._source_port @source_port.setter def source_port(self, source_port): - """ Set the source port of the packet + """ + Set the source port of the packet. - :param source_port: The source port to set, between 0 and 7 - :type source_port: int + :param int source_port: The source port to set, between 0 and 7 """ self._source_port = source_port @property def source_cpu(self): - """ The core on the source chip + """ + The core on the source chip, between 0 and 31 (settable). - :return: The core on the source chip, between 0 and 31 :rtype: int """ return self._source_cpu @source_cpu.setter def source_cpu(self, source_cpu): - """ Set the ID of the source processor of the packet + """ + Set the ID of the source processor of the packet. - :param source_cpu: The processor ID to set, between 0 and 31 - :type source_cpu: int + :param int source_cpu: The processor ID to set, between 0 and 31 """ self._source_cpu = source_cpu @property def source_chip_x(self): - """ The x-coordinate of the source chip of the packet + """ + The x-coordinate of the source chip of the packet, between + 0 and 255 (settable). - :return: The x-coordinate of the chip, between 0 and 255 :rtype: int """ return self._source_chip_x @source_chip_x.setter def source_chip_x(self, source_chip_x): - """ Set the x-coordinate of the source chip of the packet + """ + Set the x-coordinate of the source chip of the packet. - :param source_chip_x: The x-coordinate to set, between 0 and 255 - :type source_chip_x: int + :param int source_chip_x: + The x-coordinate to set, between 0 and 255 """ self._source_chip_x = source_chip_x @property def source_chip_y(self): - """ The y-coordinate of the source chip of the packet + """ + The y-coordinate of the source chip of the packet, between + 0 and 255 (settable). - :return: The y-coordinate of the chip, between 0 and 255 :rtype: int """ return self._source_chip_y @source_chip_y.setter def source_chip_y(self, source_chip_y): - """ Set the y-coordinate of the source chip of the packet + """ + Set the y-coordinate of the source chip of the packet. - :param source_chip_y: The y-coordinate to set, between 0 and 255 - :type source_chip_y: int + :param int source_chip_y: + The y-coordinate to set, between 0 and 255 """ self._source_chip_y = source_chip_y @property def bytestring(self): - """ The header as a bytestring + """ + The header as a byte-string. - :return: The header bytestring - :rtype: str + :rtype: bytes """ dest_port_cpu = (((self._destination_port & 0x7) << 5) | (self._destination_cpu & 0x1F)) @@ -284,14 +291,14 @@ def bytestring(self): @staticmethod def from_bytestring(data, offset): - """ Read the header from a bytestring - - :param data: The bytestring to read the header from - :type data: str - :param offset: The offset into the data from which to start reading - :type offset: int """ + Read the header from a byte-string. + :param data: The byte-string to read the header from + :type data: bytes or bytearray + :param int offset: + The offset into the data from which to start reading + """ (flags, tag, dest_port_cpu, source_port_cpu, destination_chip_y, destination_chip_x, source_chip_y, source_chip_x) = _EIGHT_BYTES.unpack_from(data, offset) @@ -303,3 +310,12 @@ def from_bytestring(data, offset): SDPFlag(flags), tag, destination_port, destination_cpu, destination_chip_x, destination_chip_y, source_port, source_cpu, source_chip_x, source_chip_y) + + def get_physical_cpu_id(self): + if SpiNNManDataView.has_machine(): + chip = SpiNNManDataView.get_machine().get_chip_at( + self._destination_chip_x, self._destination_chip_y) + if chip is not None: + return chip.get_physical_core_string( + self._destination_cpu) + return "" diff --git a/spinnman/messages/sdp/sdp_message.py b/spinnman/messages/sdp/sdp_message.py index 8bc85476f..c41598b74 100644 --- a/spinnman/messages/sdp/sdp_message.py +++ b/spinnman/messages/sdp/sdp_message.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .sdp_header import SDPHeader class SDPMessage(object): - """ Wraps up an SDP message with a header and optional data. + """ + Wraps up an SDP message with a header and optional data. """ __slots__ = [ "_data", @@ -26,26 +26,21 @@ class SDPMessage(object): def __init__(self, sdp_header, data=None, offset=0): """ - :param sdp_header: The header of the message - :type sdp_header:\ - :py:class:`spinnman.messages.sdp.sdp_header.SDPHeader` - :param data: The data of the SDP packet, or None if no data + :param SDPHeader sdp_header: The header of the message + :param data: The data of the SDP packet, or `None` if no data :type data: bytes or bytearray or None - :param offset: The offset where the valid data starts - :type offset: int - :raise None: No known exceptions are thrown + :param int offset: The offset where the valid data starts """ - self._sdp_header = sdp_header self._data = data self._offset = offset @property def bytestring(self): - """ The bytestring of the message + """ + The byte-string of the message. - :return: The bytestring of the message - :rtype: str + :rtype: bytes """ if self._data is not None: return self._sdp_header.bytestring + self._data[self._offset:] @@ -53,21 +48,27 @@ def bytestring(self): @staticmethod def from_bytestring(data, offset): + """ + :param bytes data: + :param int offset: + :rtype: SDPMessage + """ sdp_header = SDPHeader.from_bytestring(data, offset) return SDPMessage(sdp_header, data, offset + 8) @property def sdp_header(self): - """ The header of the packet + """ + The header of the packet. - :return: An SDP header - :rtype: :py:class:`spinnman.messages.sdp.sdp_header.SDPHeader` + :rtype: SDPHeader """ return self._sdp_header @property def data(self): - """ The data in the packet + """ + The data in the packet. :rtype: bytes or bytearray or None """ @@ -75,7 +76,8 @@ def data(self): @property def offset(self): - """ The offset where the valid data starts + """ + The offset where the valid data starts. :rtype: int """ diff --git a/spinnman/messages/spinnaker_boot/__init__.py b/spinnman/messages/spinnaker_boot/__init__.py index f074c45c3..27865ad7a 100644 --- a/spinnman/messages/spinnaker_boot/__init__.py +++ b/spinnman/messages/spinnaker_boot/__init__.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .spinnaker_boot_message import SpinnakerBootMessage from .spinnaker_boot_messages import SpinnakerBootMessages diff --git a/spinnman/messages/spinnaker_boot/boot_data/__init__.py b/spinnman/messages/spinnaker_boot/boot_data/__init__.py index d358f58a8..3c9786f6e 100644 --- a/spinnman/messages/spinnaker_boot/boot_data/__init__.py +++ b/spinnman/messages/spinnaker_boot/boot_data/__init__.py @@ -1,14 +1,17 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This package contains no Python code. +""" diff --git a/spinnman/messages/spinnaker_boot/boot_data/scamp.boot b/spinnman/messages/spinnaker_boot/boot_data/scamp.boot index 1b119a3a5..7d9cc841e 100644 Binary files a/spinnman/messages/spinnaker_boot/boot_data/scamp.boot and b/spinnman/messages/spinnaker_boot/boot_data/scamp.boot differ diff --git a/spinnman/messages/spinnaker_boot/spinnaker_boot_message.py b/spinnman/messages/spinnaker_boot/spinnaker_boot_message.py index e18c67796..d5a74bd82 100644 --- a/spinnman/messages/spinnaker_boot/spinnaker_boot_message.py +++ b/spinnman/messages/spinnaker_boot/spinnaker_boot_message.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinnman.exceptions import SpinnmanInvalidParameterException @@ -23,7 +22,8 @@ class SpinnakerBootMessage(object): - """ A message used for booting the board + """ + A message used for booting the board. """ __slots__ = [ "_data", @@ -36,20 +36,14 @@ class SpinnakerBootMessage(object): def __init__(self, opcode, operand_1, operand_2, operand_3, data=None, offset=0): """ - :param opcode: The operation of this packet - :type opcode:\ - :py:class:`spinnman.messages.spinnaker_boot.SpinnakerBootOpCode` - :param operand_1: The first operand - :type operand_1: int - :param operand_2: The second operand - :type operand_2: int - :param operand_3: The third operand - :type operand_3: int + :param SpinnakerBootOpCode opcode: The operation of this packet + :param int operand_1: The first operand + :param int operand_2: The second operand + :param int operand_3: The third operand :param data: The optional data, up to 256 words - :type data: str - :param offset: The offset of the valid data - :type offset: int - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + :type data: bytes or bytearray + :param int offset: The offset of the valid data + :raise SpinnmanInvalidParameterException: If the opcode is not a valid value """ # pylint: disable=too-many-arguments @@ -67,53 +61,55 @@ def __init__(self, opcode, operand_1, operand_2, operand_3, data=None, @property def opcode(self): - """ The operation of this packet + """ + The operation of this packet. - :return: The operation code - :rtype:\ - :py:class:`spinnman.messages.spinnaker_boot.SpinnakerBootOpCode` + :rtype: SpinnakerBootOpCode """ return self._opcode @property def operand_1(self): - """ The first operand + """ + The first operand. - :return: The operand :rtype: int """ return self._operand_1 @property def operand_2(self): - """ The second operand + """ + The second operand. - :return: The second operand :rtype: int """ return self._operand_2 @property def operand_3(self): - """ The third operand + """ + The third operand. - :return: The third operand :rtype: int """ return self._operand_3 @property def data(self): - """ The data + """ + The data, or `None` if no data. - :return: The data or None if no data - :rtype: bytearray + :rtype: bytes or bytearray """ return self._data @property def bytestring(self): - """ The message as a bytestring + """ + The message as a byte-string. + + :rtype: bytes """ data = b"" if self._data is not None: @@ -124,6 +120,11 @@ def bytestring(self): @staticmethod def from_bytestring(data, offset): + """ + :param bytes data: + :param int offset: + :rtype: SpinnakerBootMessage + """ (opcode_value, operand_1, operand_2, operand_3) = \ _PATTERN_2xIIII.unpack_from(data, offset) the_data = None diff --git a/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py b/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py index 08871ef04..f1a364177 100644 --- a/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py +++ b/spinnman/messages/spinnaker_boot/spinnaker_boot_messages.py @@ -1,23 +1,21 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import os import math import time import array -from six import iteritems from .system_variable_boot_values import ( SystemVariableBootValues, spinnaker_boot_values, SystemVariableDefinition) from .spinnaker_boot_message import SpinnakerBootMessage @@ -35,7 +33,8 @@ class SpinnakerBootMessages(object): - """ Represents a set of boot messages to be sent to boot the board + """ + A set of boot messages to be sent to boot the board. """ __slots__ = [ "_boot_data", @@ -43,15 +42,16 @@ class SpinnakerBootMessages(object): "_no_data_packets"] def __init__(self, board_version=None, extra_boot_values=None): - """ Builds the boot messages needed to boot the SpiNNaker machine - - :param board_version: The version of the board to be booted - :type board_version: int - :param extra_boot_values: Any additional values to be set during boot - :type extra_boot_values: dict of SystemVariableDefinition to value - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + """ + :param int board_version: The version of the board to be booted + :param extra_boot_values: + Any additional or overwrite values to set during boot. + This should only be used for values which are not standard + based on the board version. + :type extra_boot_values: dict(SystemVariableDefinition, object) + :raise SpinnmanInvalidParameterException: If the board version is not supported - :raise spinnman.exceptions.SpinnmanIOException: \ + :raise SpinnmanIOException: If there is an error assembling the packets """ if (board_version is not None and @@ -75,7 +75,7 @@ def __init__(self, board_version=None, extra_boot_values=None): # Set any additional values if extra_boot_values is not None: - for variable, value in iteritems(extra_boot_values): + for variable, value in extra_boot_values.items(): spinnaker_boot_value.set_value(variable, value) # Get the data as an array, to be used later @@ -101,26 +101,33 @@ def __init__(self, board_version=None, extra_boot_values=None): # Byte swap and store the data for later use boot_data.byteswap() - self._boot_data = boot_data.tostring() + self._boot_data = boot_data.tobytes() self._n_bytes_to_read = n_words_to_read * 4 @staticmethod def _get_boot_image_file(): + """ + :rtype: tuple(str,int) + """ this_dir, _ = os.path.split(__file__) file_name = os.path.join(this_dir, "boot_data", _BOOT_DATA_FILE_NAME) file_size = os.stat(file_name).st_size if file_size > _BOOT_IMAGE_MAX_BYTES: raise SpinnmanIOException( - "The boot file is too big at {} bytes (only files up to 32KB " - "are acceptable".format(file_size)) + f"The boot file is too big at {file_size} bytes " + "(only files up to 32KB are acceptable") elif file_size % 4 != 0: raise SpinnmanIOException( - "The boot file size of {} bytes must be divisible by 4".format( - file_size)) + f"The boot file size of {file_size} bytes " + "must be divisible by 4") return file_name, file_size def _get_packet_data(self, block_id): - """ Read a packet of data + """ + Read a packet of data. + + :param int block_id: + :rtype: bytes """ offset = block_id * _BOOT_MESSAGE_DATA_BYTES n_bytes = min(self._n_bytes_to_read - offset, _BOOT_MESSAGE_DATA_BYTES) @@ -128,9 +135,11 @@ def _get_packet_data(self, block_id): @property def messages(self): - """ Get an iterable of message to be sent. """ + An iterable of message to be sent. + :rtype: iterable(SpinnakerBootMessage) + """ # Construct and yield the start packet yield SpinnakerBootMessage( opcode=SpinnakerBootOpCode.FLOOD_FILL_START, diff --git a/spinnman/messages/spinnaker_boot/spinnaker_boot_op_code.py b/spinnman/messages/spinnaker_boot/spinnaker_boot_op_code.py index f8f3c362d..60f347192 100644 --- a/spinnman/messages/spinnaker_boot/spinnaker_boot_op_code.py +++ b/spinnman/messages/spinnaker_boot/spinnaker_boot_op_code.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class SpinnakerBootOpCode(Enum): - """ Boot message Operation Codes + """ + Boot message Operation Codes. """ HELLO = 0x41 diff --git a/spinnman/messages/spinnaker_boot/system_variable_boot_values.py b/spinnman/messages/spinnaker_boot/system_variable_boot_values.py index a45245aec..b3fb9d35c 100644 --- a/spinnman/messages/spinnaker_boot/system_variable_boot_values.py +++ b/spinnman/messages/spinnaker_boot/system_variable_boot_values.py @@ -1,22 +1,18 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -try: - from collections.abc import namedtuple -except ImportError: - from collections import namedtuple +from collections import namedtuple import struct from enum import Enum @@ -24,7 +20,8 @@ class _DataType(Enum): - """ Enum for data types + """ + Enum for data types. """ BYTE = (1, ". +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .adc_info import ADCInfo from .bmp_connection_data import BMPConnectionData diff --git a/spinnman/model/adc_info.py b/spinnman/model/adc_info.py index ae90308b4..772c56d06 100644 --- a/spinnman/model/adc_info.py +++ b/spinnman/model/adc_info.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinnman import constants @@ -27,7 +26,8 @@ class ADCInfo(object): - """ Container for the ADC data thats been retrieved from an FPGA. + """ + Container for the ADC data that's been retrieved from an FPGA. """ __slots__ = [ "_fan_0", @@ -45,9 +45,9 @@ class ADCInfo(object): def __init__(self, adc_data, offset): """ - :param adc_data: bytes from an SCP packet containing ADC information - :type adc_data: str - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + :param bytes adc_data: + bytes from an SCP packet containing ADC information + :raise SpinnmanInvalidParameterException: If the message does not contain valid ADC information """ data = _PATTERN.unpack_from(adc_data, offset) @@ -79,7 +79,8 @@ def __init__(self, adc_data, offset): @property def voltage_1_2c(self): - """ Actual voltage of the 1.2V c supply rail + """ + Actual voltage of the 1.2V c supply rail. :rtype: float """ @@ -87,7 +88,8 @@ def voltage_1_2c(self): @property def voltage_1_2b(self): - """ Actual voltage of the 1.2V b supply rail + """ + Actual voltage of the 1.2V b supply rail. :rtype: float """ @@ -95,7 +97,8 @@ def voltage_1_2b(self): @property def voltage_1_2a(self): - """ Actual voltage of the 1.2V a supply rail + """ + Actual voltage of the 1.2V a supply rail. :rtype: float """ @@ -103,7 +106,8 @@ def voltage_1_2a(self): @property def voltage_1_8(self): - """ Actual voltage of the 1.8V supply rail + """ + Actual voltage of the 1.8V supply rail. :rtype: float """ @@ -111,7 +115,8 @@ def voltage_1_8(self): @property def voltage_3_3(self): - """ Actual voltage of the 3.3V supply rail + """ + Actual voltage of the 3.3V supply rail. :rtype: float """ @@ -119,7 +124,8 @@ def voltage_3_3(self): @property def voltage_supply(self): - """ Actual voltage of the main power supply (nominally 12V). + """ + Actual voltage of the main power supply (nominally 12V). :rtype: float """ @@ -127,7 +133,8 @@ def voltage_supply(self): @property def temp_top(self): - """ temperature top + """ + Temperature top. :rtype: float """ @@ -135,7 +142,8 @@ def temp_top(self): @property def temp_btm(self): - """ temperature bottom + """ + Temperature bottom. :rtype: float """ @@ -143,7 +151,8 @@ def temp_btm(self): @property def temp_ext_0(self): - """ temperature external 0 + """ + Temperature external 0. :rtype: float or None """ @@ -151,7 +160,8 @@ def temp_ext_0(self): @property def temp_ext_1(self): - """ temperature external 1 + """ + Temperature external 1. :rtype: float or None """ @@ -159,7 +169,8 @@ def temp_ext_1(self): @property def fan_0(self): - """ fan 0 + """ + Fan 0. :rtype: float or None """ @@ -167,7 +178,8 @@ def fan_0(self): @property def fan_1(self): - """ fan 1 + """ + Fan 1. :rtype: float or None """ diff --git a/spinnman/model/bmp_connection_data.py b/spinnman/model/bmp_connection_data.py index 318963c7a..4b2507190 100644 --- a/spinnman/model/bmp_connection_data.py +++ b/spinnman/model/bmp_connection_data.py @@ -1,56 +1,37 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. class BMPConnectionData(object): - """ Contains the details of a BMP connection + """ + Contains the details of a BMP connection. """ __slots__ = [ "_boards", - "_cabinet", - "_frame", "_ip_address", "_port_num"] - def __init__(self, cabinet, frame, ip_address, boards, port_num): + def __init__(self, ip_address, boards, port_num): # pylint: disable=too-many-arguments - self._cabinet = cabinet - self._frame = frame self._ip_address = ip_address self._boards = boards self._port_num = port_num - @property - def cabinet(self): - """ Get the cabinet number. - - :rtype: int - """ - return self._cabinet - - @property - def frame(self): - """ Get the frame number. - - :rtype: int - """ - return self._frame - @property def ip_address(self): - """ Get the IP address of the BMP. + """ + The IP address of the BMP. :rtype: str """ @@ -58,7 +39,8 @@ def ip_address(self): @property def boards(self): - """ The boards to be addressed. + """ + The boards to be addressed. :rtype: iterable(int) """ @@ -66,16 +48,15 @@ def boards(self): @property def port_num(self): - """ The port number associated with this BMP connection. + """ + The port number associated with this BMP connection. :return: The port number """ return self._port_num def __str__(self): - return "{}:{}:{}:{}:{}".format( - self._cabinet, self._frame, self._ip_address, self._boards, - self._port_num) + return (f"{self._ip_address}:{self._boards}:{self._port_num}") def __repr__(self): return self.__str__() diff --git a/spinnman/model/chip_info.py b/spinnman/model/chip_info.py index 2c21a45a2..7db83dc85 100644 --- a/spinnman/model/chip_info.py +++ b/spinnman/model/chip_info.py @@ -1,25 +1,24 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinnman.messages.spinnaker_boot import SystemVariableDefinition class ChipInfo(object): - """ Represents the system variables for a chip, received from the chip\ - SDRAM + """ + Represents the system variables for a chip, received from the chip SDRAM. """ __slots__ = [ "_ip_address", @@ -35,11 +34,11 @@ class ChipInfo(object): def __init__(self, system_data, offset): """ - :param system_data: An bytestring retrieved from SDRAM on the board - :type system_data: str - :param offset: \ - The offset into the bytestring where the actual data starts - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + :param bytes system_data: + A byte-string retrieved from SDRAM on the board + :param int offset: + The offset into the byte-string where the actual data starts + :raise SpinnmanInvalidParameterException: If the data doesn't contain valid system data information """ self._system_data = system_data @@ -70,7 +69,7 @@ def __init__(self, system_data, offset): self._virtual_core_ids.sort() ip = bytearray(self._read_value("ethernet_ip_address")) - self._ip_address = "{}.{}.{}.{}".format(ip[0], ip[1], ip[2], ip[3]) + self._ip_address = f"{ip[0]}.{ip[1]}.{ip[2]}.{ip[3]}" if self._ip_address == "0.0.0.0": self._ip_address = None @@ -78,7 +77,7 @@ def _read_value(self, item): item_def = SystemVariableDefinition[item] code = item_def.data_type.struct_code if item_def.array_size is not None: - code = "{}{}".format(item_def.array_size, code) + code = f"{item_def.array_size}{code}" values = struct.unpack_from( code, self._system_data, self._offset + item_def.offset) return values[0] @@ -88,7 +87,8 @@ def __getattr__(self, item): @property def x(self): - """ The x-coordinate of the chip + """ + The X-coordinate of the chip. :rtype: int """ @@ -96,7 +96,8 @@ def x(self): @property def y(self): - """ The y-coordinate of the chip + """ + The Y-coordinate of the chip. :rtype: int """ @@ -104,7 +105,8 @@ def y(self): @property def x_size(self): - """ The number of chips in the x-dimension + """ + The number of chips in the X-dimension. :rtype: int """ @@ -112,7 +114,8 @@ def x_size(self): @property def y_size(self): - """ The number of chips in the y-dimension + """ + The number of chips in the Y-dimension. :rtype: int """ @@ -120,7 +123,8 @@ def y_size(self): @property def nearest_ethernet_x(self): - """ The x-coordinate of the nearest chip with Ethernet + """ + The X-coordinate of the nearest chip with Ethernet. :rtype: int """ @@ -128,7 +132,8 @@ def nearest_ethernet_x(self): @property def nearest_ethernet_y(self): - """ The y-coordinate of the nearest chip with Ethernet + """ + The Y-coordinate of the nearest chip with Ethernet. :rtype: int """ @@ -136,7 +141,8 @@ def nearest_ethernet_y(self): @property def is_ethernet_available(self): - """ True if the Ethernet is running on this chip, False otherwise + """ + Whether the Ethernet is running on this chip. :rtype: bool """ @@ -144,15 +150,17 @@ def is_ethernet_available(self): @property def links_available(self): - """ The links that are available on the chip + """ + The links that are available on the chip. - :rtype: iterable of int + :rtype: iterable(int) """ return self._links_available @property def cpu_clock_mhz(self): - """ The speed of the CPU clock in MHz + """ + The speed of the CPU clock in MHz. :rtype: int """ @@ -160,17 +168,28 @@ def cpu_clock_mhz(self): @property def physical_to_virtual_core_map(self): - """ The physical core ID to virtual core ID map; entries with a value\ - of 0xFF are non-operational cores + """ + The physical core ID to virtual core ID map; entries with a value + of 0xFF are non-operational cores. :rtype: bytearray """ return self._physical_to_virtual_core_map + @property + def virtual_to_physical_core_map(self): + """ + The virtual core ID to physical core ID map; entries with a value + of 0xFF are non-operational cores. + + :rtype: bytearray + """ + return self._virtual_to_physical_core_map + @property def virtual_core_ids(self): - """ A list of available cores by virtual core ID (including the\ - monitor) + """ + A list of available cores by virtual core ID (including the monitor). :rtype: iterable(int) """ @@ -178,7 +197,8 @@ def virtual_core_ids(self): @property def sdram_base_address(self): - """ The base address of the user region of SDRAM on the chip + """ + The base address of the user region of SDRAM on the chip. :rtype: int """ @@ -186,7 +206,8 @@ def sdram_base_address(self): @property def system_sdram_base_address(self): - """ The base address of the System SDRAM region on the chip + """ + The base address of the System SDRAM region on the chip. :rtype: int """ @@ -194,7 +215,8 @@ def system_sdram_base_address(self): @property def cpu_information_base_address(self): - """ The base address of the CPU information structure + """ + The base address of the CPU information structure. :rtype: int """ @@ -202,7 +224,8 @@ def cpu_information_base_address(self): @property def first_free_router_entry(self): - """ The ID of the first free routing entry on the chip + """ + The ID of the first free routing entry on the chip. :rtype: int """ @@ -210,7 +233,8 @@ def first_free_router_entry(self): @property def ip_address(self): - """ The IP address of the chip, or None if no Ethernet + """ + The IP address of the chip, or `None` if no Ethernet. :rtype: str """ @@ -218,14 +242,16 @@ def ip_address(self): @property def iobuf_size(self): - """ The size of the iobuf buffers in bytes + """ + The size of the IOBUF buffers in bytes. :rtype: int """ return self._read_value("iobuf_size") def router_table_copy_address(self): - """ The address of the copy of the router table + """ + The address of the copy of the router table. :rtype: int """ @@ -233,7 +259,8 @@ def router_table_copy_address(self): @property def system_ram_heap_address(self): - """ The address of the base of the heap in system RAM + """ + The address of the base of the heap in system RAM. :rtype: int """ @@ -241,7 +268,8 @@ def system_ram_heap_address(self): @property def sdram_heap_address(self): - """ The address of the base of the heap in SDRAM + """ + The address of the base of the heap in SDRAM. :rtype: int """ diff --git a/spinnman/model/chip_summary_info.py b/spinnman/model/chip_summary_info.py index 0525b68c9..648ab0318 100644 --- a/spinnman/model/chip_summary_info.py +++ b/spinnman/model/chip_summary_info.py @@ -1,29 +1,31 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2016 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinnman.model.enums import CPUState +from spinn_machine.machine import Machine _THREE_WORDS = struct.Struct("<3I") _TWO_BYTES = struct.Struct(" data_offset: + (self._parent_link, ) = _ONE_SHORT.unpack_from( + chip_summary_data, data_offset) + # The root chip will use the P2P "self", which is outside the range + # of valid links, so check and skip this one + if self._parent_link > len(Machine.LINK_ADD_TABLE): + self._parent_link = None + data_offset += 2 + @property def x(self): - """ The x-coordinate of the chip that this data is from + """ + The X-coordinate of the chip that this data is from. :rtype: int """ @@ -92,7 +104,8 @@ def x(self): @property def y(self): - """ The y-coordinate of the chip that this data is from + """ + The Y-coordinate of the chip that this data is from. :rtype: int """ @@ -100,7 +113,8 @@ def y(self): @property def n_cores(self): - """ The number of cores working on the chip (including monitors) + """ + The number of cores working on the chip (including monitors). :rtype: int """ @@ -108,15 +122,17 @@ def n_cores(self): @property def core_states(self): - """ The state of the cores on the chip (list of one per core) + """ + The state of the cores on the chip (list of one per core). - :rtype: list(:py:class:`spinnman.model.enums.CPUState`) + :rtype: list(CPUState) """ return self._core_states @property def working_links(self): - """ The IDs of the working links outgoing from this chip + """ + The IDs of the working links outgoing from this chip. :rtype: list(int) """ @@ -124,7 +140,8 @@ def working_links(self): @property def is_ethernet_available(self): - """ Determines if the Ethernet connection is available on this chip + """ + Whether the Ethernet connection is available on this chip. :rtype: bool """ @@ -132,7 +149,8 @@ def is_ethernet_available(self): @property def n_free_multicast_routing_entries(self): - """ The number of multicast routing entries free on this chip + """ + The number of multicast routing entries free on this chip. :rtype: int """ @@ -140,7 +158,8 @@ def n_free_multicast_routing_entries(self): @property def largest_free_sdram_block(self): - """ The size of the largest block of free SDRAM in bytes + """ + The size of the largest block of free SDRAM in bytes. :rtype: int """ @@ -148,7 +167,8 @@ def largest_free_sdram_block(self): @property def largest_free_sram_block(self): - """ The size of the largest block of free SRAM in bytes + """ + The size of the largest block of free SRAM in bytes. :rtype: int """ @@ -156,7 +176,8 @@ def largest_free_sram_block(self): @property def nearest_ethernet_x(self): - """ The x coordinate of the nearest Ethernet chip + """ + The X-coordinate of the nearest Ethernet chip. :rtype: int """ @@ -164,7 +185,8 @@ def nearest_ethernet_x(self): @property def nearest_ethernet_y(self): - """ The y coordinate of the nearest Ethernet chip + """ + The Y-coordinate of the nearest Ethernet chip. :rtype: int """ @@ -172,8 +194,27 @@ def nearest_ethernet_y(self): @property def ethernet_ip_address(self): - """ The IP address of the Ethernet if up, or None if not + """ + The IP address of the Ethernet if up, or `None` if not. :rtype: str """ return self._ethernet_ip_address + + def clear_ethernet_ip_address(self): + """ + Forces the Ethernet IP address to `None`, in case of an errant chip. + """ + self._ethernet_ip_address = None + + @property + def parent_link(self): + """ + The link to the parent of the chip in the tree of chips from root. + + :rtype: int + """ + return self._parent_link + + def __repr__(self): + return f"x:{self.x} y:{self.y} n_cores:{self.n_cores}" diff --git a/spinnman/model/cpu_info.py b/spinnman/model/cpu_info.py index 05e92f222..101db2fd0 100644 --- a/spinnman/model/cpu_info.py +++ b/spinnman/model/cpu_info.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinnman.model.enums import CPUState, RunTimeError, MailboxCommand @@ -24,7 +23,8 @@ class CPUInfo(object): - """ Represents information about the state of a CPU. + """ + Represents information about the state of a CPU. """ __slots__ = [ "_application_id", @@ -49,16 +49,12 @@ class CPUInfo(object): "_user", "_x", "_y", "_p"] - def __init__(self, x, y, p, cpu_data, offset): + def __init__(self, x, y, p, cpu_data): """ - :param x: The x-coordinate of a chip - :type x: int - :param y: The y-coordinate of a chip - :type y: int - :param p: The ID of a core on the chip - :type p: int - :param cpu_data: A bytestring received from SDRAM on the board - :type cpu_data: str + :param int x: The x-coordinate of a chip + :param int y: The y-coordinate of a chip + :param int p: The ID of a core on the chip + :param tuple cpu_data: A byte-string received from SDRAM on the board """ # pylint: disable=too-many-arguments self._x = x @@ -80,7 +76,7 @@ def __init__(self, x, y, p, cpu_data, offset): self._iobuf_address, self._software_version, # 2I 88 # skipped # 16x 96 user0, user1, user2, user3 # 4I 112 - ) = _INFO_PATTERN.unpack_from(cpu_data, offset) + ) = cpu_data index = self._application_name.find(b'\0') if index != -1: @@ -99,7 +95,8 @@ def __init__(self, x, y, p, cpu_data, offset): @property def x(self): - """ The x-coordinate of the chip containing the core. + """ + The x-coordinate of the chip containing the core. :return: The x-coordinate of the chip :rtype: int @@ -108,7 +105,8 @@ def x(self): @property def y(self): - """ The y-coordinate of the chip containing the core. + """ + The y-coordinate of the chip containing the core. :return: The y-coordinate of the chip :rtype: int @@ -117,7 +115,8 @@ def y(self): @property def p(self): - """ The ID of the core on the chip. + """ + The ID of the core on the chip. :return: The ID of the core :rtype: int @@ -126,16 +125,18 @@ def p(self): @property def state(self): - """ The current state of the core. + """ + The current state of the core. :return: The state of the core - :rtype: :py:class:`spinnman.model.enums.cpu_state.CPUState` + :rtype: CPUState """ return self._state @property def physical_cpu_id(self): - """ The physical ID of this processor. + """ + The physical ID of this processor. :return: The physical ID of the processor :rtype: int @@ -144,7 +145,8 @@ def physical_cpu_id(self): @property def application_name(self): - """ The name of the application running on the core. + """ + The name of the application running on the core. :return: The name of the application :rtype: str @@ -153,7 +155,8 @@ def application_name(self): @property def application_id(self): - """ The ID of the application running on the core. + """ + The ID of the application running on the core. :return: The ID of the application :rtype: int @@ -162,7 +165,8 @@ def application_id(self): @property def time(self): - """ The time at which the application started. + """ + The time at which the application started. :return: The time in seconds since 00:00:00 on the 1st January 1970 :rtype: int @@ -171,26 +175,29 @@ def time(self): @property def run_time_error(self): - """ The reason for a run time error. + """ + The reason for a run time error. :return: The run time error - :rtype: :py:class:`spinnman.model.enums.run_time_error.RunTimeError` + :rtype: RunTimeError """ return self._run_time_error @property def application_mailbox_command(self): - """ The command currently in the mailbox being sent from the monitor\ - processor to the application. + """ + The command currently in the mailbox being sent from the monitor + processor to the application. :return: The command - :rtype: :py:class:`spinnman.model.enums.mailbox_command.MailboxCommand` + :rtype: MailboxCommand """ return self._application_mailbox_command @property def application_mailbox_data_address(self): - """ The address of the data in SDRAM for the application mailbox. + """ + The address of the data in SDRAM for the application mailbox. :return: The address of the data :rtype: int @@ -199,17 +206,19 @@ def application_mailbox_data_address(self): @property def monitor_mailbox_command(self): - """ The command currently in the mailbox being sent from the\ - application to the monitor processor. + """ + The command currently in the mailbox being sent from the + application to the monitor processor. :return: The command - :rtype: :py:class:`spinnman.model.mailbox_command.MailboxCommand` + :rtype: MailboxCommand """ return self._monitor_mailbox_command @property def monitor_mailbox_data_address(self): - """ The address of the data in SDRAM of the monitor mailbox. + """ + The address of the data in SDRAM of the monitor mailbox. :return: The address of the data :rtype: int @@ -218,7 +227,8 @@ def monitor_mailbox_data_address(self): @property def software_error_count(self): - """ The number of software errors counted. + """ + The number of software errors counted. :return: The number of software errors :rtype: int @@ -227,7 +237,8 @@ def software_error_count(self): @property def software_source_filename_address(self): - """ The address of the filename of the software source. + """ + The address of the filename of the software source. :return: The filename :rtype: str @@ -236,7 +247,8 @@ def software_source_filename_address(self): @property def software_source_line_number(self): - """ The line number of the software source. + """ + The line number of the software source. :return: The line number :rtype: int @@ -245,7 +257,8 @@ def software_source_line_number(self): @property def processor_state_register(self): - """ The value in the processor state register (PSR). + """ + The value in the processor state register (PSR). :return: The PSR value :rtype: int @@ -254,7 +267,8 @@ def processor_state_register(self): @property def stack_pointer(self): - """ The current stack pointer value (SP). + """ + The current stack pointer value (SP). :return: The SP value :rtype: int @@ -263,7 +277,8 @@ def stack_pointer(self): @property def link_register(self): - """ The current link register value (LR). + """ + The current link register value (LR). :return: The LR value :rtype: int @@ -272,25 +287,28 @@ def link_register(self): @property def registers(self): - """ The current register values (r0 - r7). + """ + The current register values (r0 - r7). :return: An array of 8 values, one for each register - :rtype: array of int + :rtype: list(int) """ return self._registers @property def user(self): - """ The current user values (user0 - user3). + """ + The current user values (user0 - user3). :return: An array of 4 values, one for each user value - :rtype: array of int + :rtype: list(int) """ return self._user @property def iobuf_address(self): - """ The address of the IOBUF buffer in SDRAM. + """ + The address of the IOBUF buffer in SDRAM. :return: The address :rtype: int @@ -299,7 +317,8 @@ def iobuf_address(self): @property def software_version(self): - """ The software version. + """ + The software version. :return: The software version :rtype: int @@ -307,6 +326,6 @@ def software_version(self): return self._software_version def __str__(self): - return "{}:{}:{:02n} {:18} {:16s} {:3n}".format( - self.x, self.y, self.p, self._state.name, self._application_name, - self._application_id) + 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) diff --git a/spinnman/model/cpu_infos.py b/spinnman/model/cpu_infos.py index 38fb9fd90..181d8cc51 100644 --- a/spinnman/model/cpu_infos.py +++ b/spinnman/model/cpu_infos.py @@ -1,63 +1,70 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -try: - from collections.abc import OrderedDict -except ImportError: - from collections import OrderedDict -from six import iteritems, iterkeys, itervalues +from spinnman.model.enums import CPUState class CPUInfos(object): - """ A set of CPU information objects. + """ + A set of CPU information objects. """ __slots__ = [ "_cpu_infos"] def __init__(self): - self._cpu_infos = OrderedDict() + self._cpu_infos = dict() - def add_processor(self, x, y, processor_id, cpu_info): - """ Add a processor on a given chip to the set. + def add_info(self, cpu_info): + """ + Add a info on using its core coordinates. - :param x: The x-coordinate of the chip - :type x: int - :param y: The y-coordinate of the chip - :type y: int - :param processor_id: A processor ID - :type processor_id: int - :param cpu_info: The CPU information for the core - :type cpu_info: :py:class:`spinnman.model.enums.cpu_info.CPUInfo` + :param ~spinnman.model.CPUInfo 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 """ self._cpu_infos[x, y, processor_id] = cpu_info @property def cpu_infos(self): - """ The one per core core info. + """ + The one per core core info. :return: iterable of x,y,p core info + :rtype: iterable(~spinnman.model.CPUInfo) """ - return iteritems(self._cpu_infos) + return iter(self._cpu_infos.items()) def __iter__(self): return iter(self._cpu_infos) def iteritems(self): - """ Get an iterable of (x, y, p), cpu_info """ - return iteritems(self._cpu_infos) + 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() @@ -66,35 +73,84 @@ def values(self): return self._cpu_infos.values() def itervalues(self): - """ Get an iterable of cpu_info. """ - return itervalues(self._cpu_infos) + 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 iterkeys(self._cpu_infos) + 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. + """ + The total number of processors that are in these core subsets. """ return len(self._cpu_infos) def is_core(self, x, y, p): - """ Determine if there is a CPU Info for x, y, p + """ + Determine if there is a CPU Info for x, y, p. """ return (x, y, p) in self._cpu_infos def get_cpu_info(self, x, y, p): - """ Get the information for the given core on the given chip + """ + Get the information for the given core on the given core + + :rtype: CpuInfo """ return self._cpu_infos[x, y, p] + def infos_for_state(self, state): + """ + Creates a new CpuInfos object with Just the Infos that match the state. + + :param ~spinnman.model.enums.CPUState state: + :return: New Infos object with the filtered infos if any + :rtype: CPUInfo + """ + for_state = CPUInfos() + for info in self._cpu_infos.values(): + if info.state == state: + for_state.add_info(info) + return for_state + + def get_status_string(self): + """ + Get a string indicating the status of the given cores. + + :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) + return break_down + def __str__(self): - return self._cpu_infos.keys() + return str([f"{x}, {y}, {p} (ph: {info.physical_cpu_id})" + for (x, y, p), info in self._cpu_infos.items()]) def __repr__(self): return self.__str__() diff --git a/spinnman/model/diagnostic_filter.py b/spinnman/model/diagnostic_filter.py index 7c0e59ccc..7e628ca75 100644 --- a/spinnman/model/diagnostic_filter.py +++ b/spinnman/model/diagnostic_filter.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinnman.model.enums import ( DiagnosticFilterDestination, DiagnosticFilterSource, @@ -52,11 +51,12 @@ def _read_flags_from_word(word, enum_list, offset): class DiagnosticFilter(object): - """ A router diagnostic counter filter, which counts packets passing\ - through the router with certain properties. The counter will be\ - incremented so long as the packet matches one of the values in each\ - field i.e. one of each of the destinations, sources, payload_statuses,\ - default_routing_statuses, emergency_routing_statuses and packet_types. + """ + A router diagnostic counter filter, which counts packets passing + through the router with certain properties. The counter will be + incremented so long as the packet matches one of the values in each + field i.e. one of each of the destinations, sources, payload_statuses, + default_routing_statuses, emergency_routing_statuses and packet_types. """ __slots__ = [ "_default_routing_statuses", @@ -74,40 +74,34 @@ def __init__(self, enable_interrupt_on_counter_event, default_routing_statuses, emergency_routing_statuses, packet_types): """ - :param enable_interrupt_on_counter_event: Indicates whether\ + :param bool enable_interrupt_on_counter_event: Indicates whether an interrupt should be raised when this rule matches - :type enable_interrupt_on_counter_event: bool - :param match_emergency_routing_status_to_incoming_packet: Indicates\ - whether the emergency routing statuses should be matched against\ - packets arriving at this router (if True), or if they should be\ - matched against packets leaving this router (if False) - :type match_emergency_routing_status_to_incoming_packet: bool - :param destinations: Increment the counter if one or more of the given\ - destinations match - :type destinations: \ - iterable(:py:class:`spinnman.model.enums.DiagnosticFilterDestination`) - :param sources: Increment the counter if one or more of the given\ - sources match (or None or empty list to match all) - :type sources: \ - iterable(:py:class:`spinnman.model.enums.DiagnosticFilterSource`) - :param payload_statuses: Increment the counter if one or more of the\ - given payload statuses match (or None or empty list to match all) - :type payload_statuses: \ - iterable(:py:class:`spinnman.model.enums.DiagnosticFilterPayloadStatus`) - :param default_routing_statuses: Increment the counter if one or more\ - of the given default routing statuses match (or None or empty\ - list to match all) - :type default_routing_statuses: \ - iterable(:py:class:`spinnman.model.enums.DiagnosticFilterDefaultRoutingStatus`) - :param emergency_routing_statuses: Increment the counter if one or\ - more of the given emergency routing statuses match (or None or\ - empty list to match all) - :type emergency_routing_statuses: \ - iterable(:py:class:`spinnman.model.enums.DiagnosticFilterEmergencyRoutingStatus`) - :param packet_types: Increment the counter if one or more of the\ - given packet types match (or None or empty list to match all) - :type packet_types: \ - iterable(:py:class:`spinnman.model.enums.DiagnosticFilterPacketType`) + :param bool match_emergency_routing_status_to_incoming_packet: + Indicates whether the emergency routing statuses should be matched + against packets arriving at this router (if True), or if they + should be matched against packets leaving this router (if False) + :param list(DiagnosticFilterDestination) destinations: + Increment the counter if one or more of the given destinations + match + :param list(DiagnosticFilterSource) sources: + Increment the counter if one or more of the given sources match + (or `None` or empty list to match all) + :param list(DiagnosticFilterPayloadStatus) payload_statuses: + Increment the counter if one or more of the given payload statuses + match (or `None` or empty list to match all) + :param default_routing_statuses: + Increment the counter if one or more of the given default routing + statuses match (or `None` or empty list to match all) + :type default_routing_statuses: + list(DiagnosticFilterDefaultRoutingStatus) + :param emergency_routing_statuses: + Increment the counter if one or more of the given emergency routing + statuses match (or `None` or empty list to match all) + :type emergency_routing_statuses: + list(DiagnosticFilterEmergencyRoutingStatus) + :param list(DiagnosticFilterPacketType) packet_types: + Increment the counter if one or more of the given packet types + match (or `None` or empty list to match all) """ # pylint: disable=too-many-arguments self._enable_interrupt_on_counter_event = \ @@ -155,8 +149,8 @@ def packet_types(self): @property def filter_word(self): - """ A word of data that can be written to the router to set up\ - the filter + """ + A word of data that can be written to the router to set up the filter. """ data = 0 if self._enable_interrupt_on_counter_event: diff --git a/spinnman/model/enums/__init__.py b/spinnman/model/enums/__init__.py index 8ae228b63..5dd80ba2a 100644 --- a/spinnman/model/enums/__init__.py +++ b/spinnman/model/enums/__init__.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2016 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .cpu_state import CPUState from .diagnostic_filter_default_routing_status import ( @@ -22,12 +21,18 @@ from .diagnostic_filter_packet_type import DiagnosticFilterPacketType from .diagnostic_filter_payload_status import DiagnosticFilterPayloadStatus from .diagnostic_filter_source import DiagnosticFilterSource +from .executable_type import ExecutableType from .mailbox_command import MailboxCommand from .p2p_table_route import P2PTableRoute from .run_time_error import RunTimeError +from .router_error import RouterError +from .sdp_ports import SDP_PORTS +from .sdp_running_message_codes import SDP_RUNNING_MESSAGE_CODES __all__ = ["CPUState", "DiagnosticFilterDefaultRoutingStatus", - "DiagnosticFilterDestination", "RunTimeError", + "DiagnosticFilterDestination", "DiagnosticFilterEmergencyRoutingStatus", "DiagnosticFilterPacketType", "DiagnosticFilterPayloadStatus", - "DiagnosticFilterSource", "MailboxCommand", "P2PTableRoute"] + "DiagnosticFilterSource", "ExecutableType", "MailboxCommand", + "P2PTableRoute", "RouterError", "RunTimeError", "SDP_PORTS", + "SDP_RUNNING_MESSAGE_CODES"] diff --git a/spinnman/model/enums/cpu_state.py b/spinnman/model/enums/cpu_state.py index e0054a6cf..b9941d9ec 100644 --- a/spinnman/model/enums/cpu_state.py +++ b/spinnman/model/enums/cpu_state.py @@ -1,39 +1,55 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class CPUState(Enum): - """ SARK CPU States """ + SARK CPU States. + """ + #: Dead core DEAD = 0 + #: Powered down POWERED_DOWN = 1 + #: Died with Run Time Error RUN_TIME_EXCEPTION = 2 + #: Watchdog expired WATCHDOG = 3 + #: Initialising (transient) INITIALISING = 4 + #: Ready to execute READY = 5 + #: Entered c_main C_MAIN = 6 + #: Running (API/Event) RUNNING = 7 + #: Waiting for sync 0 SYNC0 = 8 + #: Waiting for sync 1 SYNC1 = 9 + #: Paused in application PAUSED = 10 + #: Exited application FINISHED = 11 + #: Spare CPU_STATE_12 = 12 + #: Spare CPU_STATE_13 = 13 + #: Spare CPU_STATE_14 = 14 + #: Idle (SARK stub) IDLE = 15 def __new__(cls, value, doc=""): diff --git a/spinnman/model/enums/diagnostic_filter_default_routing_status.py b/spinnman/model/enums/diagnostic_filter_default_routing_status.py index 4585a92c4..b74cb7636 100644 --- a/spinnman/model/enums/diagnostic_filter_default_routing_status.py +++ b/spinnman/model/enums/diagnostic_filter_default_routing_status.py @@ -1,26 +1,30 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class DiagnosticFilterDefaultRoutingStatus(Enum): - """ Default routing flags for the diagnostic filters. - Note that only one has to match for the counter to be incremented """ + Default routing flags for the diagnostic filters. + + .. note:: + Only one has to match for the counter to be incremented. + """ + #: Packet is to be default routed DEFAULT_ROUTED = (0, "Packet is to be default routed") + #: Packet is not to be default routed NON_DEFAULT_ROUTED = (1, "Packet is not to be default routed") def __new__(cls, value, doc=""): diff --git a/spinnman/model/enums/diagnostic_filter_destination.py b/spinnman/model/enums/diagnostic_filter_destination.py index 3e397fb08..ef7b5b1ed 100644 --- a/spinnman/model/enums/diagnostic_filter_destination.py +++ b/spinnman/model/enums/diagnostic_filter_destination.py @@ -1,33 +1,44 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class DiagnosticFilterDestination(Enum): - """ Destination flags for the diagnostic filters. - Note that only one has to match for the counter to be incremented """ + Destination flags for the diagnostic filters. + + .. note:: + Only one has to match for the counter to be incremented. + """ + #: Destination is to dump the packet DUMP = (0, "Destination is to dump the packet") + #: Destination is a local core (but not the monitor core) LOCAL = (1, "Destination is a local core (but not the monitor core)") + #: Destination is the local monitor core LOCAL_MONITOR = (2, "Destination is the local monitor core") + #: Destination is link 0 LINK_0 = (3, "Destination is link 0") + #: Destination is link 1 LINK_1 = (4, "Destination is link 1") + #: Destination is link 2 LINK_2 = (5, "Destination is link 2") + #: Destination is link 3 LINK_3 = (6, "Destination is link 3") + #: Destination is link 4 LINK_4 = (7, "Destination is link 4") + #: Destination is link 5 LINK_5 = (8, "Destination is link 5") def __new__(cls, value, doc=""): diff --git a/spinnman/model/enums/diagnostic_filter_emergency_routing_status.py b/spinnman/model/enums/diagnostic_filter_emergency_routing_status.py index 5a5b6aa69..df53ad460 100644 --- a/spinnman/model/enums/diagnostic_filter_emergency_routing_status.py +++ b/spinnman/model/enums/diagnostic_filter_emergency_routing_status.py @@ -1,32 +1,41 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class DiagnosticFilterEmergencyRoutingStatus(Enum): - """ Emergency routing status flags for the diagnostic filters. - Note that only one has to match for the counter to be incremented """ + Emergency routing status flags for the diagnostic filters. + + .. note:: + Only one has to match for the counter to be incremented. + """ + #: Packet is not emergency routed NORMAL = (0, "Packet is not emergency routed") + #: Packet is in first hop of emergency route; packet should also have been + #: sent here by normal routing FIRST_STAGE_COMBINED = (1, "Packet is in first hop of emergency route;" " packet should also have been sent here by" " normal routing") + #: Packet is in first hop of emergency route; packet wouldn't have reached + #: this router without emergency routing FIRST_STAGE = (2, "Packet is in first hop of emergency route; packet" " wouldn't have reached this router without emergency" " routing") + #: Packet is in last hop of emergency route and should now return to normal + #: routing SECOND_STAGE = (3, "Packet is in last hop of emergency route and should" " now return to normal routing") diff --git a/spinnman/model/enums/diagnostic_filter_packet_type.py b/spinnman/model/enums/diagnostic_filter_packet_type.py index ee4c3c7ff..6aec6bd12 100644 --- a/spinnman/model/enums/diagnostic_filter_packet_type.py +++ b/spinnman/model/enums/diagnostic_filter_packet_type.py @@ -1,28 +1,34 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class DiagnosticFilterPacketType(Enum): - """ Packet type flags for the diagnostic filters. - Note that only one has to match for the counter to be incremented """ + Packet type flags for the diagnostic filters. + + .. note:: + Only one has to match for the counter to be incremented. + """ + #: Packet is multicast MULTICAST = (0, "Packet is multicast") + #: Packet is point-to-point POINT_TO_POINT = (1, "Packet is point-to-point") + #: Packet is nearest-neighbour NEAREST_NEIGHBOUR = (2, "Packet is nearest-neighbour") + #: Packet is fixed-route FIXED_ROUTE = (3, "Packet is fixed-route") def __new__(cls, value, doc=""): diff --git a/spinnman/model/enums/diagnostic_filter_payload_status.py b/spinnman/model/enums/diagnostic_filter_payload_status.py index 82654e3c2..94746b3f9 100644 --- a/spinnman/model/enums/diagnostic_filter_payload_status.py +++ b/spinnman/model/enums/diagnostic_filter_payload_status.py @@ -1,26 +1,30 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class DiagnosticFilterPayloadStatus(Enum): - """ Payload flags for the diagnostic filters. - Note that only one has to match for the counter to be incremented """ + Payload flags for the diagnostic filters. + + .. note:: + Only one has to match for the counter to be incremented. + """ + #: Packet has a payload WITH_PAYLOAD = (0, "Packet has a payload") + #: Packet doesn't have a payload WITHOUT_PAYLOAD = (1, "Packet doesn't have a payload") def __new__(cls, value, doc=""): diff --git a/spinnman/model/enums/diagnostic_filter_source.py b/spinnman/model/enums/diagnostic_filter_source.py index 24648ef89..4b6697199 100644 --- a/spinnman/model/enums/diagnostic_filter_source.py +++ b/spinnman/model/enums/diagnostic_filter_source.py @@ -1,26 +1,30 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class DiagnosticFilterSource(Enum): - """ Source flags for the diagnostic filters. - Note that only one has to match for the counter to be incremented """ + Source flags for the diagnostic filters. + + .. note:: + Only one has to match for the counter to be incremented. + """ + #: Source is a local core LOCAL = (0, "Source is a local core") + #: Source is not a local core NON_LOCAL = (1, "Source is not a local core") def __new__(cls, value, doc=""): diff --git a/spinnman/model/enums/executable_type.py b/spinnman/model/enums/executable_type.py new file mode 100644 index 000000000..0481e237f --- /dev/null +++ b/spinnman/model/enums/executable_type.py @@ -0,0 +1,84 @@ +# Copyright (c) 2017 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from enum import Enum +from spinnman.model.enums import CPUState + + +class ExecutableType(Enum): + """ + The different types of executable from the perspective of how they + are started and controlled. + """ + + #: Runs immediately without waiting for barrier and then exits. + RUNNING = ( + 0, + [CPUState.RUNNING], + [CPUState.FINISHED], + False, + "Runs immediately without waiting for barrier and then exits") + #: Calls ``spin1_start(SYNC_WAIT)`` and then eventually ``spin1_exit()``. + SYNC = ( + 1, + [CPUState.SYNC0], + [CPUState.FINISHED], + False, + "Calls spin1_start(SYNC_WAIT) and then eventually spin1_exit()") + #: Calls ``simulation_run()`` and ``simulation_exit()`` / + #: ``simulation_handle_pause_resume()``. + USES_SIMULATION_INTERFACE = ( + 2, + [CPUState.SYNC0, CPUState.SYNC1, CPUState.PAUSED, CPUState.READY], + [CPUState.READY], + True, + "Calls simulation_run() and simulation_exit() / " + "simulation_handle_pause_resume()") + #: Situation where there user has supplied no application but for some + #: reason still wants to run. + NO_APPLICATION = ( + 3, + [], + [], + True, + "Situation where there user has supplied no application but for " + "some reason still wants to run") + #: Runs immediately without waiting for barrier and never ends. + SYSTEM = ( + 4, + [CPUState.RUNNING], + [CPUState.RUNNING], + True, + "Runs immediately without waiting for barrier and never ends") + + def __new__(cls, value, start_state, end_state, + supports_auto_pause_and_resume, doc=""): + # pylint: disable=protected-access, too-many-arguments + obj = object.__new__(cls) + obj._value_ = value + obj.start_state = start_state + obj.end_state = end_state + obj.supports_auto_pause_and_resume = supports_auto_pause_and_resume + obj.__doc__ = doc + return obj + + def __init__(self, value, start_state, end_state, + supports_auto_pause_and_resume, doc=""): + # pylint: disable=too-many-arguments + self._value_ = value + self.__doc__ = doc + self.start_state = start_state + self.end_state = end_state + self.supports_auto_pause_and_resume = supports_auto_pause_and_resume + self.__doc__ = doc diff --git a/spinnman/model/enums/mailbox_command.py b/spinnman/model/enums/mailbox_command.py index db575168e..c0055ef36 100644 --- a/spinnman/model/enums/mailbox_command.py +++ b/spinnman/model/enums/mailbox_command.py @@ -1,29 +1,33 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class MailboxCommand(Enum): - """ Commands sent between an application and the monitor processor """ - + Commands sent between an application and the monitor processor. + """ + #: The mailbox is idle SHM_IDLE = (0, "The mailbox is idle") + #: The mailbox contains an SDP message SHM_MSG = (1, "The mailbox contains an SDP message") + #: The mailbox contains a no-operation (used for watchdog) SHM_NOP = (2, "The mailbox contains a non-operation") + #: The mailbox contains a signal SHM_SIGNAL = (3, "The mailbox contains a signal") + #: The mailbox contains a command SHM_CMD = (4, "The mailbox contains a command") def __new__(cls, value, doc=""): diff --git a/spinnman/model/enums/p2p_table_route.py b/spinnman/model/enums/p2p_table_route.py index 5557efae1..593210baa 100644 --- a/spinnman/model/enums/p2p_table_route.py +++ b/spinnman/model/enums/p2p_table_route.py @@ -1,23 +1,23 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2016 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class P2PTableRoute(Enum): - """ P2P Routing table routes + """ + P2P Routing table routes. """ EAST = 0b000 NORTH_EAST = 0b001 @@ -25,7 +25,9 @@ class P2PTableRoute(Enum): WEST = 0b011 SOUTH_WEST = 0b100 SOUTH = 0b101 + #: No route to this chip NONE = (0b110, "No route to this chip") + #: Route to the monitor on the current chip MONITOR = (0b111, "Route to the monitor on the current chip") def __new__(cls, value, doc=""): diff --git a/spinnman/model/enums/router_error.py b/spinnman/model/enums/router_error.py new file mode 100644 index 000000000..fc6e314ed --- /dev/null +++ b/spinnman/model/enums/router_error.py @@ -0,0 +1,41 @@ +# Copyright (c) 2017 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from enum import Enum + + +class RouterError(Enum): + """ + Router error flags. + """ + #: Error packet detected (0x80000000) + ERROR = (0x80000000, "Error packet detected") + #: More than one error packet detected (0x40000000) + OVERFLOW = (0x40000000, "More than one error packet detected") + #: Parity Error (0x20000000) + PARITY = (0x20000000, "Parity Error") + #: Framing Error (0x10000000) + FRAMING = (0x10000000, "Framing Error") + #: Timestamp Error (0x08000000) + TIMESTAMP = (0x08000000, "Timestamp Error") + + def __new__(cls, value, doc=""): + # pylint: disable=protected-access, unused-argument + obj = object.__new__(cls) + obj._value_ = value + return obj + + def __init__(self, value, doc=""): + self._value_ = value + self.__doc__ = doc diff --git a/spinnman/model/enums/run_time_error.py b/spinnman/model/enums/run_time_error.py index 31d55b6e6..134af338a 100644 --- a/spinnman/model/enums/run_time_error.py +++ b/spinnman/model/enums/run_time_error.py @@ -1,44 +1,65 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from enum import Enum class RunTimeError(Enum): - """ SARK Run time errors """ + SARK Run time errors. + """ + #: No error NONE = 0 + #: Branch through zero RESET = 1 + #: Undefined instruction UNDEF = 2 + #: Undefined SVC or no handler SVC = 3 + #: Prefetch abort PABT = 4 + #: Data abort DABT = 5 + #: Unhandled IRQ IRQ = 6 + #: Unhandled FIQ FIQ = 7 + #: Unconfigured VIC vector VIC = 8 + #: Generic user abort ABORT = 9 + #: "malloc" failure MALLOC = 10 + #: Divide by zero DIVBY0 = 11 + #: Event startup failure EVENT = 12 + #: Fatal SW error SWERR = 13 + #: Failed to allocate IO buffer IOBUF = 14 + #: Bad event enable ENABLE = 15 + #: Generic null pointer error NULL = 16 + #: Pkt startup failure PKT = 17 + #: Timer startup failure TIMER = 18 + #: API startup failure API = 19 + #: SW version conflict SARK_VERSRION_INCORRECT = 20 def __new__(cls, value, doc=""): diff --git a/spinnman/model/enums/sdp_ports.py b/spinnman/model/enums/sdp_ports.py new file mode 100644 index 000000000..078501dd7 --- /dev/null +++ b/spinnman/model/enums/sdp_ports.py @@ -0,0 +1,37 @@ +# Copyright (c) 2014 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from enum import Enum + + +class SDP_PORTS(Enum): + """ + SDP port handling output buffering data streaming. + """ + + #: Command port for the buffered in functionality. + INPUT_BUFFERING_SDP_PORT = 1 + #: Command port for the buffered out functionality. + OUTPUT_BUFFERING_SDP_PORT = 2 + #: Command port for resetting runtime, etc. + #: See :py:class:`SDP_RUNNING_MESSAGE_CODES` + RUNNING_COMMAND_SDP_PORT = 3 + #: Extra monitor core reinjection control protocol. + #: See :py:class:`ReinjectorSCPCommands` + EXTRA_MONITOR_CORE_REINJECTION = 4 + #: Extra monitor core outbound data transfer protocol + EXTRA_MONITOR_CORE_DATA_SPEED_UP = 5 + #: Extra monitor core inbound data transfer protocol + #: See :py:class:`SpeedupInSCPCommands` + EXTRA_MONITOR_CORE_DATA_IN_SPEED_UP = 6 diff --git a/spinnman/model/enums/sdp_running_message_codes.py b/spinnman/model/enums/sdp_running_message_codes.py new file mode 100644 index 000000000..708381d9a --- /dev/null +++ b/spinnman/model/enums/sdp_running_message_codes.py @@ -0,0 +1,25 @@ +# Copyright (c) 2014 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from enum import Enum + + +class SDP_RUNNING_MESSAGE_CODES(Enum): + """ + Codes for sending control messages to spin1_api. + """ + SDP_STOP_ID_CODE = 6 + SDP_NEW_RUNTIME_ID_CODE = 7 + SDP_UPDATE_PROVENCE_REGION_AND_EXIT = 8 + SDP_CLEAR_IOBUF_CODE = 9 diff --git a/spinnman/model/executable_targets.py b/spinnman/model/executable_targets.py index 546aa63b4..5a3c88182 100644 --- a/spinnman/model/executable_targets.py +++ b/spinnman/model/executable_targets.py @@ -1,97 +1,167 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2016 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from collections import defaultdict +from spinn_utilities.ordered_set import OrderedSet from spinn_machine import CoreSubsets from spinnman.exceptions import SpinnmanInvalidParameterException class ExecutableTargets(object): - """ Encapsulate the binaries and cores on which to execute them. + """ + Encapsulate the binaries and cores on which to execute them. """ __slots__ = [ "_all_core_subsets", "_targets", - "_total_processors"] + "_total_processors", + "_binary_type_map"] def __init__(self): self._targets = dict() self._total_processors = 0 self._all_core_subsets = CoreSubsets() + self._binary_type_map = defaultdict(OrderedSet) - def add_subsets(self, binary, subsets): - """ Add core subsets to a binary + def add_subsets(self, binary, subsets, executable_type=None): + """ + Add core subsets to a binary. - :param binary: the path to the binary needed to be executed - :param subsets: \ + :param str binary: the path to the binary needed to be executed + :param ~spinn_machine.CoreSubsets subsets: the subset of cores that the binary needs to be loaded on - :return: + :param ~spinnman.model.enum.ExecutableType executable_type: + The type of this executable. + ``None`` means don't record it. """ - for subset in subsets.core_subsets: - for p in subset.processor_ids: - self.add_processor(binary, subset.x, subset.y, p) + try: + for subset in subsets.core_subsets: + for p in subset.processor_ids: + self.add_processor(binary, subset.x, subset.y, p) + except AttributeError: + if subsets is not None: + raise + if executable_type is not None: + self._binary_type_map[executable_type].add(binary) - def add_processor(self, binary, chip_x, chip_y, chip_p): - """ Add a processor to the executable targets + def add_processor( + self, binary, chip_x, chip_y, chip_p, executable_type=None): + """ + Add a processor to the executable targets - :param binary: the binary path for executable - :param chip_x: the coordinate on the machine in terms of x for the chip - :param chip_y: the coordinate on the machine in terms of y for the chip - :param chip_p: the processor ID to place this executable on - :return: + :param str binary: the binary path for executable + :param int chip_x: + the coordinate on the machine in terms of x for the chip + :param int chip_y: + the coordinate on the machine in terms of y for the chip + :param int chip_p: the processor ID to place this executable on + :param ~spinnman.model.enum.ExecutableType executable_type: + the executable type for locating n cores of """ if self.known(binary, chip_x, chip_y, chip_p): return if binary not in self._targets: self._targets[binary] = CoreSubsets() + if executable_type is not None: + self._binary_type_map[executable_type].add(binary) self._targets[binary].add_processor(chip_x, chip_y, chip_p) self._all_core_subsets.add_processor(chip_x, chip_y, chip_p) self._total_processors += 1 + def get_n_cores_for_executable_type(self, executable_type): + """ + Get the number of cores that the executable type is using. + + :param ~spinnman.model.enum.ExecutableType executable_type: + :return: the number of cores using this executable type + :rtype: int + """ + return sum( + len(self.get_cores_for_binary(aplx)) + for aplx in self._binary_type_map[executable_type]) + + def get_binaries_of_executable_type(self, executable_type): + """ + Get the binaries of a given a executable type. + + :param ~spinnman.model.enum.ExecutableType executable_type: + the executable type enum value + :return: iterable of binaries with that executable type + :rtype: iterable(str) + """ + return self._binary_type_map[executable_type] + + def executable_types_in_binary_set(self): + """ + Get the executable types in the set of binaries. + + :return: iterable of the executable types in this binary set. + :rtype: + iterable(~spinnman.model.enum.ExecutableType) + """ + return self._binary_type_map.keys() + def get_cores_for_binary(self, binary): - """ Get the cores that a binary is to run on + """ + Get the cores that a binary is to run on. - :param binary: The binary to find the cores for + :param str binary: The binary to find the cores for """ return self._targets.get(binary) @property def binaries(self): - """ The binaries of the executables + """ + The binaries of the executables. + + :rtype: iterable(str) """ return self._targets.keys() @property def total_processors(self): - """ The total number of cores to be loaded + """ + The total number of cores to be loaded. + + :rtype: int """ return self._total_processors @property def all_core_subsets(self): - """ All the core subsets for all the binaries + """ + All the core subsets for all the binaries. + + :rtype: ~spinn_machine.CoreSubsets """ return self._all_core_subsets def known(self, binary, chip_x, chip_y, chip_p): - if self._all_core_subsets.is_core(chip_x, chip_y, chip_p): - # OK if and only if the chip is in this binary already - if binary in self._targets: - if self._targets[binary].is_core(chip_x, chip_y, chip_p): - return True - parameter = "x:{} y:{} p:{}".format(chip_x, chip_y, chip_p) - problem = "Already associated with a different binary" - raise SpinnmanInvalidParameterException(parameter, binary, problem) - else: + """ + :param str binary: + :param int chip_x: + :param int chip_y: + :param int chip_p: + :rtype: bool + """ + if not self._all_core_subsets.is_core(chip_x, chip_y, chip_p): return False + # OK if and only if the chip is in this binary already + if binary in self._targets: + if self._targets[binary].is_core(chip_x, chip_y, chip_p): + return True + + raise SpinnmanInvalidParameterException( + f"x:{chip_x} y:{chip_y} p:{chip_p}", binary, + "Already associated with a different binary") diff --git a/spinnman/model/heap_element.py b/spinnman/model/heap_element.py index 3a7236c7c..3261ffc57 100644 --- a/spinnman/model/heap_element.py +++ b/spinnman/model/heap_element.py @@ -1,21 +1,21 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2016 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. class HeapElement(object): - """ An element of one of the heaps on SpiNNaker. + """ + An element of one of the heaps on SpiNNaker. """ __slots__ = [ @@ -33,9 +33,9 @@ class HeapElement(object): def __init__(self, block_address, next_address, free): """ - :param block_address: The address of this element on the heap - :param next_address: The address of the next element on the heap - :param free: The "free" element of the block as read from the heap + :param int block_address: The address of this element on the heap + :param int next_address: The address of the next element on the heap + :param int free: The "free" element of the block as read from the heap """ self._block_address = block_address self._next_address = next_address @@ -48,37 +48,55 @@ def __init__(self, block_address, next_address, free): @property def block_address(self): - """ The address of the block + """ + The address of the block. + + :rtype: int """ return self._block_address @property def next_address(self): - """ The address of the next block, or 0 if none + """ + The address of the next block, or 0 if none. + + :rtype: int """ return self._next_address @property def size(self): - """ The usable size of this block (not including the header) + """ + The usable size of this block (not including the header). + + :rtype: int """ return self._next_address - self._block_address - 8 @property def is_free(self): - """ True if this block is a free block, False otherwise + """ + Whether this block is a free block. + + :rtype: bool """ return self._is_free @property def tag(self): - """ The tag of the block if allocated, or None if not + """ + The tag of the block if allocated, or `None` if not. + + :rtype: int or None """ return self._tag @property def app_id(self): - """ The application ID of the block if allocated, or None if not + """ + The application ID of the block if allocated, or `None` if not. + + :rtype: int or None """ return self._app_id diff --git a/spinnman/model/io_buffer.py b/spinnman/model/io_buffer.py index b7439f192..70946fee7 100644 --- a/spinnman/model/io_buffer.py +++ b/spinnman/model/io_buffer.py @@ -1,21 +1,21 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. class IOBuffer(object): - """ The contents of IOBUF for a core + """ + The contents of IOBUF for a core. """ __slots__ = [ "_iobuf", @@ -23,15 +23,10 @@ class IOBuffer(object): def __init__(self, x, y, p, iobuf): """ - :param x: The x-coordinate of a chip - :type x: int - :param y: The y-coordinate of a chip - :type y: int - :param p: The p-coordinate of a chip - :type p: int - :param iobuf: The contents of the buffer for the chip - :type iobuf: str - :raise None: No known exceptions are raised + :param int x: The x-coordinate of a chip + :param int y: The y-coordinate of a chip + :param int p: The p-coordinate of a chip + :param str iobuf: The contents of the buffer for the chip """ self._x = x self._y = y @@ -40,36 +35,36 @@ def __init__(self, x, y, p, iobuf): @property def x(self): - """ The x-coordinate of the chip containing the core + """ + The X-coordinate of the chip containing the core. - :return: The x-coordinate of the chip :rtype: int """ return self._x @property def y(self): - """ The y-coordinate of the chip containing the core + """ + The Y-coordinate of the chip containing the core. - :return: The y-coordinate of the chip :rtype: int """ return self._y @property def p(self): - """ The ID of the core on the chip + """ + The ID of the core on the chip. - :return: The ID of the core :rtype: int """ return self._p @property def iobuf(self): - """ The contents of the buffer + """ + The contents of the buffer. - :return: The contents of the buffer :rtype: str """ return self._iobuf @@ -77,6 +72,5 @@ def iobuf(self): def __str__(self): value = "" for line in self._iobuf.split("\n"): - value += "{}:{}:{:2n}: {}\n".format( - self._x, self._y, self._p, line) + value += f"{self._x}:{self._y}:{self._p:2n}: {line}\n" return value[:-1] diff --git a/spinnman/model/machine_dimensions.py b/spinnman/model/machine_dimensions.py index 5e8d5d523..ee6833506 100644 --- a/spinnman/model/machine_dimensions.py +++ b/spinnman/model/machine_dimensions.py @@ -1,21 +1,21 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. class MachineDimensions(object): - """ Represents the size of a machine in chips + """ + Represents the size of a machine in chips. """ __slots__ = [ "_height", @@ -23,29 +23,26 @@ class MachineDimensions(object): def __init__(self, width, height): """ - :param width: The width of the machine in chips - :type width: int - :param height: The height of the machine in chips - :type height: int - :raise None: No known exceptions are raised + :param int width: The width of the machine in chips + :param int height: The height of the machine in chips """ self._width = width self._height = height @property def width(self): - """ The width of the machine in chips + """ + The width of the machine in chips. - :return: The width :rtype: int """ return self._width @property def height(self): - """ The height of the machine in chips + """ + The height of the machine in chips. - :return: The height :rtype: int """ return self._height diff --git a/spinnman/model/p2p_table.py b/spinnman/model/p2p_table.py index a7e837d8c..d74ede746 100644 --- a/spinnman/model/p2p_table.py +++ b/spinnman/model/p2p_table.py @@ -1,28 +1,26 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2016 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct -from past.builtins import xrange -from six import iterkeys from spinnman.model.enums import P2PTableRoute _ONE_WORD = struct.Struct(". +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinnman.exceptions import SpinnmanInvalidParameterException from spinnman.constants import ROUTER_REGISTER_REGISTERS +from spinnman.model.enums.router_error import RouterError class RouterDiagnostics(object): - """ Represents a set of diagnostic information available from a chip\ - router. + """ + Represents a set of diagnostic information available from a chip router. """ __slots__ = [ "_error_status", @@ -30,13 +30,11 @@ class RouterDiagnostics(object): def __init__(self, control_register, error_status, register_values): """ - :param control_register: The value of the control register - :type control_register: int - :param error_status: The value of the error_status - :type error_status: int - :param register_values: The values of the 16 router registers - :type register_values: iterable(int) - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + :param int control_register: The value of the control register + :param int error_status: The value of the error_status + :param list(int) register_values: + The values of the 16 router registers + :raise SpinnmanInvalidParameterException: If the number of register values is not 16 """ if len(register_values) != 16: @@ -55,184 +53,207 @@ def __init__(self, control_register, error_status, register_values): @property def mon(self): - """ The "mon" part of the control register + """ + The "mon" part of the control register. - :return: The mon bits :rtype: int """ return self._mon @property def wait_1(self): - """ The "wait_1" part of the control register + """ + The "wait_1" part of the control register. - :return: The wait_1 bits :rtype: int """ return self._wait_1 @property def wait_2(self): - """ The "wait_2" part of the control register + """ + The "wait_2" part of the control register. - :return: The wait_2 bits :rtype: int """ return self._wait_2 @property def error_status(self): - """ The error status + """ + The error status. - :return: The error status :rtype: int """ return self._error_status + @property + def error_count(self): + """ + The count of errors. + + :rtype: int + """ + return self._error_status & 0xFF + + @property + def errors_set(self): + """ + A list of errors that have been detected. + + :rtype: list(RouterError) + """ + return [ + error for error in RouterError if error.value & self._error_status] + @property def n_local_multicast_packets(self): - """ The number of multicast packets received from local cores. + """ + The number of multicast packets received from local cores. - :return: The number of packets :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.LOC_MC.value] @property def n_external_multicast_packets(self): - """ The number of multicast packets received from external links. + """ + The number of multicast packets received from external links. - :return: The number of packets :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.EXT_MC.value] @property def n_dropped_multicast_packets(self): - """ The number of multicast packets received that were dropped. + """ + The number of multicast packets received that were dropped. - :return: The number of packets :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.DUMP_MC.value] @property def n_local_peer_to_peer_packets(self): - """ The number of peer-to-peer packets received from local cores. + """ + The number of peer-to-peer packets received from local cores. - :return: The number of packets :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.LOC_PP.value] @property def n_external_peer_to_peer_packets(self): - """ The number of peer-to-peer packets received from external links. + """ + The number of peer-to-peer packets received from external links. - :return: The number of packets :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.EXT_PP.value] @property def n_dropped_peer_to_peer_packets(self): - """ The number of peer-to-peer packets received that were dropped. + """ + The number of peer-to-peer packets received that were dropped. - :return: The number of packets :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.DUMP_PP.value] @property def n_local_nearest_neighbour_packets(self): - """ The number of nearest-neighbour packets received from local cores. + """ + The number of nearest-neighbour packets received from local cores. - :return: The number of packets :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.LOC_NN.value] @property def n_external_nearest_neighbour_packets(self): - """ The number of nearest-neighbour packets received from external\ - links. + """ + The number of nearest-neighbour packets received from external links. - :return: The number of packets :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.EXT_NN.value] @property def n_dropped_nearest_neighbour_packets(self): - """ The number of nearest-neighbour packets received that were dropped. + """ + The number of nearest-neighbour packets received that were dropped. - :return: The number of packets :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.DUMP_NN.value] @property def n_local_fixed_route_packets(self): - """ The number of fixed-route packets received from local cores. + """ + The number of fixed-route packets received from local cores. - :return: The number of packets :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.LOC_FR.value] @property def n_external_fixed_route_packets(self): - """ The number of fixed-route packets received from external links. + """ + The number of fixed-route packets received from external links. - :return: The number of packets :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.EXT_FR.value] @property def n_dropped_fixed_route_packets(self): - """ The number of fixed-route packets received that were dropped. + """ + The number of fixed-route packets received that were dropped. - :return: The number of packets :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.DUMP_FR.value] @property def user_0(self): - """ The data gained from the user 0 router diagnostic filter. + """ + The number of packets counted by the user 0 router diagnostic filter. - :return: the number of packets captured by this filter. + :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.USER_0.value] @property def user_1(self): - """ The data gained from the user 1 router diagnostic filter + """ + The number of packets counted by the user 1 router diagnostic filter. - :return: the number of packets captured by this filter. + :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.USER_1.value] @property def user_2(self): - """ The data gained from the user 2 router diagnostic filter. + """ + The number of packets counted by the user 2 router diagnostic filter. - :return: the number of packets captured by this filter. + :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.USER_2.value] @property def user_3(self): - """ The data gained from the user 3 router diagnostic filter. + """ + The number of packets counted by the user 3 router diagnostic filter. - :return: the number of packets captured by this filter. + :rtype: int """ return self._register_values[ROUTER_REGISTER_REGISTERS.USER_3.value] @property def user_registers(self): - """ The values in the user control registers. + """ + The values in the user control registers. :return: An array of 4 values :rtype: list(int) @@ -243,9 +264,9 @@ def user_registers(self): @property def registers(self): - """ The values in all of the registers. Can be used to directly\ - access the registers if they have been programmed to give\ - different values + """ + The values in all of the registers. Can be used to directly access + the registers if they have been programmed to give different values. :return: An array of 16 values :rtype: array(int) diff --git a/spinnman/model/version_info.py b/spinnman/model/version_info.py index 3e149cbc1..ebcf798b6 100644 --- a/spinnman/model/version_info.py +++ b/spinnman/model/version_info.py @@ -1,29 +1,28 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import re import struct from time import localtime, asctime -from six import raise_from from spinnman.exceptions import SpinnmanInvalidParameterException _VERSION_PATTERN = struct.Struct(". +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .abstract_multi_connection_process import AbstractMultiConnectionProcess +from .abstract_multi_connection_process_connection_selector import ( + AbstractMultiConnectionProcessConnectionSelector) +from .application_copy_run_process import ApplicationCopyRunProcess from .application_run_process import ApplicationRunProcess -from .de_alloc_sdram_process import DeAllocSDRAMProcess -from .fill_process import FillDataType -from .fill_process import FillProcess + +from .fixed_connection_selector import FixedConnectionSelector from .get_heap_process import GetHeapProcess from .get_cpu_info_process import GetCPUInfoProcess +from .get_exclude_cpu_info_process import GetExcludeCPUInfoProcess +from .get_include_cpu_info_process import GetIncludeCPUInfoProcess from .get_machine_process import GetMachineProcess from .get_routes_process import GetMultiCastRoutesProcess from .get_tags_process import GetTagsProcess @@ -36,16 +39,19 @@ from .read_router_diagnostics_process import ReadRouterDiagnosticsProcess from .round_robin_connection_selector import RoundRobinConnectionSelector from .send_single_command_process import SendSingleCommandProcess -from .write_memory_flood_process import WriteMemoryFloodProcess from .write_memory_process import WriteMemoryProcess -__all__ = ["AbstractMultiConnectionProcess", "ApplicationRunProcess", - "DeAllocSDRAMProcess", "GetCPUInfoProcess", "GetHeapProcess", +__all__ = ["AbstractMultiConnectionProcessConnectionSelector", + "FixedConnectionSelector", "MostDirectConnectionSelector", + "RoundRobinConnectionSelector", + "AbstractMultiConnectionProcess", + "ApplicationRunProcess", "ApplicationCopyRunProcess", + "GetCPUInfoProcess", + "GetExcludeCPUInfoProcess", "GetIncludeCPUInfoProcess", + "GetHeapProcess", "GetMachineProcess", "GetMultiCastRoutesProcess", "GetTagsProcess", - "GetVersionProcess", "FillDataType", "FillProcess", - "LoadFixedRouteRoutingEntryProcess", "LoadMultiCastRoutesProcess", - "MallocSDRAMProcess", "MostDirectConnectionSelector", + "GetVersionProcess", "LoadFixedRouteRoutingEntryProcess", + "LoadMultiCastRoutesProcess", "MallocSDRAMProcess", "ReadFixedRouteRoutingEntryProcess", "ReadIOBufProcess", "ReadMemoryProcess", "ReadRouterDiagnosticsProcess", - "RoundRobinConnectionSelector", "SendSingleCommandProcess", - "WriteMemoryFloodProcess", "WriteMemoryProcess"] + "SendSingleCommandProcess", "WriteMemoryProcess"] diff --git a/spinnman/processes/abstract_multi_connection_process.py b/spinnman/processes/abstract_multi_connection_process.py index 0e4a719af..ddc0d53bc 100644 --- a/spinnman/processes/abstract_multi_connection_process.py +++ b/spinnman/processes/abstract_multi_connection_process.py @@ -1,51 +1,80 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -from six import itervalues -from .abstract_process import AbstractProcess +import logging +import sys +from spinn_utilities.log import FormatAdapter from spinnman.connections import SCPRequestPipeLine from spinnman.constants import SCP_TIMEOUT, N_RETRIES +from spinnman.exceptions import ( + SpinnmanGenericProcessException, SpinnmanGroupedProcessException) +logger = FormatAdapter(logging.getLogger(__name__)) -class AbstractMultiConnectionProcess(AbstractProcess): - """ A process that uses multiple connections in communication. + +class AbstractMultiConnectionProcess: + """ + A process for talking to SpiNNaker efficiently that uses multiple + connections in communication if relevant. """ __slots__ = [ + "_error_requests", + "_exceptions", + "_tracebacks", + "_connections", "_intermediate_channel_waits", "_n_channels", "_n_retries", - "_next_connection_selector", + "_conn_selector", "_scp_request_pipelines", "_timeout"] def __init__(self, next_connection_selector, n_retries=N_RETRIES, timeout=SCP_TIMEOUT, n_channels=8, intermediate_channel_waits=7): - super(AbstractMultiConnectionProcess, self).__init__() + """ + :param next_connection_selector: + How to choose the connection. + :type next_connection_selector: + AbstractMultiConnectionProcessConnectionSelector + :param int n_retries: + The number of retries of a message to use. Passed to + :py:class:`SCPRequestPipeLine` + :param float timeout: + The timeout, in seconds. Passed to :py:class:`SCPRequestPipeLine` + :param int n_channels: + The maximum number of channels to use when talking to a particular + SCAMP instance. Passed to :py:class:`SCPRequestPipeLine` + :param int intermediate_channel_waits: + The maximum number of outstanding message/reply pairs to have on a + particular connection. Passed to :py:class:`SCPRequestPipeLine` + """ + self._exceptions = [] + self._tracebacks = [] + self._error_requests = [] + self._connections = [] self._scp_request_pipelines = dict() self._n_retries = n_retries self._timeout = timeout self._n_channels = n_channels self._intermediate_channel_waits = intermediate_channel_waits - self._next_connection_selector = next_connection_selector + self._conn_selector = next_connection_selector def _send_request(self, request, callback=None, error_callback=None): if error_callback is None: error_callback = self._receive_error - connection = self._next_connection_selector.get_next_connection( - request) + connection = self._conn_selector.get_next_connection(request) if connection not in self._scp_request_pipelines: scp_request_set = SCPRequestPipeLine( connection, n_retries=self._n_retries, @@ -56,6 +85,53 @@ def _send_request(self, request, callback=None, error_callback=None): self._scp_request_pipelines[connection].send_request( request, callback, error_callback) + def _receive_error(self, request, exception, tb, connection): + self._error_requests.append(request) + self._exceptions.append(exception) + self._tracebacks.append(tb) + self._connections.append(connection) + + def is_error(self): + return bool(self._exceptions) + def _finish(self): - for request_pipeline in itervalues(self._scp_request_pipelines): + for request_pipeline in self._scp_request_pipelines.values(): request_pipeline.finish() + + @property + def connection_selector(self): + """ + The connection selector of the process. + + :rtype: AbstractMultiConnectionProcessConnectionSelector + """ + return self._conn_selector + + def check_for_error(self, print_exception=False): + if len(self._exceptions) == 1: + exc_info = sys.exc_info() + sdp_header = self._error_requests[0].sdp_header + phys_p = sdp_header.get_physical_cpu_id() + + if print_exception: + connection = self._connections[0] + logger.error( + "failure in request to board {} with ethernet chip " + "({}, {}) for chip ({}, {}, {}({}))", + connection.remote_ip_address, connection.chip_x, + connection.chip_y, sdp_header.destination_chip_x, + sdp_header.destination_chip_y, sdp_header.destination_cpu, + phys_p) + + raise SpinnmanGenericProcessException( + self._exceptions[0], exc_info[2], + sdp_header.destination_chip_x, + sdp_header.destination_chip_y, sdp_header.destination_cpu, + phys_p, self._tracebacks[0]) + elif self._exceptions: + ex = SpinnmanGroupedProcessException( + self._error_requests, self._exceptions, self._tracebacks, + self._connections) + if print_exception: + logger.error("{}", str(ex)) + raise ex diff --git a/spinnman/processes/abstract_multi_connection_process_connection_selector.py b/spinnman/processes/abstract_multi_connection_process_connection_selector.py index a5830059a..20ddccc84 100644 --- a/spinnman/processes/abstract_multi_connection_process_connection_selector.py +++ b/spinnman/processes/abstract_multi_connection_process_connection_selector.py @@ -1,41 +1,34 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -from six import add_metaclass -from spinn_utilities.abstract_base import AbstractBase, abstractmethod +from spinn_utilities.abstract_base import ( + AbstractBase, abstractmethod) -@add_metaclass(AbstractBase) -class AbstractMultiConnectionProcessConnectionSelector(object): - """ A connection selector for multi-connection processes +class AbstractMultiConnectionProcessConnectionSelector( + object, metaclass=AbstractBase): + """ + A connection selector for multi-connection processes. """ __slots__ = [] - # connections will be used when worked out how - - @abstractmethod - def __init__(self, connections): - """ - :param connections: The connections to be used - """ - @abstractmethod def get_next_connection(self, message): - """ Get the index of the next connection for the process from a list\ - of connections. + """ + Get the index of the next connection for the process from a list + of connections. - :param message: The SCP message to be sent - :rtype: int + :param AbstractSCPRequest message: The SCP message to be sent + :rtype: SCAMPConnection """ diff --git a/spinnman/processes/abstract_process.py b/spinnman/processes/abstract_process.py deleted file mode 100644 index aa01ef1c1..000000000 --- a/spinnman/processes/abstract_process.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from __future__ import print_function -import logging -import sys -from spinnman.exceptions import ( - SpinnmanGenericProcessException, SpinnmanGroupedProcessException) - -logger = logging.getLogger(__name__) - - -class AbstractProcess(object): - """ An abstract process for talking to SpiNNaker efficiently. - """ - __slots__ = [ - "_error_requests", - "_exceptions", - "_tracebacks"] - - def __init__(self): - self._exceptions = [] - self._tracebacks = [] - self._error_requests = [] - - def _receive_error(self, request, exception, tb): - self._error_requests.append(request) - self._exceptions.append(exception) - self._tracebacks.append(tb) - - def is_error(self): - return bool(self._exceptions) - - def check_for_error(self, print_exception=False): - if len(self._exceptions) == 1: - exc_info = sys.exc_info() - sdp_header = self._error_requests[0].sdp_header - - if print_exception: - logger.error("failure in request to (%d, %d, %d)", - sdp_header.destination_chip_x, - sdp_header.destination_chip_y, - sdp_header.destination_cpu, - exc_info=(Exception, self._exceptions, - self._tracebacks)) - - raise SpinnmanGenericProcessException( - self._exceptions[0], exc_info[2], - sdp_header.destination_chip_x, - sdp_header.destination_chip_y, sdp_header.destination_cpu, - self._tracebacks[0]) - elif self._exceptions: - ex = SpinnmanGroupedProcessException( - self._error_requests, self._exceptions, self._tracebacks) - if print_exception: - logger.error("%s", str(ex)) - raise ex diff --git a/spinnman/processes/abstract_single_connection_process.py b/spinnman/processes/abstract_single_connection_process.py deleted file mode 100644 index 10716ccc5..000000000 --- a/spinnman/processes/abstract_single_connection_process.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from .abstract_process import AbstractProcess -from spinnman.connections import SCPRequestPipeLine -from spinnman.constants import N_RETRIES - - -class AbstractSingleConnectionProcess(AbstractProcess): - """ A process that uses a single connection in communication. - """ - __slots__ = [ - "_connection_selector", - "_scp_request_pipeline", - "_n_retries"] - - def __init__(self, connection_selector, n_retries=N_RETRIES): - super(AbstractSingleConnectionProcess, self).__init__() - self._scp_request_pipeline = None - self._connection_selector = connection_selector - self._n_retries = n_retries - - def _send_request(self, request, callback=None, error_callback=None): - if error_callback is None: - error_callback = self._receive_error - - # If no pipe line built yet, build one on the connection selected for - # it - if self._scp_request_pipeline is None: - self._scp_request_pipeline = SCPRequestPipeLine( - self._connection_selector.get_next_connection(request), - n_retries=self._n_retries) - - # send request - self._scp_request_pipeline.send_request( - request, callback, error_callback) - - def _finish(self): - self._scp_request_pipeline.finish() diff --git a/spinnman/processes/application_copy_run_process.py b/spinnman/processes/application_copy_run_process.py new file mode 100644 index 000000000..b9ee64ef0 --- /dev/null +++ b/spinnman/processes/application_copy_run_process.py @@ -0,0 +1,91 @@ +# Copyright (c) 2017 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from spinnman.data import SpiNNManDataView +from spinnman.messages.scp.impl import AppCopyRun +from .abstract_multi_connection_process import AbstractMultiConnectionProcess + + +def _on_same_board(chip_1, chip_2): + return (chip_1.nearest_ethernet_x == chip_2.nearest_ethernet_x and + chip_1.nearest_ethernet_y == chip_2.nearest_ethernet_y) + + +def _get_next_chips(chips_done): + """ + Get the chips that are adjacent to the last set of chips, which + haven't yet been loaded. Also returned are the links for each chip, + which gives the link which should be read from to get the data. + + :param set((int,int)) chips_done: + The coordinates of chips that have already been done + :return: A dict of chip coordinates to link to use, Chip + :rtype: dict((int,int), (int, Chip)) + """ + next_chips = dict() + for x, y in chips_done: + chip = SpiNNManDataView.get_chip_at(x, y) + for link in chip.router.links: + chip_coords = (link.destination_x, link.destination_y) + if chip_coords not in chips_done and chip_coords not in next_chips: + next_chip = SpiNNManDataView.get_chip_at(*chip_coords) + opp_link = (link.source_link_id + 3) % 6 + next_chips[chip_coords] = (opp_link, next_chip) + # Only let one thing copy from this chip + break + return next_chips + + +class ApplicationCopyRunProcess(AbstractMultiConnectionProcess): + """ + Process to start a binary on a subset of cores on a subset of chips + of a machine, performed by, on each chip, copying the data from + an adjacent chip and then starting the binary. This goes to each + chip in turn, and so detects failures early on, as well as ensuring + that the copy and execution is done in the case of success i.e. this + ensures that if all commands are successful, the full binary has been + copied and started. + + .. note:: + The binary must have been loaded to the boot chip before this is + called! + """ + __slots__ = [] + + def run(self, size, app_id, core_subsets, chksum, wait): + """ + Run the process. + + :param int size: The size of the binary to copy + :param int app_id: The application id to assign to the running binary + :param CoreSubsets core_subsets: The cores to load the binary on to + :param int chksum: The checksum of the data to test against + :param bool wait: + Whether to put the binary in "wait" mode or run it straight away + """ + boot_chip = SpiNNManDataView.get_machine().boot_chip + chips_done = set([(boot_chip.x, boot_chip.y)]) + next_chips = _get_next_chips(chips_done) + + while next_chips: + # Do all the chips at the current level + for link, chip in next_chips.values(): + subset = core_subsets.get_core_subset_for_chip(chip.x, chip.y) + self._send_request(AppCopyRun( + chip.x, chip.y, link, size, app_id, subset.processor_ids, + chksum, wait)) + chips_done.add((chip.x, chip.y)) + self._finish() + self.check_for_error() + next_chips = _get_next_chips(chips_done) diff --git a/spinnman/processes/application_run_process.py b/spinnman/processes/application_run_process.py index 4ca66510a..79c192949 100644 --- a/spinnman/processes/application_run_process.py +++ b/spinnman/processes/application_run_process.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinnman.messages.scp.impl import ApplicationRun from .abstract_multi_connection_process import AbstractMultiConnectionProcess diff --git a/spinnman/processes/de_alloc_sdram_process.py b/spinnman/processes/de_alloc_sdram_process.py deleted file mode 100644 index 8def8d4c1..000000000 --- a/spinnman/processes/de_alloc_sdram_process.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from spinnman.messages.scp.impl import SDRAMDeAlloc -from .abstract_multi_connection_process import AbstractMultiConnectionProcess - - -class DeAllocSDRAMProcess(AbstractMultiConnectionProcess): - __slots__ = [ - "_no_blocks_freed"] - - def __init__(self, connection_selector): - super(DeAllocSDRAMProcess, self).__init__(connection_selector) - self._no_blocks_freed = None - - def de_alloc_sdram(self, x, y, app_id, base_address=None): - callback = None - # deallocate space in the SDRAM - if base_address is None: - callback = self._handle_sdram_alloc_response - self._send_request(SDRAMDeAlloc(x, y, app_id, base_address), - callback=callback) - self._finish() - self.check_for_error() - - def _handle_sdram_alloc_response(self, response): - self._no_blocks_freed = response.number_of_blocks_freed - - @property - def no_blocks_freed(self): - return self._no_blocks_freed diff --git a/spinnman/processes/fill_process.py b/spinnman/processes/fill_process.py deleted file mode 100644 index 8e02b22ea..000000000 --- a/spinnman/processes/fill_process.py +++ /dev/null @@ -1,168 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import logging -import struct -from enum import Enum -from spinnman.messages.scp.impl import FillRequest, WriteMemory -from spinnman.processes.abstract_multi_connection_process import ( - AbstractMultiConnectionProcess) - -logger = logging.getLogger(__name__) -ALIGNMENT = 4 - - -class FillDataType(Enum): - - WORD = (4, "= len(pre_bytes) - pre_data = pre_bytes[:size] - if not pre_data: - return 0 - self._send_request(WriteMemory(x, y, base, pre_data)) - return extra_bytes - - def _write_fill(self, x, y, address, base, data_to_fill, size): - extra_bytes = (ALIGNMENT - base % ALIGNMENT) % ALIGNMENT - size = size - ALIGNMENT if extra_bytes else size - if not size: - return 0 - # The data to send is the repeated fill data, from the end of the - # pre-data circling round to the start of the post-data; we double - # it up so that we don't need to use mod (it's pretty small). - data = data_to_fill + data_to_fill - fill_word = FillDataType.WORD.unpack( - data[extra_bytes:extra_bytes + ALIGNMENT - 1])[0] - self._send_request(FillRequest(x, y, address, fill_word, size)) - return size - - def _write_post_bytes( - self, x, y, address, base, data_to_fill, bytes_to_write): - # Post bytes is the last part of the data from the end of the last - # aligned word; the number of bytes to write here is exactly the - # number of bytes later than a word boundary the initial address is. - n_bytes = base % ALIGNMENT - if not n_bytes or not bytes_to_write: - return - - post_bytes = self._PACKS[n_bytes].pack(*data_to_fill[-n_bytes:]) - self._send_request(WriteMemory( - x, y, address, post_bytes[:bytes_to_write])) - - def fill_memory(self, x, y, base_address, data, size, data_type): - """ - :type x: int - :type y: int - :type base_address: int - :type data: int - :type size: int - :type data_type: spinnman.processes.fill_process.FillDataType - """ - # Don't do anything if there is nothing to do! - if size == 0: - return - - # Check that the data can fill the requested size - if size % data_type.value: - raise Exception( - "The size of {} bytes to fill is not divisible by the size of" - " the data of {} bytes".format(size, data_type.value)) - if base_address % ALIGNMENT: - logger.warning( - "Unaligned fill starting at %d; please use aligned fills", - base_address) - - # Get a word of data regardless of the type - data_to_fill = bytearray(data_type.struct.pack( - *([data] * (ALIGNMENT / data_type.value)))) - - written = 0 - address = base_address - - # Send the pre-data to make the memory aligned (or all the - # data if the size is correct - note that pre_bytes[:size] will - # return all of pre_bytes if size >= len(pre_bytes) - delta = self._write_pre_bytes(x, y, base_address, data_to_fill, size) - written += delta - address += delta - - # Fill as much as possible - delta = self._write_fill( - x, y, address, base_address, data_to_fill, size) - written += delta - address += delta - - # Post bytes is the last part of the data from the end of the last - # aligned word; send them if required - self._write_post_bytes( - x, y, address, base_address, data_to_fill, size - written) - - # Wait for all the packets to be confirmed and then check there - # are no errors - self._finish() - self.check_for_error() diff --git a/spinnman/processes/fixed_connection_selector.py b/spinnman/processes/fixed_connection_selector.py new file mode 100644 index 000000000..e40a0ff94 --- /dev/null +++ b/spinnman/processes/fixed_connection_selector.py @@ -0,0 +1,37 @@ +# Copyright (c) 2017 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from spinn_utilities.overrides import overrides +from .abstract_multi_connection_process_connection_selector import ( + AbstractMultiConnectionProcessConnectionSelector) + + +class FixedConnectionSelector( + AbstractMultiConnectionProcessConnectionSelector): + """ + A connection selector that only uses a single connection. + """ + __slots__ = ("__connection", ) + + def __init__(self, connection): + """ + :param SCAMPConnection connection: + The connection to be used + """ + self.__connection = connection + + @overrides( + AbstractMultiConnectionProcessConnectionSelector.get_next_connection) + def get_next_connection(self, message): + return self.__connection diff --git a/spinnman/processes/get_cpu_info_process.py b/spinnman/processes/get_cpu_info_process.py index d05229838..2bdc09690 100644 --- a/spinnman/processes/get_cpu_info_process.py +++ b/spinnman/processes/get_cpu_info_process.py @@ -1,38 +1,53 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import functools -from spinnman.model import CPUInfo +import struct +from spinnman.model import CPUInfo, CPUInfos from spinnman.constants import CPU_INFO_BYTES from spinnman.utilities.utility_functions import get_vcpu_address from spinnman.messages.scp.impl import ReadMemory from .abstract_multi_connection_process import AbstractMultiConnectionProcess +_INFO_PATTERN = struct.Struct("< 32s 3I 2B 2B 2I 2B H 3I 16s 2I 16x 4I") + class GetCPUInfoProcess(AbstractMultiConnectionProcess): __slots__ = [ - "_cpu_info"] + "_cpu_infos"] def __init__(self, connection_selector): - super(GetCPUInfoProcess, self).__init__(connection_selector) - self._cpu_info = list() + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + """ + super().__init__(connection_selector) + self._cpu_infos = CPUInfos() + + def _filter_and_add_repsonse(self, x, y, p, cpu_data): + self._cpu_infos.add_info(CPUInfo(x, y, p, cpu_data)) - def handle_response(self, x, y, p, response): - self._cpu_info.append(CPUInfo(x, y, p, response.data, response.offset)) + def __handle_response(self, x, y, p, response): + cpu_data = _INFO_PATTERN.unpack_from(response.data, response.offset) + self._filter_and_add_repsonse(x, y, p, cpu_data) def get_cpu_info(self, core_subsets): + """ + :param ~spinn_machine.CoreSubsets core_subsets: + :rtype: list(CPUInfo) + """ for core_subset in core_subsets: x = core_subset.x y = core_subset.y @@ -40,8 +55,8 @@ def get_cpu_info(self, core_subsets): for p in core_subset.processor_ids: self._send_request( ReadMemory(x, y, get_vcpu_address(p), CPU_INFO_BYTES), - functools.partial(self.handle_response, x, y, p)) + functools.partial(self.__handle_response, x, y, p)) self._finish() self.check_for_error() - return self._cpu_info + return self._cpu_infos diff --git a/spinnman/processes/get_exclude_cpu_info_process.py b/spinnman/processes/get_exclude_cpu_info_process.py new file mode 100644 index 000000000..50b703ffc --- /dev/null +++ b/spinnman/processes/get_exclude_cpu_info_process.py @@ -0,0 +1,36 @@ +# Copyright (c) 2015 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from spinnman.model import CPUInfo +from spinnman.model.enums import CPUState +from .get_cpu_info_process import GetCPUInfoProcess + + +class GetExcludeCPUInfoProcess(GetCPUInfoProcess): + __slots__ = [ + "_states"] + + def __init__(self, connection_selector, states): + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + """ + super().__init__(connection_selector) + self._states = states + + def _filter_and_add_repsonse(self, x, y, p, cpu_data): + state = CPUState(cpu_data[6]) + if state not in self._states: + self._cpu_infos.add_info(CPUInfo(x, y, p, cpu_data)) diff --git a/spinnman/processes/get_heap_process.py b/spinnman/processes/get_heap_process.py index cd9f0f89c..a25a86754 100644 --- a/spinnman/processes/get_heap_process.py +++ b/spinnman/processes/get_heap_process.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2016 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct import functools @@ -35,7 +34,12 @@ class GetHeapProcess(AbstractMultiConnectionProcess): "_next_block_address"] def __init__(self, connection_selector): - super(GetHeapProcess, self).__init__(connection_selector) + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + """ + super().__init__(connection_selector) self._heap_address = None self._next_block_address = None @@ -64,6 +68,11 @@ def _read_address(self, chip_address, address, size, callback): self.check_for_error() def get_heap(self, chip_address, pointer=HEAP_ADDRESS): + """ + :param tuple(int,int) chip_address: x, y + :param SystemVariableDefinition pointer: + :rtype: list(HeapElement) + """ self._read_address( chip_address, SYSTEM_VARIABLE_BASE_ADDRESS + pointer.offset, pointer.data_type.value, self._read_heap_address_response) diff --git a/spinnman/processes/get_include_cpu_info_process.py b/spinnman/processes/get_include_cpu_info_process.py new file mode 100644 index 000000000..5ec6a90eb --- /dev/null +++ b/spinnman/processes/get_include_cpu_info_process.py @@ -0,0 +1,36 @@ +# Copyright (c) 2015 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from spinnman.model import CPUInfo +from spinnman.model.enums import CPUState +from .get_cpu_info_process import GetCPUInfoProcess + + +class GetIncludeCPUInfoProcess(GetCPUInfoProcess): + __slots__ = [ + "_states"] + + def __init__(self, connection_selector, states): + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + """ + super().__init__(connection_selector) + self._states = states + + def _filter_and_add_repsonse(self, x, y, p, cpu_data): + state = CPUState(cpu_data[6]) + if state in self._states: + self._cpu_infos.add_info(CPUInfo(x, y, p, cpu_data)) diff --git a/spinnman/processes/get_machine_process.py b/spinnman/processes/get_machine_process.py index df71eb283..2a52f3491 100644 --- a/spinnman/processes/get_machine_process.py +++ b/spinnman/processes/get_machine_process.py @@ -1,25 +1,34 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from collections import defaultdict +from contextlib import suppress import logging import functools +from os.path import join +from spinn_utilities.config_holder import ( + get_config_bool, get_config_int_or_none, get_config_str_or_none) +from spinn_utilities.data import UtilsDataView from spinn_utilities.log import FormatAdapter -from spinn_machine import ( - Processor, Router, Chip, SDRAM, Link, machine_from_size) +from spinn_machine import (Router, Chip, Link) +from spinn_machine.ignores import IgnoreChip, IgnoreCore, IgnoreLink from spinn_machine.machine_factory import machine_repair -from spinnman.constants import ROUTER_REGISTER_P2P_ADDRESS +from spinnman.constants import ( + ROUTER_REGISTER_P2P_ADDRESS, SYSTEM_VARIABLE_BASE_ADDRESS) +from spinnman.data import SpiNNManDataView +from spinnman.messages.spinnaker_boot import ( + SystemVariableDefinition) from spinnman.exceptions import SpinnmanUnexpectedResponseCodeException from spinnman.messages.scp.impl import ReadMemory, ReadLink, GetChipInfo from spinnman.model import P2PTable @@ -28,119 +37,171 @@ logger = FormatAdapter(logging.getLogger(__name__)) +REPORT_FILE = "Ignores_report.rpt" + +P_TO_V = SystemVariableDefinition.physical_to_virtual_core_map +V_TO_P = SystemVariableDefinition.virtual_to_physical_core_map +P_TO_V_ADDR = SYSTEM_VARIABLE_BASE_ADDRESS + P_TO_V.offset +P_MAPS_SIZE = P_TO_V.array_size + V_TO_P.array_size + class GetMachineProcess(AbstractMultiConnectionProcess): - """ A process for getting the machine details over a set of connections. + """ + A process for getting the machine details over a set of connections. """ __slots__ = [ "_chip_info", - "_ignore_chips", - "_ignore_cores", - "_ignore_links", - "_max_core_id", - "_max_sdram_size", - "_p2p_column_data"] - - def __init__(self, connection_selector, ignore_chips, ignore_cores, - ignore_links, max_core_id, max_sdram_size=None): - # pylint: disable=too-many-arguments - super(GetMachineProcess, self).__init__(connection_selector) - - self._ignore_chips = ignore_chips if ignore_chips is not None else {} - self._ignore_cores = ignore_cores if ignore_cores is not None else {} - self._ignore_links = ignore_links if ignore_links is not None else {} - self._max_core_id = max_core_id - self._max_sdram_size = max_sdram_size + # Used if there are any ignores with ip addresses + # Holds a mapping from ip to board root (x,y) + "_ethernets", + # Holds a map from x,y to a set of virtual cores to ignores + "_ignore_cores_map", + "_p2p_column_data", + # Holds a mapping from (x,y) to a mapping of physical to virtual core + "_virtual_to_physical_map", + # Holds a mapping from (x,y) to a mapping of virtual to physical core + "_physical_to_virtual_map"] + + def __init__(self, connection_selector): + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + """ + super().__init__(connection_selector) + + self._ignore_cores_map = defaultdict(set) self._p2p_column_data = list() # A dictionary of (x, y) -> ChipInfo self._chip_info = dict() + # Set ethernets to None meaning not computed yet + self._ethernets = None + + # Maps between virtual and physical cores + self._virtual_to_physical_map = dict() + self._physical_to_virtual_map = dict() + def _make_chip(self, chip_info, machine): - """ Creates a chip from a ChipSummaryInfo structure. + """ + Creates a chip from a ChipSummaryInfo structure. - :param chip_info: \ + :param ChipSummaryInfo chip_info: The ChipSummaryInfo structure to create the chip from - :type chip_info: \ - :py:class:`spinnman.model.ChipSummaryInfo` :return: The created chip - :rtype: :py:class:`spinn_machine.Chip` + :rtype: ~spinn_machine.Chip """ - - # Create the processor list - processors = list() - max_core_id = chip_info.n_cores - 1 + # Create the down cores set if any + n_cores = \ + SpiNNManDataView.get_machine_version().max_cores_per_chip + n_cores = min(chip_info.n_cores, n_cores) core_states = chip_info.core_states - if self._max_core_id is not None and max_core_id > self._max_core_id: - max_core_id = self._max_core_id - for virtual_core_id in range(max_core_id + 1): - # Add the core provided it is not to be ignored - if ((chip_info.x, chip_info.y, virtual_core_id) not in - self._ignore_cores): - if virtual_core_id == 0: - processors.append(Processor.factory( - virtual_core_id, is_monitor=True)) - elif core_states[virtual_core_id] == CPUState.IDLE: - processors.append(Processor.factory(virtual_core_id)) - else: - logger.warning( - "Not using core {}, {}, {} in state {}", - chip_info.x, chip_info.y, virtual_core_id, - core_states[virtual_core_id]) + down_cores = self._ignore_cores_map.get( + (chip_info.x, chip_info.y), None) + for i in range(1, n_cores): + if core_states[i] != CPUState.IDLE: + self._report_ignore( + "Not using core {}, {}, {} in state {}", + chip_info.x, chip_info.y, i, core_states[i]) + if down_cores is None: + down_cores = set() + down_cores.add(i) # Create the router router = self._make_router(chip_info, machine) # Create the chip's SDRAM object sdram_size = chip_info.largest_free_sdram_block - if (self._max_sdram_size is not None and - sdram_size > self._max_sdram_size): - sdram_size = self._max_sdram_size - sdram = SDRAM(size=sdram_size) + max_sdram_size = get_config_int_or_none( + "Machine", "max_sdram_allowed_per_chip") + if (max_sdram_size is not None and + sdram_size > max_sdram_size): + sdram_size = max_sdram_size # Create the chip return Chip( - x=chip_info.x, y=chip_info.y, processors=processors, - router=router, sdram=sdram, + x=chip_info.x, y=chip_info.y, n_processors=n_cores, + router=router, sdram=sdram_size, ip_address=chip_info.ethernet_ip_address, nearest_ethernet_x=chip_info.nearest_ethernet_x, - nearest_ethernet_y=chip_info.nearest_ethernet_y) + nearest_ethernet_y=chip_info.nearest_ethernet_y, + down_cores=down_cores, parent_link=chip_info.parent_link, + v_to_p_map=self._virtual_to_physical_map.get( + (chip_info.x, chip_info.y))) def _make_router(self, chip_info, machine): + """ + :param ChipSummaryInfo chip_info: + :param ~spinn_machine.Machine machine: + :rtype: ~spinn_machine.Router + """ links = list() for link in chip_info.working_links: - dest = machine.xy_over_link(chip_info.x, chip_info.y, link) - if ((chip_info.x, chip_info.y, link) not in self._ignore_links - and dest not in self._ignore_chips - and dest in self._chip_info): - dest_x, dest_y = dest + dest_xy = machine.xy_over_link(chip_info.x, chip_info.y, link) + if dest_xy in self._chip_info: links.append(Link( - chip_info.x, chip_info.y, link, dest_x, dest_y)) + chip_info.x, chip_info.y, link, dest_xy[0], dest_xy[1])) + else: + logger.warning( + "Link {},{}:{} points to {} but that is not included ", + chip_info.x, chip_info.y, link, dest_xy) + return Router( links=links, n_available_multicast_entries=( chip_info.n_free_multicast_routing_entries)) - def _receive_p2p_data(self, column, scp_read_response): + def __receive_p2p_data(self, column, scp_read_response): + """ + :param int column: + :param _SCPReadMemoryResponse scp_read_response: + """ self._p2p_column_data[column] = ( scp_read_response.data, scp_read_response.offset) def _receive_chip_info(self, scp_read_chip_info_response): + """ + :param GetChipInfoResponse scp_read_chip_info_response: + """ chip_info = scp_read_chip_info_response.chip_info self._chip_info[chip_info.x, chip_info.y] = chip_info - def _receive_error(self, request, exception, tb): + def _receive_p_maps(self, x, y, scp_read_response): + """ + Receive the physical-to-virtual and virtual-to-physical maps. + + :param _SCPReadMemoryResponse scp_read_response: + """ + data = scp_read_response.data + off = scp_read_response.offset + self._physical_to_virtual_map[x, y] = bytearray( + data[off:P_TO_V.array_size + off]) + off += P_TO_V.array_size + self._virtual_to_physical_map[x, y] = bytearray(data[off:]) + + def _receive_error(self, request, exception, tb, connection): + """ + :param AbstractSCPRequest request: + :param Exception exception: + """ # If we get an ReadLink with a # SpinnmanUnexpectedResponseCodeException, this is a failed link # and so can be ignored if isinstance(request, ReadLink): if isinstance(exception, SpinnmanUnexpectedResponseCodeException): return - super(GetMachineProcess, self)._receive_error(request, exception, tb) + super()._receive_error(request, exception, tb, connection) - def get_machine_details(self, boot_x, boot_y, width, height, - repair_machine, ignore_bad_ethernets): + def get_machine_details(self, boot_x, boot_y, width, height): + """ + :param int boot_x: + :param int boot_y: + :param int width: + :param int height: + :rtype: ~spinn_machine.Machine + """ # Get the P2P table - 8 entries are packed into each 32-bit word p2p_column_bytes = P2PTable.get_n_column_bytes(height) self._p2p_column_data = [None] * width @@ -151,7 +212,7 @@ def get_machine_details(self, boot_x, boot_y, width, height, x=boot_x, y=boot_y, base_address=(ROUTER_REGISTER_P2P_ADDRESS + offset), size=p2p_column_bytes), - functools.partial(self._receive_p2p_data, column)) + functools.partial(self.__receive_p2p_data, column)) self._finish() self.check_for_error() p2p_table = P2PTable(width, height, self._p2p_column_data) @@ -159,13 +220,14 @@ def get_machine_details(self, boot_x, boot_y, width, height, # Get the chip information for each chip for (x, y) in p2p_table.iterchips(): self._send_request(GetChipInfo(x, y), self._receive_chip_info) + self._send_request( + ReadMemory(x, y, P_TO_V_ADDR, P_MAPS_SIZE), + functools.partial(self._receive_p_maps, x, y)) self._finish() - try: - self.check_for_error() - except Exception: # pylint: disable=broad-except - # Ignore errors so far, as any error here just means that a chip + with suppress(Exception): + # Ignore errors, as any error here just means that a chip # is down that wasn't marked as down - pass + self.check_for_error() # Warn about unexpected missing chips for (x, y) in p2p_table.iterchips(): @@ -173,52 +235,244 @@ def get_machine_details(self, boot_x, boot_y, width, height, logger.warning( "Chip {}, {} was expected but didn't reply", x, y) - return self.create_machine( - width, height, repair_machine, ignore_bad_ethernets) + version = SpiNNManDataView.get_machine_version() + machine = version.create_machine(width, height) + self._preprocess_ignore_chips(machine) + self._process_ignore_links(machine) + self._preprocess_ignore_cores(machine) + + return self._fill_machine(machine) - def create_machine( - self, width, height, repair_machine, ignore_bad_ethernets): - bad_ethernets = [] - machine = machine_from_size(width, height) + def _fill_machine(self, machine): + """ + :param ~spinn_machine.Machine machine: + :param bool repair_machine: + :param bool ignore_bad_ethernets: + :rtype: ~spinn_machine.Machine + """ for chip_info in sorted( self._chip_info.values(), key=lambda chip: (chip.x, chip.y)): - if (chip_info.x, chip_info.y) not in self._ignore_chips: - if (chip_info.ethernet_ip_address is not None and + if (chip_info.ethernet_ip_address is not None and (chip_info.x != chip_info.nearest_ethernet_x or chip_info.y != chip_info.nearest_ethernet_y)): - bad_ethernets.append( - (chip_info, chip_info.ethernet_ip_address)) - if ignore_bad_ethernets: - # pylint: disable=protected-access - chip_info._ethernet_ip_address = None - machine.add_chip( - self._make_chip(chip_info, machine)) + if get_config_bool("Machine", "ignore_bad_ethernets"): + logger.warning( + "Chip {}:{} claimed it has ip address: {}. " + "This ip will not be used.", + chip_info.x, chip_info.y, + chip_info.ethernet_ip_address) + chip_info.clear_ethernet_ip_address() else: - machine.add_chip(self._make_chip(chip_info, machine)) - - removed_chips = [] - if len(bad_ethernets) > 0: - msg = "" - for chip_info, claim in bad_ethernets: - chip = self._make_chip(chip_info, machine) - local_x, local_y = machine.get_local_xy(chip) - ethernet = machine.get_chip_at( - chip.nearest_ethernet_x, chip.nearest_ethernet_y) - msg += "x:{} y:{} on board:{} claims it has ethernet {} " \ - "".format(local_x, local_y, ethernet.ip_address, claim) - removed_chips.append((chip.x, chip.y)) - logger.warning(msg) - - if ignore_bad_ethernets: - # No chips actually removed - removed_chips = [] + logger.warning( + "Not using chip {}:{} as it has an unexpected " + "ip address: {}", chip_info.x, chip_info.y, + chip_info.ethernet_ip_address) + continue + + # If the above has not continued, add the chip + machine.add_chip(self._make_chip(chip_info, machine)) + machine.validate() - return machine_repair(machine, repair_machine, removed_chips) + return machine_repair(machine) + + # Stuff below here is purely for dealing with ignores + + def _process_ignore_links(self, machine): + """ + Processes the collection of ignore links to remove then from chipinfo. + + Converts any local (x, y, IP address) to global (x, y) + + Discards any ignores which are known to have no + affect based on the already read chip_info + + Also removes any inverse links + + Logs all actions except for ignores with unused IP addresses + + :param ~spinn_machine.Machine machine: + An empty machine to handle wrap-arounds + """ + for ignore in IgnoreLink.parse_string( + get_config_str_or_none("Machine", "down_links")): + global_xy = self._ignores_local_to_global( + ignore.x, ignore.y, ignore.ip_address, machine) + if global_xy is None: + continue + chip_info = self._chip_info.get(global_xy, None) + if chip_info is None: + self._report_ignore( + "Discarding ignore link on chip {} as it is not/ no longer" + " in info", global_xy) + continue + link = ignore.link + if link in chip_info.working_links: + chip_info.working_links.remove(link) + self._report_ignore( + "On chip {} ignoring link:{}", global_xy, link) + # ignore the inverse link too + inv_xy = machine.xy_over_link(global_xy[0], global_xy[1], link) + if inv_xy in self._chip_info: + inv_chip_info = self._chip_info[inv_xy] + inv_link = (link + 3) % 6 + if inv_link in inv_chip_info.working_links: + inv_chip_info.working_links.remove(inv_link) + self._report_ignore( + "On chip {} ignoring link {} as it is the inverse " + "of link {} on chip {}", inv_xy, inv_link, link, + global_xy) + else: + self._report_ignore( + "Discarding ignore link {} on chip {} as it is not/" + "no longer in info", link, global_xy) + + def _preprocess_ignore_cores(self, machine): + """ + Converts the collection of ignore cores into a map of ignore by x,y. + + Converts any local (x, y, IP address) to global (x, y) + + Discards (with log messages) any ignores which are known to have no + affect based on the already read chip_info + + Converts any physical cores to virtual ones. + Core numbers <= 0 are assumed to be 0 - physical_id + + :param ~spinn_machine.Machine machine: + An empty machine to handle wrap-arounds + """ + # Convert by ip to global + for ignore in IgnoreCore.parse_string( + get_config_str_or_none("Machine", "down_cores")): + global_xy = self._ignores_local_to_global( + ignore.x, ignore.y, ignore.ip_address, machine) + if global_xy is None: + continue + p = self._get_virtual_p(global_xy, ignore.p) + if p is not None: + self._ignore_cores_map[global_xy].add(p) + + def _preprocess_ignore_chips(self, machine): + """ + Processes the collection of ignore chips and discards their chipinfo. + + Converts any local (x, y IP address) to global (x, y) + + Discards any ignores which are known to have no + affect based on the already read chip_info + + Logs all actions except for ignores with unused IP addresses + + :param ~spinn_machine.Machine machine: + An empty machine to handle wrap-arounds + """ + for ignore in IgnoreChip.parse_string( + get_config_str_or_none("Machine", "down_chips")): + # Convert by ip to global + global_xy = self._ignores_local_to_global( + ignore.x, ignore.y, ignore.ip_address, machine) + if global_xy is None: + continue # Never on this machine + chip_info = self._chip_info.pop(global_xy, None) + if chip_info is None: + continue # Already ignored maybe by a dead chip list + self._report_ignore("Chip {} will be ignored", global_xy) + for link in chip_info.working_links: + # ignore the inverse link + inv_xy = machine.xy_over_link(global_xy[0], global_xy[1], link) + if inv_xy in self._chip_info: + inv_chip_info = self._chip_info[inv_xy] + inv_link = (link + 3) % 6 + if inv_link in inv_chip_info.working_links: + inv_chip_info.working_links.remove(inv_link) + self._report_ignore( + "On chip {} ignoring link {} as it points to " + "ignored chip chip {}", + inv_xy, inv_link, global_xy) + + def _ignores_local_to_global(self, local_x, local_y, ip_address, machine): + """ + :param int local_x: + :param int local_y: + :param str ip_address: + :param ~spinn_machine.Machine machine: + :rtype: tuple(int,int) + """ + if ip_address is None: + global_xy = (local_x, local_y) + else: + ethernet = self._ethernet_by_ipaddress(ip_address) + if ethernet is None: + self._report_ignore( + "Ignore with ip:{} will be discarded as no board with " + "that ip in this machine", ip_address) + return None + global_xy = machine.get_global_xy( + local_x, local_y, ethernet[0], ethernet[1]) + self._report_ignore( + "Ignores for local x:{} y:{} and ip:{} map to global {} with " + "root {}", local_x, local_y, ip_address, global_xy, + self._chip_info[(0, 0)].ethernet_ip_address) + + if global_xy in self._chip_info: + return global_xy + else: + self._report_ignore( + "Ignore for global chip {} will be discarded as no such chip " + "in this machine", global_xy) + return None + + def _ethernet_by_ipaddress(self, ip_address): + """ + :param str ip_address: + :rtype: tuple(int,int) + """ + if self._ethernets is None: + self._ethernets = dict() + for chip_info in self._chip_info.values(): + if chip_info.ethernet_ip_address is not None: + self._ethernets[chip_info.ethernet_ip_address] = \ + (chip_info.x, chip_info.y) + return self._ethernets.get(ip_address, None) + + def _get_virtual_p(self, xy, p): + """ + :param tuple(int,int) xy: + :param int p: + :rtype: int + """ + if p > 0: + self._report_ignore("On chip {} ignoring core {}", xy, p) + return p + virtual_map = self._physical_to_virtual_map[xy] + physical = 0 - p + if physical >= len(virtual_map) or virtual_map[physical] == 0xFF: + self._report_ignore( + "On chip {} physical core {} was not used " + "so ignore is being discarded.", xy, physical) + return None + virtual_p = virtual_map[physical] + if virtual_p == 0: + self._report_ignore( + "On chip {} physical core {} was used as the monitor " + "so will NOT be ignored", xy, physical) + return None + self._report_ignore( + "On chip {} ignoring core {} as it maps to physical " + "core {}", xy, virtual_p, physical) + return virtual_p + + def _report_ignore(self, message, *args): + """ + Writes the ignore message by either creating or appending the report. - def get_chip_info(self): - """ Get the chip information for the machine. + The implementation choice to reopen the file every time is not the + fastest but is the cleanest and safest for code that in default + conditions is never run. - .. note:: - :py:meth:`get_machine_details` must have been called first. + :param str message: """ - return self._chip_info + full_message = message.format(*args) + "\n" + report_file = join(UtilsDataView.get_run_dir_path(), REPORT_FILE) + with open(report_file, "a", encoding="utf-8") as r_file: + r_file.write(full_message) diff --git a/spinnman/processes/get_routes_process.py b/spinnman/processes/get_routes_process.py index 458161f28..951d5bfd7 100644 --- a/spinnman/processes/get_routes_process.py +++ b/spinnman/processes/get_routes_process.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct import functools @@ -33,14 +32,21 @@ class GetMultiCastRoutesProcess(AbstractMultiConnectionProcess): - """ A process for reading the multicast routing table of a SpiNNaker chip. + """ + A process for reading the multicast routing table of a SpiNNaker chip. """ __slots__ = [ "_app_id", "_entries"] def __init__(self, connection_selector, app_id=None): - super(GetMultiCastRoutesProcess, self).__init__(connection_selector) + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + :param int app_id: + """ + super().__init__(connection_selector) self._entries = [None] * _N_ENTRIES self._app_id = app_id @@ -58,7 +64,7 @@ def _add_routing_entry(self, route_no, offset, app_id, route, key, mask): self._entries[route_no + offset] = MulticastRoutingEntry( key, mask, processor_ids, link_ids, False) - def handle_read_response(self, offset, response): + def __handle_response(self, offset, response): for route_no in range(_ENTRIES_PER_READ): entry = _ROUTE_ENTRY_PATTERN.unpack_from( response.data, @@ -66,14 +72,19 @@ def handle_read_response(self, offset, response): self._add_routing_entry(route_no, offset, *entry) def get_routes(self, x, y, base_address): - + """ + :param int x: + :param int y: + :param int base_address: + :rtype: list(~spinn_machine.MulticastRoutingEntry) + """ # Create the read requests offset = 0 for _ in range(_N_READS): self._send_request( ReadMemory( x, y, base_address + (offset * 16), UDP_MESSAGE_MAX_SIZE), - functools.partial(self.handle_read_response, offset)) + functools.partial(self.__handle_response, offset)) offset += _ENTRIES_PER_READ self._finish() self.check_for_error() diff --git a/spinnman/processes/get_tags_process.py b/spinnman/processes/get_tags_process.py index 1b89e9824..633e08641 100644 --- a/spinnman/processes/get_tags_process.py +++ b/spinnman/processes/get_tags_process.py @@ -1,20 +1,18 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import functools -from past.builtins import xrange from spinn_machine.tags import ReverseIPTag, IPTag from .abstract_multi_connection_process import AbstractMultiConnectionProcess from spinnman.messages.scp.impl import IPTagGetInfo, IPTagGet @@ -26,18 +24,22 @@ class GetTagsProcess(AbstractMultiConnectionProcess): "_tag_info"] def __init__(self, connection_selector): - super(GetTagsProcess, self).__init__(connection_selector) + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + """ + super().__init__(connection_selector) self._tag_info = None self._tags = None - def handle_tag_info_response(self, response): + def __handle_tag_info_response(self, response): self._tag_info = response - def handle_get_tag_response(self, tag, board_address, response): + def __handle_get_tag_response(self, tag, board_address, response): if response.in_use: - ip_address = response.ip_address - host = "{}.{}.{}.{}".format(ip_address[0], ip_address[1], - ip_address[2], ip_address[3]) + ip = response.ip_address + host = f"{ip[0]}.{ip[1]}.{ip[2]}.{ip[3]}" if response.is_reverse: self._tags[tag] = ReverseIPTag( board_address, tag, @@ -51,21 +53,26 @@ def handle_get_tag_response(self, tag, board_address, response): response.port, response.strip_sdp) def get_tags(self, connection): + """ + :param SCAMPConnection connection: + :rtype: + list(~spinn_machine.tags.IPTag or ~spinn_machine.tags.ReverseIPTag) + """ # Get the tag information, without which we cannot continue self._send_request(IPTagGetInfo( connection.chip_x, connection.chip_y), - self.handle_tag_info_response) + self.__handle_tag_info_response) self._finish() self.check_for_error() # Get the tags themselves n_tags = self._tag_info.pool_size + self._tag_info.fixed_size self._tags = [None] * n_tags - for tag in xrange(n_tags): + for tag in range(n_tags): self._send_request(IPTagGet( connection.chip_x, connection.chip_y, tag), functools.partial( - self.handle_get_tag_response, tag, + self.__handle_get_tag_response, tag, connection.remote_ip_address)) self._finish() self.check_for_error() diff --git a/spinnman/processes/get_version_process.py b/spinnman/processes/get_version_process.py index e37f76db3..cb534cef4 100644 --- a/spinnman/processes/get_version_process.py +++ b/spinnman/processes/get_version_process.py @@ -1,37 +1,51 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinnman.messages.scp.impl import GetVersion -from .abstract_single_connection_process import AbstractSingleConnectionProcess +from .abstract_multi_connection_process import AbstractMultiConnectionProcess from spinnman.constants import N_RETRIES -class GetVersionProcess(AbstractSingleConnectionProcess): - """ A process for getting the version of the machine. +class GetVersionProcess(AbstractMultiConnectionProcess): + """ + A process for getting the version of the machine. """ __slots__ = [ "_version_info"] def __init__(self, connection_selector, n_retries=N_RETRIES): - super(GetVersionProcess, self).__init__(connection_selector, n_retries) + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + """ + super().__init__(connection_selector, n_retries) self._version_info = None def _get_response(self, version_response): + """ + :param GetVersionResponse version_response: + """ self._version_info = version_response.version_info def get_version(self, x, y, p): + """ + :param int x: + :param int y: + :param int p: + :rtype: VersionInfo + """ self._send_request(GetVersion(x=x, y=y, p=p), self._get_response) self._finish() diff --git a/spinnman/processes/load_fixed_route_routing_entry_process.py b/spinnman/processes/load_fixed_route_routing_entry_process.py index fa9ae5ada..d252e9a41 100644 --- a/spinnman/processes/load_fixed_route_routing_entry_process.py +++ b/spinnman/processes/load_fixed_route_routing_entry_process.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_machine import Router from spinnman.messages.scp.impl.fixed_route_init import FixedRouteInit @@ -19,22 +18,21 @@ class LoadFixedRouteRoutingEntryProcess(AbstractMultiConnectionProcess): + """ + Load a fixed route routing entry onto a chip. + """ __slots__ = [] def load_fixed_route(self, x, y, fixed_route, app_id=0): - """ Load a fixed route routing entry onto a chip. - - :param x: The x-coordinate of the chip, between 0 and 255; \ + """ + :param int x: The x-coordinate of the chip, between 0 and 255; this is not checked due to speed restrictions. - :type x: int - :param y: The y-coordinate of the chip, between 0 and 255; \ + :param int y: The y-coordinate of the chip, between 0 and 255; this is not checked due to speed restrictions. - :type y: int - :param fixed_route: the fixed route entry - :param app_id: The ID of the application with which to associate the\ - routes. If not specified, defaults to 0. - :type app_id: int - :rtype: None + :param ~spinn_machine.FixedRouteEntry fixed_route: + the fixed route entry + :param int app_id: The ID of the application with which to associate + the routes. If not specified, defaults to 0. """ route_entry = \ Router.convert_routing_table_entry_to_spinnaker_route(fixed_route) diff --git a/spinnman/processes/load_routes_process.py b/spinnman/processes/load_routes_process.py index c5bf2427f..12700d685 100644 --- a/spinnman/processes/load_routes_process.py +++ b/spinnman/processes/load_routes_process.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinn_machine import Router @@ -22,23 +21,35 @@ _ROUTE_PATTERN = struct.Struct(". +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinnman.messages.scp.impl import SDRAMAlloc from .abstract_multi_connection_process import AbstractMultiConnectionProcess class MallocSDRAMProcess(AbstractMultiConnectionProcess): - """ A process for allocating a block of SDRAM on a SpiNNaker chip. + """ + A process for allocating a block of SDRAM on a SpiNNaker chip. """ __slots__ = [ "_base_address"] def __init__(self, connection_selector): - super(MallocSDRAMProcess, self).__init__(connection_selector) + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + """ + super().__init__(connection_selector) self._base_address = None def _handle_sdram_alloc_response(self, response): self._base_address = response.base_address def malloc_sdram(self, x, y, size, app_id, tag): - """ Allocate space in the SDRAM space. + """ + Allocate space in the SDRAM space. + + :param int x: + :param int y: + :param int size: + :param int app_id: + :param int tag: """ # pylint: disable=too-many-arguments self._send_request(SDRAMAlloc(x, y, app_id, size, tag), @@ -41,4 +53,9 @@ def malloc_sdram(self, x, y, size, app_id, tag): @property def base_address(self): + """ + The address of the allocated memory block. + + :rtype: int + """ return self._base_address diff --git a/spinnman/processes/most_direct_connection_selector.py b/spinnman/processes/most_direct_connection_selector.py index 32c7fad60..1b8f4ab76 100644 --- a/spinnman/processes/most_direct_connection_selector.py +++ b/spinnman/processes/most_direct_connection_selector.py @@ -1,35 +1,38 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides +from spinnman.data import SpiNNManDataView from .abstract_multi_connection_process_connection_selector import ( AbstractMultiConnectionProcessConnectionSelector) class MostDirectConnectionSelector( AbstractMultiConnectionProcessConnectionSelector): - """ A selector that goes for the most direct connection for the message. + """ + A selector that goes for the most direct connection for the message. """ __slots__ = [ "_connections", - "_first_connection", - "_machine"] - - @overrides(AbstractMultiConnectionProcessConnectionSelector.__init__) - def __init__(self, machine, connections): - self._machine = machine + "_first_connection"] + + # pylint: disable=super-init-not-called + def __init__(self, connections): + """ + :param list(SCAMPConnection) connections: + The connections to be used + """ self._connections = dict() self._first_connection = None for connection in connections: @@ -40,20 +43,21 @@ def __init__(self, machine, connections): if self._first_connection is None: self._first_connection = next(iter(connections)) - def set_machine(self, new_machine): - self._machine = new_machine - @overrides( AbstractMultiConnectionProcessConnectionSelector.get_next_connection) def get_next_connection(self, message): - if self._machine is None or len(self._connections) == 1: + key = (message.sdp_header.destination_chip_x, + message.sdp_header.destination_chip_y) + if key in self._connections: + return self._connections[key] + + if not SpiNNManDataView.has_machine() or len(self._connections) == 1: return self._first_connection - chip = self._machine.get_chip_at( - message.sdp_header.destination_chip_x, - message.sdp_header.destination_chip_y) - key = (chip.nearest_ethernet_x, chip.nearest_ethernet_y) + x, y = key + key = SpiNNManDataView.get_nearest_ethernet(x, y) - if key not in self._connections: + if key in self._connections: + return self._connections[key] + else: return self._first_connection - return self._connections[key] diff --git a/spinnman/processes/read_fixed_route_routing_entry_process.py b/spinnman/processes/read_fixed_route_routing_entry_process.py index 22dcf05da..077e21a40 100644 --- a/spinnman/processes/read_fixed_route_routing_entry_process.py +++ b/spinnman/processes/read_fixed_route_routing_entry_process.py @@ -1,24 +1,24 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinnman.messages.scp.impl.fixed_route_read import FixedRouteRead from .abstract_multi_connection_process import AbstractMultiConnectionProcess class ReadFixedRouteRoutingEntryProcess(AbstractMultiConnectionProcess): - """ A process for reading a chip's fixed route routing entry. + """ + A process for reading a fixed route routing table entry. """ __slots__ = ( @@ -27,34 +27,32 @@ class ReadFixedRouteRoutingEntryProcess(AbstractMultiConnectionProcess): ) def __init__(self, connection_selector): - """ Creates the process for writing a fixed route entry to a chip's\ - router. - + """ :param connection_selector: the SC&MP connection selector + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector """ - super(ReadFixedRouteRoutingEntryProcess, self).__init__( - connection_selector) + super().__init__(connection_selector) self._route = None - def handle_read_response(self, response): + def __handle_read_response(self, response): self._route = response.route def read_fixed_route(self, x, y, app_id=0): - """ Reads a fixed route routing table entry. + """ + Read the fixed route entry installed on a particular chip's router. - :param x: The x-coordinate of the chip, between 0 and 255; \ + :param int x: The x-coordinate of the chip, between 0 and 255; this is not checked due to speed restrictions - :type x: int - :param y: The y-coordinate of the chip, between 0 and 255; \ + :param int y: The y-coordinate of the chip, between 0 and 255; this is not checked due to speed restrictions - :type y: int - :param app_id: The ID of the application with which to associate the\ + :param int app_id: + The ID of the application with which to associate the routes. If not specified, defaults to 0. - :type app_id: int - :rtype: None + :rtype: ~spinn_machine.FixedRouteEntry """ self._send_request(FixedRouteRead(x, y, app_id), - self.handle_read_response) + self.__handle_read_response) self._finish() self.check_for_error() return self._route diff --git a/spinnman/processes/read_iobuf_process.py b/spinnman/processes/read_iobuf_process.py index 0ee9767ca..8884ce291 100644 --- a/spinnman/processes/read_iobuf_process.py +++ b/spinnman/processes/read_iobuf_process.py @@ -1,25 +1,20 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import functools import struct -try: - from collections.abc import defaultdict, OrderedDict -except ImportError: - from collections import defaultdict, OrderedDict -from six import itervalues +from collections import defaultdict from spinnman.model import IOBuffer from spinnman.utilities.utility_functions import get_vcpu_address from spinnman.messages.scp.impl import ReadMemory @@ -32,8 +27,9 @@ class ReadIOBufProcess(AbstractMultiConnectionProcess): - """ A process for reading IOBUF memory (mostly log messages) from a\ - SpiNNaker core. + """ + A process for reading IOBUF memory (mostly log messages) from a + SpiNNaker core. """ __slots__ = [ "_extra_reads", @@ -43,16 +39,21 @@ class ReadIOBufProcess(AbstractMultiConnectionProcess): "_next_reads"] def __init__(self, connection_selector): - super(ReadIOBufProcess, self).__init__(connection_selector) + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + """ + super().__init__(connection_selector) # A dictionary of (x, y, p) -> iobuf address self._iobuf_address = dict() # A dictionary of (x, y, p) -> OrderedDict(n) -> bytearray - self._iobuf = defaultdict(OrderedDict) + self._iobuf = defaultdict(dict) # A dictionary of (x, y, p) -> OrderedDict(n) -> memoryview - self._iobuf_view = defaultdict(OrderedDict) + self._iobuf_view = defaultdict(dict) # A list of extra reads that need to be done as a result of the first # read = list of (x, y, p, n, base_address, size, offset) @@ -141,7 +142,9 @@ def _handle_first_iobuf_response(self, x, y, p, n, base_address, def read_iobuf(self, iobuf_size, core_subsets): """ - :rtype: iterable of IOBuffer + :param int iobuf_size: + :param ~spinn_machine.CoreSubsets core_subsets: + :rtype: iterable(IOBuffer) """ # Get the iobuf address for each core for core_subset in core_subsets: @@ -171,7 +174,6 @@ def read_iobuf(self, iobuf_size, core_subsets): y = core_subset.y for p in core_subset.processor_ids: iobuf = "" - for item in itervalues(self._iobuf[x, y, p]): + for item in self._iobuf[x, y, p].values(): iobuf += item.decode(_ENCODING) - yield IOBuffer(x, y, p, iobuf) diff --git a/spinnman/processes/read_memory_process.py b/spinnman/processes/read_memory_process.py index 607d672d7..e582557c9 100644 --- a/spinnman/processes/read_memory_process.py +++ b/spinnman/processes/read_memory_process.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import functools from spinnman.messages.scp.impl import ReadLink, ReadMemory @@ -20,25 +19,48 @@ class ReadMemoryProcess(AbstractMultiConnectionProcess): - """ A process for reading memory on a SpiNNaker chip. + """ + A process for reading memory on a SpiNNaker chip. """ __slots__ = [ "_view"] def __init__(self, connection_selector): - super(ReadMemoryProcess, self).__init__(connection_selector) + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + """ + super().__init__(connection_selector) self._view = None - def handle_response(self, offset, response): + def __handle_response(self, offset, response): self._view[offset:offset + response.length] = response.data[ response.offset:response.offset + response.length] def read_memory(self, x, y, p, base_address, length): + """ + :param int x: + :param int y: + :param int p: + :param int base_address: + :param int length: + :rtype: bytearray + """ return self._read_memory( base_address, length, functools.partial(ReadMemory, x=x, y=y, cpu=p)) def read_link_memory(self, x, y, p, link, base_address, length): + """ + :param int x: + :param int y: + :param int p: + :param int link: + :param int base_address: + :param int length: + :rtype: bytearray + """ return self._read_memory( base_address, length, functools.partial(ReadLink, x=x, y=y, cpu=p, link=link)) @@ -50,11 +72,10 @@ def _read_memory(self, base_address, length, packet_class): offset = 0 while n_bytes > 0: bytes_to_get = min((n_bytes, UDP_MESSAGE_MAX_SIZE)) - response_handler = functools.partial(self.handle_response, offset) self._send_request( packet_class( base_address=base_address + offset, size=bytes_to_get), - response_handler) + functools.partial(self.__handle_response, offset)) n_bytes -= bytes_to_get offset += bytes_to_get diff --git a/spinnman/processes/read_router_diagnostics_process.py b/spinnman/processes/read_router_diagnostics_process.py index f1f72620f..6a9cb8179 100644 --- a/spinnman/processes/read_router_diagnostics_process.py +++ b/spinnman/processes/read_router_diagnostics_process.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct from spinnman.messages.scp.impl import ReadMemory @@ -23,8 +22,8 @@ class ReadRouterDiagnosticsProcess(AbstractMultiConnectionProcess): - """ A process for reading the diagnostic data block from a SpiNNaker\ - router. + """ + A process for reading the diagnostic data block from a SpiNNaker router. """ __slots__ = [ "_control_register", @@ -32,31 +31,41 @@ class ReadRouterDiagnosticsProcess(AbstractMultiConnectionProcess): "_register_values"] def __init__(self, connection_selector): - super(ReadRouterDiagnosticsProcess, self).__init__(connection_selector) + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + """ + super().__init__(connection_selector) self._control_register = None self._error_status = None self._register_values = [0] * _N_REGISTERS - def handle_control_register_response(self, response): + def __handle_control_register_response(self, response): self._control_register = _ONE_WORD.unpack_from( response.data, response.offset)[0] - def handle_error_status_response(self, response): + def __handle_error_status_response(self, response): self._error_status = _ONE_WORD.unpack_from( response.data, response.offset)[0] - def handle_register_response(self, response): + def __handle_register_response(self, response): for register in range(_N_REGISTERS): self._register_values[register] = _ONE_WORD.unpack_from( response.data, response.offset + (register * 4))[0] def get_router_diagnostics(self, x, y): + """ + :param int x: + :param int y: + :rtype: RouterDiagnostics + """ self._send_request(ReadMemory(x, y, 0xe1000000, 4), - self.handle_control_register_response) + self.__handle_control_register_response) self._send_request(ReadMemory(x, y, 0xe1000014, 4), - self.handle_error_status_response) + self.__handle_error_status_response) self._send_request(ReadMemory(x, y, 0xe1000300, 16 * 4), - self.handle_register_response) + self.__handle_register_response) self._finish() self.check_for_error() diff --git a/spinnman/processes/round_robin_connection_selector.py b/spinnman/processes/round_robin_connection_selector.py index b8dbb8820..9b80e35b7 100644 --- a/spinnman/processes/round_robin_connection_selector.py +++ b/spinnman/processes/round_robin_connection_selector.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from spinn_utilities.overrides import overrides from .abstract_multi_connection_process_connection_selector import ( @@ -20,12 +19,18 @@ class RoundRobinConnectionSelector( AbstractMultiConnectionProcessConnectionSelector): + """ + A connection selector that just spreads work as evenly as possible. + """ __slots__ = [ "_connections", "_next_connection_index"] - @overrides(AbstractMultiConnectionProcessConnectionSelector.__init__) def __init__(self, connections): + """ + :param list(SCAMPConnection) connections: + The connections to be used + """ self._connections = connections self._next_connection_index = 0 diff --git a/spinnman/processes/send_single_command_process.py b/spinnman/processes/send_single_command_process.py index 3779eecf7..05af20315 100644 --- a/spinnman/processes/send_single_command_process.py +++ b/spinnman/processes/send_single_command_process.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. from .abstract_multi_connection_process import AbstractMultiConnectionProcess from spinnman.constants import SCP_TIMEOUT @@ -22,15 +21,24 @@ class SendSingleCommandProcess(AbstractMultiConnectionProcess): "_response"] def __init__(self, connection_selector, n_retries=3, timeout=SCP_TIMEOUT): - super(SendSingleCommandProcess, self).__init__( + """ + :param connection_selector: + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + """ + super().__init__( connection_selector, n_retries=n_retries, timeout=timeout) self._response = None - def handle_response(self, response): + def __handle_response(self, response): self._response = response def execute(self, request): - self._send_request(request, self.handle_response) + """ + :param AbstractSCPRequest request: + :rtype: AbstractSCPResponse + """ + self._send_request(request, self.__handle_response) self._finish() self.check_for_error() return self._response diff --git a/spinnman/processes/write_memory_process.py b/spinnman/processes/write_memory_process.py index 492bd55ed..e5f63a15c 100644 --- a/spinnman/processes/write_memory_process.py +++ b/spinnman/processes/write_memory_process.py @@ -1,86 +1,135 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import functools from spinnman.messages.scp.impl import WriteLink, WriteMemory from .abstract_multi_connection_process import AbstractMultiConnectionProcess from spinnman.constants import UDP_MESSAGE_MAX_SIZE +import numpy class WriteMemoryProcess(AbstractMultiConnectionProcess): - """ A process for writing memory on a SpiNNaker chip. + """ + A process for writing memory on a SpiNNaker chip. """ __slots__ = [] # pylint: disable=too-many-arguments def write_memory_from_bytearray( - self, x, y, p, base_address, data, offset, n_bytes): - """ Writes memory onto a SpiNNaker chip from a bytearray. + self, x, y, p, base_address, data, offset, n_bytes, + get_sum=False): + """ + Writes memory onto a SpiNNaker chip from a bytearray. - :param x: \ + :param int x: The x-coordinate of the chip where the memory is to be written to - :param y: \ + :param int y: The y-coordinate of the chip where the memory is to be written to - :param p: \ + :param int p: The processor of the chip where the memory is to be written to - :param processor_address: the (x, y, p) coords of the chip in question - :param base_address: the address in SDRAM to start writing + :param int base_address: the address in SDRAM to start writing :param data: the data to write :type data: bytearray or bytes - :param offset: where in the data to start writing from - :param n_bytes: how much data to write - :rtype: None + :param int offset: where in the data to start writing from + :param int n_bytes: how much data to write + :param bool get_sum: whether to return a checksum or 0 + :return: the data checksum or 0 if get_sum is False + :rtype: int """ - self._write_memory_from_bytearray( + return self._write_memory_from_bytearray( base_address, data, offset, n_bytes, - functools.partial(WriteMemory, x=x, y=y, cpu=p)) + functools.partial(WriteMemory, x=x, y=y, cpu=p), get_sum) def write_link_memory_from_bytearray( - self, x, y, p, link, base_address, data, offset, n_bytes): - self._write_memory_from_bytearray( + self, x, y, p, link, base_address, data, offset, n_bytes, + get_sum=False): + """ + Writes memory onto a neighbour of a SpiNNaker chip from a bytearray. + + :param int x: + The x-coordinate of the chip where the memory is to be written to + :param int y: + The y-coordinate of the chip where the memory is to be written to + :param int p: + The processor of the chip where the memory is to be written to + :param int link: + Along which link is the neighbour. + :param int base_address: the address in SDRAM to start writing + :param data: the data to write + :type data: bytearray or bytes + :param int offset: where in the data to start writing from + :param int n_bytes: how much data to write + :param bool get_sum: whether to return a checksum or 0 + :return: the data checksum or 0 if get_sum is False + :rtype: int + """ + return self._write_memory_from_bytearray( base_address, data, offset, n_bytes, - functools.partial(WriteLink, x=x, y=y, cpu=p, link=link)) + functools.partial(WriteLink, x=x, y=y, cpu=p, link=link), get_sum) def write_memory_from_reader( - self, x, y, p, base_address, reader, n_bytes): - """ Writes memory onto a SpiNNaker chip from a reader. + self, x, y, p, base_address, reader, n_bytes, get_sum=False): + """ + Writes memory onto a SpiNNaker chip from a reader. - :param x: \ + :param int x: The x-coordinate of the chip where the memory is to be written to - :param y: \ + :param int y: The y-coordinate of the chip where the memory is to be written to - :param p: \ + :param int p: The processor of the chip where the memory is to be written to - :param base_address: the address in SDRAM to start writing + :param int base_address: the address in SDRAM to start writing :param reader: the readable object containing the data to write - :type reader: :py:class:`io.RawIOBase` or :py:class:`io.BufferedIOBase` - :param n_bytes: how much data to write - :rtype: None + :type reader: ~io.RawIOBase or ~io.BufferedIOBase + :param int n_bytes: how much data to write + :param bool get_sum: whether to return a checksum or 0 + :return: the data checksum or 0 if get_sum is False + :rtype: int """ - self._write_memory_from_reader( + return self._write_memory_from_reader( base_address, reader, n_bytes, - functools.partial(WriteMemory, x=x, y=y, cpu=p)) + functools.partial(WriteMemory, x=x, y=y, cpu=p), get_sum) def write_link_memory_from_reader( - self, x, y, p, link, base_address, reader, n_bytes): - self._write_memory_from_reader( + self, x, y, p, link, base_address, reader, n_bytes, + get_sum=False): + """ + Writes memory onto a neighbour of a SpiNNaker chip from a reader. + + :param int x: + The x-coordinate of the chip where the memory is to be written to + :param int y: + The y-coordinate of the chip where the memory is to be written to + :param int p: + The processor of the chip where the memory is to be written to + :param int link: + Along which link is the neighbour. + :param int base_address: the address in SDRAM to start writing + :param reader: the readable object containing the data to write + :type reader: ~io.RawIOBase or ~io.BufferedIOBase + :param int n_bytes: how much data to write + :param bool get_sum: whether to return a checksum or 0 + :return: the data checksum or 0 if get_sum is False + :rtype: int + """ + return self._write_memory_from_reader( base_address, reader, n_bytes, - functools.partial(WriteLink, x=x, y=y, cpu=p, link=link)) + functools.partial(WriteLink, x=x, y=y, cpu=p, link=link), get_sum) def _write_memory_from_bytearray( - self, base_address, data, data_offset, n_bytes, packet_class): + self, base_address, data, data_offset, n_bytes, packet_class, + get_sum): offset = 0 n_bytes_to_write = int(n_bytes) while n_bytes_to_write > 0: @@ -95,20 +144,32 @@ def _write_memory_from_bytearray( data_offset += bytes_to_send self._finish() self.check_for_error() + if not get_sum: + return 0 + np_data = numpy.array(data, dtype="uint8") + np_sum = int(numpy.sum(np_data.view("uint32"), dtype="uint32")) + return np_sum & 0xFFFFFFFF def _write_memory_from_reader( - self, base_address, reader, n_bytes, packet_class): + self, base_address, reader, n_bytes, packet_class, with_sum): offset = 0 n_bytes_to_write = int(n_bytes) + chksum = 0 while n_bytes_to_write > 0: data_array = reader.read( min((n_bytes_to_write, UDP_MESSAGE_MAX_SIZE))) bytes_to_send = len(data_array) - self._send_request(packet_class( base_address=base_address + offset, data=data_array)) n_bytes_to_write -= bytes_to_send offset += bytes_to_send + + if with_sum: + np_data = numpy.frombuffer(data_array, dtype="uint8") + np_sum = int(numpy.sum(np_data.view("uint32"))) + chksum = (chksum + np_sum) & 0xFFFFFFFF + self._finish() self.check_for_error() + return chksum diff --git a/spinnman/spalloc/__init__.py b/spinnman/spalloc/__init__.py new file mode 100644 index 000000000..d263d132f --- /dev/null +++ b/spinnman/spalloc/__init__.py @@ -0,0 +1,43 @@ +# Copyright (c) 2017 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +The new Spalloc client implementation. This has the notable distinction of +including a proxying system that allows creating a transceiver with access to +the boards of a job despite the client being not within the same firewall/NAT +security domain as the Spalloc-managed service. + +The main class in here is :py:class:`~spinnman.spalloc.SpallocClient`. +""" + +from .abstract_spalloc_client import AbstractSpallocClient +from .spalloc_job import SpallocJob +from .spalloc_machine import SpallocMachine +from .spalloc_proxied_connection import SpallocProxiedConnection +from .spalloc_eieio_connection import SpallocEIEIOConnection +from .spalloc_eieio_listener import SpallocEIEIOListener +from .spalloc_state import SpallocState +from .spalloc_client import SpallocClient +from .utils import is_server_address + +__all__ = ( + "AbstractSpallocClient", + "is_server_address", + "SpallocClient", + "SpallocJob", + "SpallocMachine", + "SpallocProxiedConnection", + "SpallocEIEIOConnection", + "SpallocEIEIOListener", + "SpallocState") diff --git a/spinnman/spalloc/abstract_spalloc_client.py b/spinnman/spalloc/abstract_spalloc_client.py new file mode 100644 index 000000000..700175669 --- /dev/null +++ b/spinnman/spalloc/abstract_spalloc_client.py @@ -0,0 +1,120 @@ +# Copyright (c) 2022 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +API of the client for the Spalloc web service. +""" + +import struct +from typing import Dict, Iterable, Tuple +from spinn_utilities.abstract_base import (AbstractBase, abstractmethod) +from .spalloc_machine import SpallocMachine +from .spalloc_job import SpallocJob + +_ONE_SHORT = struct.Struct(" Dict[str, SpallocMachine]: + """ + Get the machines supported by the server. + + :return: + Mapping from machine names to handles for working with a machine. + :rtype: dict(str,SpallocMachine) + """ + + @abstractmethod + def list_jobs(self, deleted: bool = False) -> Iterable[SpallocJob]: + """ + Get the jobs known to the server. + + :param bool deleted: Whether to include deleted jobs. + :return: The jobs known to the server. + :rtype: ~typing.Iterable(SpallocJob) + """ + + @abstractmethod + def create_job( + self, num_boards: int = 1, machine_name: str = None, + keepalive: int = 45) -> SpallocJob: + """ + Create a job with a specified number of boards. + + :param int num_boards: + How many boards to ask for (defaults to 1) + :param str machine_name: + Which machine to run on? If omitted, the service's machine tagged + with ``default`` will be used. + :param int keepalive: + After how many seconds of no activity should a job become eligible + for automatic pruning? + :return: A handle for monitoring and interacting with the job. + :rtype: SpallocJob + """ + + @abstractmethod + def create_job_rect( + self, width: int, height: int, machine_name: str = None, + keepalive: int = 45) -> SpallocJob: + """ + Create a job with a rectangle of boards. + + :param int width: + The width of rectangle to request + :param int height: + The height of rectangle to request + :param str machine_name: + Which machine to run on? If omitted, the service's machine tagged + with ``default`` will be used. + :param int keepalive: + After how many seconds of no activity should a job become eligible + for automatic pruning? + :return: A handle for monitoring and interacting with the job. + :rtype: SpallocJob + """ + + @abstractmethod + def create_job_board( + self, triad: Tuple[int, int, int] = None, + physical: Tuple[int, int, int] = None, ip_address: str = None, + machine_name: str = None, keepalive: int = 45) -> SpallocJob: + """ + Create a job with a specific board. At least one of ``triad``, + ``physical`` and ``ip_address`` must be not ``None``. + + :param tuple(int,int,int) triad: + The logical coordinate of the board to request + :param tuple(int,int,int) physical: + The physical coordinate of the board to request + :param str ip_address: + The IP address of the board to request + :param str machine_name: + Which machine to run on? If omitted, the service's machine tagged + with ``default`` will be used. + :param int keepalive: + After how many seconds of no activity should a job become eligible + for automatic pruning? + :return: A handle for monitoring and interacting with the job. + :rtype: SpallocJob + """ diff --git a/spinnman/spalloc/proxy_protocol.py b/spinnman/spalloc/proxy_protocol.py new file mode 100644 index 000000000..09ab6bc90 --- /dev/null +++ b/spinnman/spalloc/proxy_protocol.py @@ -0,0 +1,32 @@ +# Copyright (c) 2021 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from enum import IntEnum + + +class ProxyProtocol(IntEnum): + """ + Websocket binary frame type identifiers in the Spalloc Proxy protocol. + """ + #: Message relating to opening a channel + OPEN = 0 + #: Message relating to closing a channel + CLOSE = 1 + #: Message sent on a channel + MSG = 2 + #: Message relating to opening an unbound listen-only channel + OPEN_UNBOUND = 3 + #: Message sent on an unbound channel to a given board + MSG_TO = 4 + #: Message relating to an error when opening or closing a channel + ERROR = 5 diff --git a/spinnman/spalloc/session.py b/spinnman/spalloc/session.py new file mode 100644 index 000000000..98b0dc314 --- /dev/null +++ b/spinnman/spalloc/session.py @@ -0,0 +1,360 @@ +# Copyright (c) 2021 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from functools import wraps +from logging import getLogger +import re +import requests +from typing import Dict, Tuple +import websocket +from spinn_utilities.log import FormatAdapter +from .utils import clean_url +from spinnman.exceptions import SpallocException + +logger = FormatAdapter(getLogger(__name__)) +#: The name of the session cookie issued by Spring Security +_SESSION_COOKIE = "JSESSIONID" +#: Enable detailed debugging by setting to True +_debug_pretty_print = False + + +def _may_renew(method): + def pp_req(req: requests.PreparedRequest): + """ + :param ~requests.PreparedRequest req: + """ + print('{}\n{}\r\n{}\r\n\r\n{}'.format( + '>>>>>>>>>>>START>>>>>>>>>>>', + req.method + ' ' + req.url, + '\r\n'.join('{}: {}'.format(*kv) for kv in req.headers.items()), + req.body if req.body else "")) + + def pp_resp(resp: requests.Response): + """ + :param ~requests.Response resp: + """ + print('{}\n{}\r\n{}\r\n\r\n{}'.format( + '<<<<<<<<<<= 200 and code < 400: + return response + result = response.content + raise ValueError(f"Unexpected response from server {code}\n" + f" {str(result)}") + + @_may_renew + def get(self, url: str, timeout: int = 10, **kwargs) -> requests.Response: + """ + Do an HTTP ``GET`` in the session. + + :param str url: + :param int timeout: + :rtype: ~requests.Response + :raise ValueError: If the server rejects a request + """ + params = kwargs if kwargs else None + cookies = {_SESSION_COOKIE: self._session_id} + r = requests.get(url, params=params, cookies=cookies, + allow_redirects=False, timeout=timeout) + logger.debug("GET {} returned {}", url, r.status_code) + return self.__handle_error_or_return(r) + + @_may_renew + def post(self, url: str, jsonobj: dict, timeout: int = 10, + **kwargs) -> requests.Response: + """ + Do an HTTP ``POST`` in the session. + + :param str url: + :param int timeout: + :param dict jsonobj: + :rtype: ~requests.Response + :raise ValueError: If the server rejects a request + """ + params = kwargs if kwargs else None + cookies, headers = self._credentials + r = requests.post(url, params=params, json=jsonobj, + cookies=cookies, headers=headers, + allow_redirects=False, timeout=timeout) + logger.debug("POST {} returned {}", url, r.status_code) + return self.__handle_error_or_return(r) + + @_may_renew + def put(self, url: str, data: str, timeout: int = 10, + **kwargs) -> requests.Response: + """ + Do an HTTP ``PUT`` in the session. Puts plain text *OR* JSON! + + :param str url: + :param str data: + :param int timeout: + :rtype: ~requests.Response + :raise ValueError: If the server rejects a request + """ + params = kwargs if kwargs else None + cookies, headers = self._credentials + if isinstance(data, str): + headers["Content-Type"] = "text/plain; charset=UTF-8" + r = requests.put(url, params=params, data=data, + cookies=cookies, headers=headers, + allow_redirects=False, timeout=timeout) + logger.debug("PUT {} returned {}", url, r.status_code) + return self.__handle_error_or_return(r) + + @_may_renew + def delete(self, url: str, timeout: int = 10, + **kwargs) -> requests.Response: + """ + Do an HTTP ``DELETE`` in the session. + + :param str url: + :rtype: ~requests.Response + :raise ValueError: If the server rejects a request + """ + params = kwargs if kwargs else None + cookies, headers = self._credentials + r = requests.delete(url, params=params, cookies=cookies, + headers=headers, allow_redirects=False, + timeout=timeout) + logger.debug("DELETE {} returned {}", url, r.status_code) + return self.__handle_error_or_return(r) + + def renew(self) -> dict: + """ + Renews the session, logging the user into it so that state modification + operations can be performed. + + :returns: Description of the root of the service, without CSRF data + :rtype: dict + :raises SpallocException: + If the session cannot be renewed. + """ + if self.__token: + r = requests.get( + self.__login_form_url, + headers={"Authorization": f"Bearer {self.__token}"}, + allow_redirects=False, timeout=10) + if not r.ok: + raise SpallocException(f"Could not renew session: {r.content}") + self._session_id = r.cookies[_SESSION_COOKIE] + else: + # Step one: a temporary session so we can log in + csrf_matcher = re.compile( + r"""""") + r = requests.get(self.__login_form_url, allow_redirects=False, + timeout=10) + logger.debug("GET {} returned {}", + self.__login_form_url, r.status_code) + m = csrf_matcher.search(r.text) + if not m: + raise SpallocException("could not establish temporary session") + csrf = m.group(1) + session = r.cookies[_SESSION_COOKIE] + + # Step two: actually do the log in + form = { + "_csrf": csrf, + "username": self.__username, + "password": self.__password, + "submit": "submit" + } + # NB: returns redirect that sets a cookie + r = requests.post(self.__login_submit_url, + cookies={_SESSION_COOKIE: session}, + allow_redirects=False, + data=form, timeout=10) + logger.debug("POST {} returned {}", + self.__login_submit_url, r.status_code) + self._session_id = r.cookies[_SESSION_COOKIE] + # We don't need to follow that redirect + + # Step three: get the basic service data and new CSRF token + obj = self.get(self.__srv_base).json() + self.__csrf_header = obj["csrf-header"] + self.__csrf = obj["csrf-token"] + del obj["csrf-header"] + del obj["csrf-token"] + return obj + + @property + def _credentials(self) -> Tuple[Dict[str, str], Dict[str, str]]: + """ + The credentials for requests. *Serializable.* + """ + cookies = {_SESSION_COOKIE: self._session_id} + headers = {self.__csrf_header: self.__csrf} + if self.__token: + # This would be better off done once per session only + headers["Authorization"] = f"Bearer {self.__token}" + return cookies, headers + + def websocket( + self, url: str, header: dict = None, cookie: str = None, + **kwargs) -> websocket.WebSocket: + """ + Create a websocket that uses the session credentials to establish + itself. + + :param str url: Actual location to open websocket at + :param dict(str,str) header: Optional HTTP headers + :param str cookie: + Optional cookies (composed as semicolon-separated string) + :param kwargs: Other options to :py:func:`~websocket.create_connection` + :rtype: ~websocket.WebSocket + """ + # Note: *NOT* a renewable action! + if header is None: + header = {} + header[self.__csrf_header] = self.__csrf + if cookie is not None: + cookie += ";" + _SESSION_COOKIE + "=" + self._session_id + else: + cookie = _SESSION_COOKIE + "=" + self._session_id + return websocket.create_connection( + url, header=header, cookie=cookie, **kwargs) + + def _purge(self): + """ + Clears out all credentials from this session, rendering the session + completely inoperable henceforth. + """ + self.__username = None + self.__password = None + self._session_id = None + self.__csrf = None + + +class SessionAware: + """ + Connects to the session. + + .. warning:: + This class does not present a stable API for public consumption. + """ + __slots__ = ("__session", "_url") + + def __init__(self, session: Session, url: str): + self.__session = session + self._url = clean_url(url) + + @property + def _session_credentials(self): + """ + The current session credentials. + Only supposed to be called by subclasses. + + :rtype: tuple(dict(str,str),dict(str,str)) + """ + # pylint: disable=protected-access + return self.__session._credentials + + @property + def _service_url(self): + """ + The main service URL. + + :rtype: str + """ + # pylint: disable=protected-access + return self.__session._service_url + + def _get(self, url: str, **kwargs) -> requests.Response: + return self.__session.get(url, **kwargs) + + def _post(self, url: str, jsonobj: dict, **kwargs) -> requests.Response: + return self.__session.post(url, jsonobj, **kwargs) + + def _put(self, url: str, data: str, **kwargs) -> requests.Response: + return self.__session.put(url, data, **kwargs) + + def _delete(self, url: str, **kwargs) -> requests.Response: + return self.__session.delete(url, **kwargs) + + def _websocket(self, url: str, **kwargs) -> websocket.WebSocket: + """ + Create a websocket that uses the session credentials to establish + itself. + + :param str url: Actual location to open websocket at + :rtype: ~websocket.WebSocket + """ + return self.__session.websocket(url, **kwargs) diff --git a/spinnman/spalloc/spalloc_boot_connection.py b/spinnman/spalloc/spalloc_boot_connection.py new file mode 100644 index 000000000..1e95b65a7 --- /dev/null +++ b/spinnman/spalloc/spalloc_boot_connection.py @@ -0,0 +1,60 @@ +# Copyright (c) 2022 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +API of the client for the Spalloc web service. +""" + +import struct +import time +from typing import Callable +from spinn_utilities.abstract_base import AbstractBase +from spinn_utilities.overrides import overrides +from spinnman.connections.abstract_classes import Listenable +from spinnman.connections.udp_packet_connections import BootConnection +from spinnman.connections.udp_packet_connections.boot_connection import ( + _ANTI_FLOOD_DELAY) +from spinnman.messages.spinnaker_boot import SpinnakerBootMessage +from .spalloc_proxied_connection import SpallocProxiedConnection + +_ONE_SHORT = struct.Struct(" SpinnakerBootMessage: + data = self.receive(timeout) + return SpinnakerBootMessage.from_bytestring(data, 0) + + @overrides(Listenable.get_receive_method) + def get_receive_method(self) -> Callable: + return self.receive_boot_message diff --git a/spinnman/spalloc/spalloc_client.py b/spinnman/spalloc/spalloc_client.py new file mode 100644 index 000000000..90bd74179 --- /dev/null +++ b/spinnman/spalloc/spalloc_client.py @@ -0,0 +1,972 @@ +# Copyright (c) 2021 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Implementation of the client for the Spalloc web service. +""" + +from logging import getLogger +from multiprocessing import Process, Queue +from time import sleep +from packaging.version import Version +import queue +import requests +import struct +import threading +from typing import Dict, List, Tuple +from websocket import WebSocket +from spinn_utilities.abstract_base import AbstractBase, abstractmethod +from spinn_utilities.abstract_context_manager import AbstractContextManager +from spinn_utilities.log import FormatAdapter +from spinn_utilities.overrides import overrides +from spinnman.connections.udp_packet_connections import UDPConnection +from spinnman.connections.abstract_classes import Connection, Listenable +from spinnman.constants import SCP_SCAMP_PORT, UDP_BOOT_CONNECTION_DEFAULT_PORT +from spinnman.exceptions import SpinnmanTimeoutException +from spinnman.exceptions import SpallocException +from spinnman.transceiver import Transceiver +from .spalloc_state import SpallocState +from .proxy_protocol import ProxyProtocol +from .session import Session, SessionAware +from .utils import parse_service_url, get_hostname +from .abstract_spalloc_client import AbstractSpallocClient +from .spalloc_machine import SpallocMachine +from .spalloc_job import SpallocJob +from .spalloc_proxied_connection import SpallocProxiedConnection +from .spalloc_boot_connection import SpallocBootConnection +from .spalloc_eieio_connection import SpallocEIEIOConnection +from .spalloc_eieio_listener import SpallocEIEIOListener +from .spalloc_scp_connection import SpallocSCPConnection + +logger = FormatAdapter(getLogger(__name__)) +_open_req = struct.Struct(" SpallocJob: + """ + Create a job from the description in the attached database. This is + intended to allow for access to the job's allocated resources from + visualisers and other third party code participating in the SpiNNaker + Tools Notification Protocol. + + .. note:: + The job is not verified to exist and be running. The session + credentials may have expired; if so, the job will be unable to + regenerate them. + + :param str service_url: + :param str job_url: + :param dict(str, str) cookies: + :param dict(str, str) headers: + + :return: + The job handle, or ``None`` if the records in the database are + absent or incomplete. + :rtype: SpallocJob + """ + session = Session(service_url, session_credentials=(cookies, headers)) + return _SpallocJob(session, job_url) + + @overrides(AbstractSpallocClient.list_machines) + def list_machines(self): + obj = self.__session.get(self.__machines_url).json() + return {m["name"]: _SpallocMachine(self, m) for m in obj["machines"]} + + @overrides(AbstractSpallocClient.list_jobs) + def list_jobs(self, deleted=False): + obj = self.__session.get( + self.__jobs_url, + deleted=("true" if deleted else "false")).json() + while obj["jobs"]: + for u in obj["jobs"]: + yield _SpallocJob(self.__session, u) + if "next" not in obj: + break + obj = self.__session.get(obj["next"]).json() + + def _create(self, create, machine_name): + if machine_name: + create["machine-name"] = machine_name + else: + create["tags"] = ["default"] + if self.__group is not None: + create["group"] = self.__group + if self.__collab is not None: + create["nmpi-collab"] = self.__collab + if self.__nmpi_job is not None: + create["nmpi-job-id"] = self.__nmpi_job + if self.__nmpi_user is not None: + create["owner"] = self.__nmpi_user + r = self.__session.post(self.__jobs_url, create, timeout=30) + url = r.headers["Location"] + return _SpallocJob(self.__session, url) + + @overrides(AbstractSpallocClient.create_job) + def create_job(self, num_boards=1, machine_name=None, keepalive=45): + return self._create({ + "num-boards": int(num_boards), + "keepalive-interval": f"PT{int(keepalive)}S" + }, machine_name) + + @overrides(AbstractSpallocClient.create_job_rect) + def create_job_rect(self, width, height, machine_name=None, keepalive=45): + return self._create({ + "dimensions": { + "width": int(width), + "height": int(height) + }, + "keepalive-interval": f"PT{int(keepalive)}S" + }, machine_name) + + @overrides(AbstractSpallocClient.create_job_board) + def create_job_board( + self, triad=None, physical=None, ip_address=None, + machine_name=None, keepalive=45): + if triad: + x, y, z = triad + board = {"x": int(x), "y": int(y), "z": int(z)} + elif physical: + c, f, b = physical + board = {"cabinet": int(c), "frame": int(f), "board": int(b)} + elif ip_address: + board = {"address": str(ip_address)} + else: + raise KeyError("at least one of triad, physical and ip_address " + "must be given") + return self._create({ + "board": board, + "keepalive-interval": f"PT{int(keepalive)}S" + }, machine_name) + + def close(self): + # pylint: disable=protected-access + if self.__session is not None: + self.__session._purge() + self.__session = None + + +class _ProxyServiceError(IOError): + """ + An error passed to us from the server over the proxy channel. + """ + + +def _SpallocKeepalive(url, interval, term_queue, cookies, headers): + """ + Actual keepalive task implementation. Don't use directly. + """ + headers["Content-Type"] = "text/plain; charset=UTF-8" + while True: + requests.put(url, data="alive", cookies=cookies, headers=headers, + allow_redirects=False, timeout=10) + try: + term_queue.get(True, interval) + break + except queue.Empty: + continue + + +class _SpallocMachine(SessionAware, SpallocMachine): + """ + Represents a Spalloc-controlled machine. + + Don't make this yourself. Use :py:class:`SpallocClient` instead. + """ + __slots__ = ("__name", "__tags", "__width", "__height", + "__dead_boards", "__dead_links") + + def __init__(self, session, machine_data): + """ + :param _Session session: + :param dict machine_data: + """ + super().__init__(session, machine_data["uri"]) + self.__name = machine_data["name"] + self.__tags = frozenset(machine_data["tags"]) + self.__width = machine_data["width"] + self.__height = machine_data["height"] + self.__dead_boards = machine_data["dead-boards"] + self.__dead_links = machine_data["dead-links"] + + @property + @overrides(SpallocMachine.name) + def name(self): + return self.__name + + @property + @overrides(SpallocMachine.tags) + def tags(self): + return self.__tags + + @property + @overrides(SpallocMachine.width) + def width(self): + return self.__width + + @property + @overrides(SpallocMachine.height) + def height(self): + return self.__height + + @property + @overrides(SpallocMachine.dead_boards) + def dead_boards(self): + return self.__dead_boards + + @property + @overrides(SpallocMachine.dead_links) + def dead_links(self): + return self.__dead_links + + @property + @overrides(SpallocMachine.area) + def area(self): + return (self.width, self.height) + + def __repr__(self): + return "SpallocMachine" + str(( + self.name, self.tags, self.width, self.height, self.dead_boards, + self.dead_links)) + + +class _ProxyPing(threading.Thread): + """ + Sends ping messages to an open websocket + """ + + def __init__(self, ws, sleep_time=30): + super().__init__(daemon=True) + self.__ws = ws + self.__sleep_time = sleep_time + self.__closed = False + self.start() + + def run(self): + """ + The handler loop of this thread + """ + while self.__ws.connected: + try: + self.__ws.ping() + except Exception: # pylint: disable=broad-except + # If closed, ignore error and get out of here + if self.__closed: + break + + # Make someone aware of the error + logger.exception("Error in websocket before close") + sleep(self.__sleep_time) + + def close(self): + """ + Mark as closed to avoid error messages. + """ + self.__closed = True + + +class _ProxyReceiver(threading.Thread): + """ + Receives all messages off an open websocket and dispatches them to + registered listeners. + """ + + def __init__(self, ws): + super().__init__(daemon=True) + self.__ws = ws + self.__returns = {} + self.__handlers = {} + self.__correlation_id = 0 + self.__closed = False + self.start() + + def run(self): + """ + The handler loop of this thread. + """ + while self.__ws.connected: + try: + result = self.__ws.recv_data() + frame = result[1] + if len(frame) < _msg.size: + # Message is out of protocol + continue + except Exception: # pylint: disable=broad-except + # If closed, ignore error and get out of here + if self.__closed: + break + + # Make someone aware of the error + logger.exception("Error in websocket before close") + + # If we are disconnected before closing, make errors happen + if not self.__ws.connected: + for rt in self.__returns.values(): + rt(None) + for hd in self.__handlers.values(): + hd(None) + break + code, num = _msg.unpack_from(frame, 0) + if code == ProxyProtocol.MSG: + self.dispatch_message(num, frame) + else: + self.dispatch_return(num, frame) + + def expect_return(self, handler) -> int: + """ + Register a one-shot listener for a call-like message's return. + + :return: The correlation ID + """ + c = self.__correlation_id + self.__correlation_id += 1 + self.__returns[c] = handler + return c + + def listen(self, channel_id: int, handler): + """ + Register a persistent listener for one-way messages. + """ + self.__handlers[channel_id] = handler + + def dispatch_return(self, correlation_id: int, msg: bytes): + """ + Dispatch a received call-return message. + """ + handler = self.__returns.pop(correlation_id, None) + if handler: + handler(msg) + + def dispatch_message(self, channel_id: int, msg: bytes): + """ + Dispatch a received one-way message. + """ + handler = self.__handlers.get(channel_id, None) + if handler: + handler(msg) + + def unlisten(self, channel_id): + """ + De-register a listener for a channel + """ + self.__handlers.pop(channel_id) + + def close(self): + """ + Mark receiver closed to avoid errors + """ + self.__closed = True + + +class _SpallocJob(SessionAware, SpallocJob): + """ + Represents a job in Spalloc. + + Don't make this yourself. Use :py:class:`SpallocClient` instead. + """ + __slots__ = ("__machine_url", "__chip_url", + "_keepalive_url", "__keepalive_handle", "__proxy_handle", + "__proxy_thread", "__proxy_ping") + + def __init__(self, session, job_handle): + """ + :param _Session session: + :param str job_handle: + """ + super().__init__(session, job_handle) + logger.info("established job at {}", job_handle) + self.__machine_url = self._url + "machine" + self.__chip_url = self._url + "chip" + self._keepalive_url = self._url + "keepalive" + self.__keepalive_handle = None + self.__proxy_handle = None + self.__proxy_thread = None + self.__proxy_ping = None + + @overrides(SpallocJob.get_session_credentials_for_db) + def get_session_credentials_for_db(self): + config = {} + config["SPALLOC", "service uri"] = self._service_url + config["SPALLOC", "job uri"] = self._url + cookies, headers = self._session_credentials + for k, v in cookies.items(): + config["COOKIE", k] = v + for k, v in headers.items(): + config["HEADER", k] = v + if "Authorization" in headers: + # We never write the auth headers themselves; we just extend the + # session + del headers["Authorization"] + return config + + @overrides(SpallocJob.get_state) + def get_state(self): + obj = self._get(self._url).json() + return SpallocState[obj["state"]] + + @overrides(SpallocJob.get_root_host) + def get_root_host(self): + r = self._get(self.__machine_url) + if r.status_code == 204: + return None + obj = r.json() + for c in obj["connections"]: + [x, y], host = c + if x == 0 and y == 0: + return host + return None + + @overrides(SpallocJob.get_connections) + def get_connections(self): + r = self._get(self.__machine_url) + if r.status_code == 204: + return None + return { + (int(x), int(y)): str(host) + for ((x, y), host) in r.json()["connections"] + } + + @property + def __proxy_url(self): + """ + The URL for talking to the proxy connection system. + """ + r = self._get(self._url) + if r.status_code == 204: + return None + try: + url = r.json()["proxy-ref"] + logger.info("Connecting to proxy on {}", url) + return url + except KeyError: + return None + + def __init_proxy(self): + if self.__proxy_handle is None or not self.__proxy_handle.connected: + self.__proxy_handle = self._websocket( + self.__proxy_url, origin=get_hostname(self._url)) + self.__proxy_thread = _ProxyReceiver(self.__proxy_handle) + self.__proxy_ping = _ProxyPing(self.__proxy_handle) + + @overrides(SpallocJob.connect_to_board) + def connect_to_board(self, x, y, port=SCP_SCAMP_PORT): + self.__init_proxy() + return _ProxiedSCAMPConnection( + self.__proxy_handle, self.__proxy_thread, + int(x), int(y), int(port)) + + @overrides(SpallocJob.connect_for_booting) + def connect_for_booting(self): + self.__init_proxy() + return _ProxiedBootConnection(self.__proxy_handle, self.__proxy_thread) + + @overrides(SpallocJob.open_eieio_connection) + def open_eieio_connection(self, x, y): + self.__init_proxy() + return _ProxiedEIEIOConnection( + self.__proxy_handle, self.__proxy_thread, + int(x), int(y), SCP_SCAMP_PORT) + + @overrides(SpallocJob.open_eieio_listener_connection) + def open_eieio_listener_connection(self): + self.__init_proxy() + return _ProxiedEIEIOListener( + self.__proxy_handle, self.__proxy_thread, self.get_connections()) + + @overrides(SpallocJob.open_udp_listener_connection) + def open_udp_listener_connection(self): + self.__init_proxy() + return _ProxiedUDPListener( + self.__proxy_handle, self.__proxy_thread, self.get_connections()) + + @overrides(SpallocJob.wait_for_state_change) + def wait_for_state_change(self, old_state): + while old_state != SpallocState.DESTROYED: + obj = self._get(self._url, wait="true", timeout=None).json() + s = SpallocState[obj["state"]] + if s != old_state or s == SpallocState.DESTROYED: + return s + return old_state + + @overrides(SpallocJob.wait_until_ready) + def wait_until_ready(self): + state = SpallocState.UNKNOWN + while state != SpallocState.READY: + state = self.wait_for_state_change(state) + if state == SpallocState.DESTROYED: + raise SpallocException("job was unexpectedly destroyed") + + @overrides(SpallocJob.destroy) + def destroy(self, reason="finished"): + if self.__keepalive_handle: + self.__keepalive_handle.close() + self.__keepalive_handle = None + if self.__proxy_handle is not None: + self.__proxy_thread.close() + self.__proxy_ping.close() + self.__proxy_handle.close() + self._delete(self._url, reason=str(reason)) + logger.info("deleted job at {}", self._url) + + @overrides(SpallocJob.keepalive) + def keepalive(self): + self._put(self._keepalive_url, "alive") + + @overrides(SpallocJob.launch_keepalive_task) + def launch_keepalive_task(self, period=30): + """ + .. note:: + Tricky! *Cannot* be done with a thread, as the main thread is known + to do significant amounts of CPU-intensive work. + """ + class Closer(AbstractContextManager): + def __init__(self): + self._queue = Queue(1) + self._p = None + + def close(self): + self._queue.put("quit") + # Give it a second, and if it still isn't dead, kill it + p.join(1) + if p.is_alive(): + p.kill() + + self._keepalive_handle = Closer() + # pylint: disable=protected-access + p = Process(target=_SpallocKeepalive, args=( + self._keepalive_url, 0 + period, self._keepalive_handle._queue, + *self._session_credentials), daemon=True) + p.start() + self._keepalive_handle._p = p + return self._keepalive_handle + + @overrides(SpallocJob.where_is_machine) + def where_is_machine(self, x: int, y: int) -> Tuple[int, int, int]: + r = self._get(self.__chip_url, x=int(x), y=int(y)) + if r.status_code == 204: + return None + return tuple(r.json()["physical-board-coordinates"]) + + @property + def _keepalive_handle(self): + return self.__keepalive_handle + + @_keepalive_handle.setter + def _keepalive_handle(self, handle): + if self.__keepalive_handle is not None: + raise SpallocException("cannot keep job alive from two tasks") + self.__keepalive_handle = handle + + @overrides(SpallocJob.create_transceiver) + def create_transceiver(self) -> Transceiver: + if self.get_state() != SpallocState.READY: + raise SpallocException("job not ready to execute scripts") + proxies = [ + self.connect_to_board(x, y) for (x, y) in self.get_connections()] + # Also need a boot connection + proxies.append(self.connect_for_booting()) + return Transceiver(version=5, connections=proxies) + + def __repr__(self): + return f"SpallocJob({self._url})" + + +class _ProxiedConnection(metaclass=AbstractBase): + """ + Core mux/demux emulating a connection that is proxied over a websocket. + + None of the methods are public because subclasses may expose a profile of + them to conform to a particular type of connection. + """ + + def __init__(self, ws: WebSocket, receiver: _ProxyReceiver): + self.__ws = ws + self.__receiver = receiver + self.__msgs = queue.SimpleQueue() + self.__call_queue = queue.Queue(1) + self.__call_lock = threading.RLock() + self.__current_msg = None + self.__handle = self._open_connection() + self.__receiver.listen(self.__handle, self.__msgs.put) + + @abstractmethod + def _open_connection(self) -> int: + pass + + def _call(self, proto: ProxyProtocol, packer: struct.Struct, + unpacker: struct.Struct, *args) -> List[int]: + """ + Do a synchronous call. + + :param proto: + The protocol message number. + :param packer: + How to form the protocol message. The first two arguments passed + will be the protocol message number and an issued correlation ID + (not needed by the caller). + :param unpacker: + How to extract the expected response. + :param args: + Additional arguments to pass to the packer. + :return: + The results from the unpacker *after* the protocol message code and + the correlation ID. + :raises IOError: + If something goes wrong. This could be due to the websocket being + closed, or the receipt of an ERROR response. + """ + if not self._connected: + raise IOError("socket closed") + with self.__call_lock: + # All calls via websocket use correlation_id + correlation_id = self.__receiver.expect_return( + self.__call_queue.put) + self.__ws.send_binary(packer.pack(proto, correlation_id, *args)) + if not self._connected: + raise IOError("socket closed after send!") + reply = self.__call_queue.get() + code, _ = _msg.unpack_from(reply, 0) + if code == ProxyProtocol.ERROR: + # Rest of message is UTF-8 encoded error message string + payload = reply[_msg.size:].decode("utf-8") + if len(payload): + raise _ProxyServiceError(payload) + raise _ProxyServiceError(f"unknown problem with {proto} call") + return unpacker.unpack(reply)[2:] + + @property + def _connected(self) -> bool: + return self.__ws and self.__ws.connected + + def _throw_if_closed(self): + if not self._connected: + raise IOError("socket closed") + + def _close(self): + if self._connected: + channel_id, = self._call( + ProxyProtocol.CLOSE, _close_req, _open_close_res, + self.__handle) + if channel_id != self.__handle: + raise IOError("failed to close proxy socket") + self.__receiver.unlisten(self.__handle) + self.__ws = None + self.__receiver = None + + def _send(self, message: bytes): + self._throw_if_closed() + # Put the header on the front and send it + self.__ws.send_binary(_msg.pack( + ProxyProtocol.MSG, self.__handle) + message) + + def _send_to(self, message: bytes, x: int, y: int, port: int): + self._throw_if_closed() + # Put the header on the front and send it + self.__ws.send_binary(_msg_to.pack( + ProxyProtocol.MSG_TO, self.__handle, x, y, port) + message) + + def __get(self, timeout: float = 0.5) -> bytes: + """ + Get a value from the queue. Handles block/non-block switching and + trimming of the message protocol prefix. + """ + if not timeout: + return self.__msgs.get(block=False)[_msg.size:] + else: + return self.__msgs.get(timeout=timeout)[_msg.size:] + + def _receive(self, timeout=None) -> bytes: + if self.__current_msg is not None: + try: + return self.__current_msg + finally: + self.__current_msg = None + if timeout is None: + while True: + try: + return self.__get() + except queue.Empty: + self._throw_if_closed() + else: + try: + return self.__get(timeout) + except queue.Empty as e: + self._throw_if_closed() + raise SpinnmanTimeoutException("receive", timeout) from e + + def _is_ready_to_receive(self, timeout=0) -> bool: + # If we already have a message or the queue peek succeeds, return now + if self.__current_msg is not None or not self.__msgs.empty(): + return True + try: + self.__current_msg = self.__get(timeout) + return True + except queue.Empty: + return False + + +class _ProxiedBidirectionalConnection( + _ProxiedConnection, SpallocProxiedConnection): + """ + A connection that talks to a particular board via the proxy. + """ + + def __init__( + self, ws: WebSocket, receiver: _ProxyReceiver, + x: int, y: int, port: int): + self.__connect_args = (x, y, port) + super().__init__(ws, receiver) + + @overrides(_ProxiedConnection._open_connection) + def _open_connection(self): + handle, = self._call( + ProxyProtocol.OPEN, _open_req, _open_close_res, + *self.__connect_args) + return handle + + @overrides(Connection.is_connected) + def is_connected(self) -> bool: + return self._connected + + @overrides(Connection.close) + def close(self): + self._close() + + @overrides(SpallocProxiedConnection.send) + def send(self, data: bytes): + if not isinstance(data, (bytes, bytearray)): + data = bytes(data) + self._send(data) + + @overrides(SpallocProxiedConnection.receive) + def receive(self, timeout=None) -> bytes: + return self._receive(timeout) + + @overrides(Listenable.is_ready_to_receive) + def is_ready_to_receive(self, timeout=0) -> bool: + return self._is_ready_to_receive(timeout) + + +class _ProxiedUnboundConnection( + _ProxiedConnection, SpallocProxiedConnection): + """ + A connection that can listen to all boards via the proxy, but which can + only send if a target board is provided. + """ + + def __init__(self, ws: WebSocket, receiver: _ProxyReceiver): + super().__init__(ws, receiver) + self.__addr = None + self.__port = None + + @overrides(_ProxiedConnection._open_connection) + def _open_connection(self) -> int: + handle, ip1, ip2, ip3, ip4, self.__port = self._call( + ProxyProtocol.OPEN_UNBOUND, _open_listen_req, _open_listen_res) + # Assemble the address into the format expected elsewhere + self.__addr = f"{ip1}.{ip2}.{ip3}.{ip4}" + return handle + + @property + def _addr(self) -> str: + return self.__addr if self._connected else None + + @property + def _port(self) -> int: + return self.__port if self._connected else None + + @overrides(Connection.is_connected) + def is_connected(self) -> bool: + return self._connected + + @overrides(Connection.close) + def close(self): + self._close() + + @overrides(SpallocProxiedConnection.send) + def send(self, data: bytes): + self._throw_if_closed() + raise IOError("socket is not open for sending") + + @overrides(SpallocProxiedConnection.receive) + def receive(self, timeout=None) -> bytes: + return self._receive(timeout) + + @overrides(Listenable.is_ready_to_receive) + def is_ready_to_receive(self, timeout=0) -> bool: + return self._is_ready_to_receive(timeout) + + +class _ProxiedSCAMPConnection( + _ProxiedBidirectionalConnection, SpallocSCPConnection): + __slots__ = ("__chip_x", "__chip_y") + + def __init__( + self, ws: WebSocket, receiver: _ProxyReceiver, + x: int, y: int, port: int): + super().__init__(ws, receiver, x, y, port) + SpallocSCPConnection.__init__(self, x, y) + + def __str__(self): + return f"SCAMPConnection[proxied]({self.chip_x},{self.chip_y})" + + +class _ProxiedBootConnection( + _ProxiedBidirectionalConnection, SpallocBootConnection): + __slots__ = () + + def __init__(self, ws: WebSocket, receiver: _ProxyReceiver): + super().__init__(ws, receiver, 0, 0, UDP_BOOT_CONNECTION_DEFAULT_PORT) + + def __str__(self): + return "BootConnection[proxied]()" + + +class _ProxiedEIEIOConnection( + _ProxiedBidirectionalConnection, + SpallocEIEIOConnection, SpallocProxiedConnection): + # Special: This is a unidirectional receive-only connection + __slots__ = ("__addr", "__port", "__chip_x", "__chip_y") + + def __init__( + self, ws: WebSocket, receiver: _ProxyReceiver, + x: int, y: int, port: int): + super().__init__(ws, receiver, x, y, port) + self.__chip_x = x + self.__chip_y = y + + @property + @overrides(SpallocEIEIOConnection._coords) + def _coords(self): + return self.__chip_x, self.__chip_y + + def send_to( + self, + data: bytes, address: tuple): # pylint: disable=unused-argument + """ + Direct ``send_to`` is unsupported. + """ + self._throw_if_closed() + raise IOError("socket is not open for sending") + + def __str__(self): + return (f"EIEIOConnection[proxied](remote:{self.__chip_x}," + f"{self.__chip_y})") + + +class _ProxiedEIEIOListener(_ProxiedUnboundConnection, SpallocEIEIOListener): + __slots__ = ("__conns", ) + + def __init__(self, ws: WebSocket, receiver: _ProxyReceiver, + conns: Dict[Tuple[int, int], str]): + super().__init__(ws, receiver) + # Invert the map + self.__conns = {ip: xy for (xy, ip) in conns.items()} + + @overrides(SpallocEIEIOListener.send_to_chip) + def send_to_chip( + self, message: bytes, x: int, y: int, port: int = SCP_SCAMP_PORT): + if not isinstance(message, (bytes, bytearray)): + message = bytes(message) + self._send_to(bytes(message), x, y, port) + + @property + @overrides(SpallocEIEIOListener.local_ip_address) + def local_ip_address(self) -> str: + return self._addr + + @property + @overrides(SpallocEIEIOListener.local_port) + def local_port(self) -> int: + return self._port + + @overrides(SpallocEIEIOListener._get_chip_coords) + def _get_chip_coords(self, ip_address: str) -> Tuple[int, int]: + return self.__conns[str(ip_address)] + + def __str__(self): + return f"EIEIOConnection[proxied](local:{self._addr}:{self._port})" + + +class _ProxiedUDPListener(_ProxiedUnboundConnection, UDPConnection): + __slots__ = ("__conns", ) + + def __init__(self, ws: WebSocket, receiver: _ProxyReceiver, + conns: Dict[Tuple[int, int], str]): + super().__init__(ws, receiver) + # Invert the map + self.__conns = {ip: xy for (xy, ip) in conns.items()} + + @overrides(UDPConnection.send_to) + def send_to(self, data: bytes, address: Tuple[str, int]): + ip, port = address + x, y = self.__conns[ip] + self._send_to(data, x, y, port) + + @property + @overrides(UDPConnection.local_ip_address) + def local_ip_address(self) -> str: + return self._addr + + @property + @overrides(UDPConnection.local_port) + def local_port(self) -> int: + return self._port + + def __str__(self): + return f"UDPConnection[proxied](local:{self._addr}:{self._port})" diff --git a/spinnman/spalloc/spalloc_eieio_connection.py b/spinnman/spalloc/spalloc_eieio_connection.py new file mode 100644 index 000000000..de029e983 --- /dev/null +++ b/spinnman/spalloc/spalloc_eieio_connection.py @@ -0,0 +1,115 @@ +# Copyright (c) 2022 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +API of the client for the Spalloc web service. +""" + +import struct +from typing import Tuple +from spinn_utilities.abstract_base import ( + AbstractBase, abstractproperty) +from spinn_utilities.overrides import overrides +from spinnman.connections.abstract_classes import Listenable +from spinnman.connections.udp_packet_connections import ( + update_sdp_header_for_udp_send, EIEIOConnection) +from spinnman.exceptions import SpinnmanTimeoutException +from spinnman.messages.eieio import ( + AbstractEIEIOMessage, + read_eieio_command_message, read_eieio_data_message) +from spinnman.messages.sdp import SDPMessage, SDPFlag, SDPHeader +from spinnman.messages.scp.impl import IPTagSet +from .spalloc_proxied_connection import SpallocProxiedConnection + +_ONE_SHORT = struct.Struct(" Tuple[int, int]: + """ + The X, Y coordinates of the chip this connection is connected to. + + :rtype: tuple(int,int) + """ + + def update_tag(self, tag: int, do_receive: bool = True): + """ + Update the given tag on the connected Ethernet-enabled chip to send + messages to this connection. + + :param int tag: The tag ID to update + :param bool do_receive: + Whether to do the reception of the response or not + :raises SpinnmanTimeoutException: + If the message isn't handled within a reasonable timeout. + :raises SpinnmanUnexpectedResponseCodeException: + If the message is rejected by SpiNNaker/SCAMP. + """ + x, y = self._coords + request = IPTagSet( + x, y, [0, 0, 0, 0], 0, tag, strip=True, use_sender=True) + request.sdp_header.flags = SDPFlag.REPLY_EXPECTED_NO_P2P + update_sdp_header_for_udp_send(request.sdp_header, x, y) + data = _TWO_SKIP + request.bytestring + for _try in range(_NUM_UPDATE_TAG_TRIES): + try: + self.send(data) + if do_receive: + response_data = self.receive(_UPDATE_TAG_TIMEOUT) + request.get_scp_response().read_bytestring( + response_data, len(_TWO_SKIP)) + return + except SpinnmanTimeoutException as e: + if _try + 1 == _NUM_UPDATE_TAG_TRIES: + raise e diff --git a/spinnman/spalloc/spalloc_eieio_listener.py b/spinnman/spalloc/spalloc_eieio_listener.py new file mode 100644 index 000000000..333bf9e36 --- /dev/null +++ b/spinnman/spalloc/spalloc_eieio_listener.py @@ -0,0 +1,201 @@ +# Copyright (c) 2022 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +API of the client for the Spalloc web service. +""" + +import struct +from typing import Tuple +from spinn_utilities.abstract_base import ( + AbstractBase, abstractmethod, abstractproperty) +from spinn_utilities.overrides import overrides +from spinnman.connections.abstract_classes import Listenable +from spinnman.connections.udp_packet_connections import ( + update_sdp_header_for_udp_send, EIEIOConnection) +from spinnman.constants import SCP_SCAMP_PORT +from spinnman.exceptions import SpinnmanTimeoutException +from spinnman.messages.eieio import ( + AbstractEIEIOMessage, + read_eieio_command_message, read_eieio_data_message) +from spinnman.messages.sdp import SDPMessage, SDPFlag, SDPHeader +from spinnman.messages.scp.impl import IPTagSet +from .spalloc_proxied_connection import SpallocProxiedConnection + +_ONE_SHORT = struct.Struct(" Tuple[int, int]: + """ + Get the coordinates of a chip given its IP address. + + :param str ip_address: + The IP address of an Ethernet-enabled chip in the job. + :return: Ethernet-enabled chip coordinates: X, Y + :rtype: tuple(int, int) + """ + + @abstractmethod + def send_to_chip( + self, message: bytes, x: int, y: int, port: int = SCP_SCAMP_PORT): + """ + Send a message on an open socket to a particular board. + + :param bytes message: The message to send. + :param int x: + The X coordinate of the Ethernet-enabled chip to send the message + to. + :param int y: + The Y coordinate of the Ethernet-enabled chip to send the message + to. + :param int port: + The UDP port on the Ethernet-enabled chip to send the message to. + Defaults to the SCP port. + """ + + def send_to(self, data: bytes, address: Tuple[str, int]): + """ + Send a message on an open socket. + + :param bytes message: The message to send. + :param tuple(str,int) address: + Where to send it to. Must be the address of an Ethernet-enabled + chip on a board allocated to the job. Does not mean that SpiNNaker + is listening on that port (but the SCP port is being listened to if + the board is booted). + """ + ip, port = address + x, y = self._get_chip_coords(ip) + self.send_to_chip(data, x, y, port) + + @abstractproperty + def local_ip_address(self) -> str: + """ + The IP address on the server to which the connection is bound. + + :return: The IP address as a dotted string, e.g., 0.0.0.0 + :rtype: str + """ + + @abstractproperty + def local_port(self) -> int: + """ + The port on the server to which the connection is bound. + + :return: The local port number + :rtype: int + """ + + def send_eieio_message_to_core( + self, eieio_message: AbstractEIEIOMessage, x: int, y: int, p: int, + ip_address: str): + """ + Send an EIEIO message (one way) to a given core. + + :param AbstractEIEIOMessage eieio_message: + The message to send. + :param int x: + The X coordinate of the core to send to. + :param int y: + The Y coordinate of the core to send to. + :param int p: + The ID of the core to send to. + :param str ip_address: + The IP address of the Ethernet-enabled chip to route the message + via. + """ + sdp_message = SDPMessage( + SDPHeader( + flags=SDPFlag.REPLY_NOT_EXPECTED, tag=0, + destination_port=1, destination_cpu=p, + destination_chip_x=x, destination_chip_y=y, + source_port=0, source_cpu=0, + source_chip_x=0, source_chip_y=0), + data=eieio_message.bytestring) + self.send_to( + _TWO_SKIP + sdp_message.bytestring, (ip_address, SCP_SCAMP_PORT)) + + def update_tag(self, x: int, y: int, tag: int, do_receive: bool = True): + """ + Update the given tag on the given Ethernet-enabled chip to send + messages to this connection. + + :param int x: The Ethernet-enabled chip's X coordinate + :param int y: The Ethernet-enabled chip's Y coordinate + :param int tag: The tag ID to update + :param bool do_receive: Whether to receive the response or not + :raises SpinnmanTimeoutException: + If the message isn't handled within a reasonable timeout. + :raises SpinnmanUnexpectedResponseCodeException: + If the message is rejected by SpiNNaker/SCAMP. + """ + request = IPTagSet( + x, y, [0, 0, 0, 0], 0, tag, strip=True, use_sender=True) + request.sdp_header.flags = SDPFlag.REPLY_EXPECTED_NO_P2P + update_sdp_header_for_udp_send(request.sdp_header, x, y) + data = _TWO_SKIP + request.bytestring + for _try in range(_NUM_UPDATE_TAG_TRIES): + try: + self.send_to_chip(data, x, y, SCP_SCAMP_PORT) + if do_receive: + response_data = self.receive(_UPDATE_TAG_TIMEOUT) + request.get_scp_response().read_bytestring( + response_data, len(_TWO_SKIP)) + return + except SpinnmanTimeoutException as e: + if _try + 1 == _NUM_UPDATE_TAG_TRIES: + raise e + + def update_tag_by_ip(self, ip_address: str, tag: int): + """ + Update a tag on a board at a given IP address to send messages to this + connection. + + :param str ip_address: The address of the Ethernet-enabled chip + :param int tag: The ID of the tag + """ + x, y = self._get_chip_coords(ip_address) + self.update_tag(x, y, tag) diff --git a/spinnman/spalloc/spalloc_job.py b/spinnman/spalloc/spalloc_job.py new file mode 100644 index 000000000..3666a5746 --- /dev/null +++ b/spinnman/spalloc/spalloc_job.py @@ -0,0 +1,221 @@ +# Copyright (c) 2022 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Dict, Tuple +from spinn_utilities.abstract_base import AbstractBase, abstractmethod +from spinn_utilities.abstract_context_manager import AbstractContextManager +from spinnman.constants import SCP_SCAMP_PORT +from spinnman.transceiver import Transceiver +from spinnman.connections.udp_packet_connections import UDPConnection +from .spalloc_state import SpallocState +from .spalloc_boot_connection import SpallocBootConnection +from .spalloc_eieio_connection import SpallocEIEIOConnection +from .spalloc_eieio_listener import SpallocEIEIOListener +from .spalloc_scp_connection import SpallocSCPConnection + + +class SpallocJob(object, metaclass=AbstractBase): + """ + Represents a job in Spalloc. + + Don't make this yourself. Use :py:class:`SpallocClient` instead. + """ + __slots__ = () + + @abstractmethod + def get_state(self) -> SpallocState: + """ + Get the current state of the machine. + + :rtype: SpallocState + """ + + @abstractmethod + def get_root_host(self) -> str: + """ + Get the IP address for talking to the machine. + + :return: The IP address, or ``None`` if not allocated. + :rtype: str or None + """ + + @abstractmethod + def get_connections(self) -> Dict[Tuple[int, int], str]: + """ + Get the mapping from board coordinates to IP addresses. + + :return: (x,y)->IP mapping, or ``None`` if not allocated + :rtype: dict(tuple(int,int), str) or None + """ + + @abstractmethod + def connect_to_board( + self, x: int, y: int, + port: int = SCP_SCAMP_PORT) -> SpallocSCPConnection: + """ + Open a connection to a particular board in the job. + + :param int x: X coordinate of the board's Ethernet-enabled chip + :param int y: Y coordinate of the board's Ethernet-enabled chip + :param int port: UDP port to talk to; defaults to the SCP port + :return: A connection that talks to the board. + :rtype: SpallocProxiedConnection + """ + + @abstractmethod + def connect_for_booting(self) -> SpallocBootConnection: + """ + Open a connection to a job's allocation so it can be booted. + + :return: a boot connection + :rtype: SpallocBootConnection + """ + + @abstractmethod + def open_eieio_connection(self, x: int, y: int) -> SpallocEIEIOConnection: + """ + Open an EIEIO connection to a specific board in a job. + + :param int x: + The X coordinate of the Ethernet-enabled chip to connect to + :param int y: + The Y coordinate of the Ethernet-enabled chip to connect to + :return: an EIEIO connection with a board address bound + :rtype: SpallocEIEIOConnection + """ + + @abstractmethod + def open_eieio_listener_connection(self) -> SpallocEIEIOListener: + """ + Open a listening EIEIO connection to the job's boards. Messages cannot + be sent on this connection unless you say which board to send to, but + they can be received from all boards. You can also get the *server* + side connection information so you can program that into a tag. + + :return: an EIEIO connection with no board address bound + :rtype: SpallocEIEIOListener + """ + + @abstractmethod + def open_udp_listener_connection(self) -> UDPConnection: + """ + Open a listening UDP connection to the job's boards. Messages cannot + be sent on this connection unless you say which board to send to, but + they can be received from all boards. You can also get the *server* + side connection information so you can program that into a tag. + + :return: a UDP connection with no board address bound + :rtype: UDPConnection + """ + + @abstractmethod + def create_transceiver(self) -> Transceiver: + """ + Create a transceiver that will talk to this job. The transceiver will + only be configured to talk to the SCP ports of the boards of the job. + + :rtype: Transceiver + """ + + @abstractmethod + def wait_for_state_change(self, old_state: SpallocState) -> SpallocState: + """ + Wait until the allocation is not in the given old state. + + :param SpallocState old_state: + The state that we are looking to change out of. + :return: The state that the allocation is now in. + + .. note:: + If the machine gets destroyed, this will not wait for it. + :rtype: SpallocState + """ + + @abstractmethod + def wait_until_ready(self): + """ + Wait until the allocation is in the ``READY`` state. + + :raises Exception: If the allocation is destroyed + """ + + @abstractmethod + def destroy(self, reason: str = "finished"): + """ + Destroy the job. + + :param str reason: Why the job is being destroyed. + """ + + @abstractmethod + def keepalive(self): + """ + Signal the job that we want it to stay alive for a while longer. + """ + + @abstractmethod + def launch_keepalive_task( + self, period: int = 30) -> AbstractContextManager: + """ + Starts a periodic task to keep a job alive. + + :param SpallocJob job: + The job to keep alive + :param int period: + How often to send a keepalive message (in seconds) + :return: + Some kind of closable task handle; closing it terminates the task. + Destroying the job will also terminate the task. + """ + + @abstractmethod + def where_is_machine(self, x: int, y: int) -> Tuple[int, int, int]: + """ + Get the *physical* coordinates of the board hosting the given chip. + + :param int x: Chip X coordinate + :param int y: Chip Y coordinate + :return: physical board coordinates (cabinet, frame, board), or + ``None`` if there are no boards currently allocated to the job or + the chip lies outside the allocation. + :rtype: tuple(int,int,int) or None + """ + + @abstractmethod + def get_session_credentials_for_db(self): + """ + Get the session credentials for the job to be written into a database + + .. note:: + May assume that there is a ``proxy_configuration`` table with + ``kind``, ``name`` and ``value`` columns. + """ + + def __enter__(self): + """ + Return self on entering context. + """ + return self + + def __exit__(self, exc_type, exc_value, traceback): + """ + Handle exceptions by killing the job and logging the exception in the + job's destroy reason. + """ + try: + self.destroy(str(exc_value)) + except Exception: # pylint: disable=broad-except + # Ignore this exception; there's not much we can do with it + pass + return None diff --git a/spinnman/spalloc/spalloc_machine.py b/spinnman/spalloc/spalloc_machine.py new file mode 100644 index 000000000..0e9caa7a9 --- /dev/null +++ b/spinnman/spalloc/spalloc_machine.py @@ -0,0 +1,73 @@ +# Copyright (c) 2022 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Set, Tuple +from spinn_utilities.abstract_base import AbstractBase, abstractproperty + + +class SpallocMachine(object, metaclass=AbstractBase): + """ + Represents a Spalloc-controlled machine. + + Don't make this yourself. Use :py:class:`SpallocClient` instead. + """ + __slots__ = () + + @abstractproperty + def name(self) -> str: + """ + The name of the machine. + """ + + @abstractproperty + def tags(self) -> Set[str]: + """ + The tags of the machine. + """ + + @abstractproperty + def width(self) -> int: + """ + The width of the machine, in boards. + """ + + @abstractproperty + def height(self) -> int: + """ + The height of the machine, in boards. + """ + + @abstractproperty + def dead_boards(self) -> list: + """ + The dead or out-of-service boards of the machine. + """ + + @abstractproperty + def dead_links(self) -> list: + """ + The dead or out-of-service links of the machine. + """ + + @abstractproperty + def area(self) -> Tuple[int, int]: + """ + Area of machine, in boards. + + :return: width, height + :rtype: tuple(int,int) + """ + +# TODO: Add in where-is operations +# Not yet done because not needed immediately for toolchain diff --git a/spinnman/spalloc/spalloc_proxied_connection.py b/spinnman/spalloc/spalloc_proxied_connection.py new file mode 100644 index 000000000..448242a99 --- /dev/null +++ b/spinnman/spalloc/spalloc_proxied_connection.py @@ -0,0 +1,50 @@ +# Copyright (c) 2022 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +API of the client for the Spalloc web service. +""" + +from spinn_utilities.abstract_base import AbstractBase, abstractmethod +from spinnman.connections.abstract_classes import Listenable + + +class SpallocProxiedConnection(Listenable, metaclass=AbstractBase): + """ + Base class for connections proxied via Spalloc. + """ + __slots__ = () + + @abstractmethod + def send(self, data: bytes): + """ + Send a message on an open socket. + + :param data: The message to send. + """ + + @abstractmethod + def receive(self, timeout=None) -> bytes: + """ + Receive a message on an open socket. Will block until a message is + received. + + :param timeout: + How long to wait for a message to be received before timing out. + If `None`, will wait indefinitely (or until the connection is + closed). + :type timeout: int or float or None + :return: The received message. + :raises SpinnmanTimeoutException: + If a timeout happens + """ diff --git a/spinnman/spalloc/spalloc_scp_connection.py b/spinnman/spalloc/spalloc_scp_connection.py new file mode 100644 index 000000000..b85575ae2 --- /dev/null +++ b/spinnman/spalloc/spalloc_scp_connection.py @@ -0,0 +1,82 @@ +# Copyright (c) 2022 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import struct +from typing import Callable, Tuple +from spinn_utilities.abstract_base import AbstractBase +from spinn_utilities.overrides import overrides +from spinnman.connections.abstract_classes import Listenable +from spinnman.connections.udp_packet_connections import ( + update_sdp_header_for_udp_send, SCAMPConnection) +from spinnman.messages.sdp import SDPMessage, SDPFlag +from spinnman.messages.scp.abstract_messages import AbstractSCPRequest +from spinnman.messages.scp.enums import SCPResult +from .spalloc_proxied_connection import SpallocProxiedConnection + +_TWO_SHORTS = struct.Struct("<2H") +_TWO_SKIP: bytes = b'\0\0' + + +class SpallocSCPConnection( + SCAMPConnection, SpallocProxiedConnection, + metaclass=AbstractBase): + """ + The socket interface supported by proxied sockets. The socket will always + be talking to a specific board. This emulates a + :py:class:`SCAMPConnection`. + """ + __slots__ = () + + def __init__(self, x, y): + super(SpallocSCPConnection, self).__init__(x, y) + + @overrides(Listenable.get_receive_method) + def get_receive_method(self) -> Callable: + return self.receive_sdp_message + + @overrides(SCAMPConnection.receive_sdp_message) + def receive_sdp_message(self, timeout=None) -> SDPMessage: + data = self.receive(timeout) + return SDPMessage.from_bytestring(data, 2) + + @overrides(SCAMPConnection.send_sdp_message) + def send_sdp_message(self, sdp_message: SDPMessage): + # If a reply is expected, the connection should + if sdp_message.sdp_header.flags == SDPFlag.REPLY_EXPECTED: + update_sdp_header_for_udp_send( + sdp_message.sdp_header, self.chip_x, self.chip_y) + else: + update_sdp_header_for_udp_send(sdp_message.sdp_header, 0, 0) + self.send(_TWO_SKIP + sdp_message.bytestring) + + @overrides(SCAMPConnection.receive_scp_response) + def receive_scp_response( + self, timeout=1.0) -> Tuple[SCPResult, int, bytes, int]: + data = self.receive(timeout) + result, sequence = _TWO_SHORTS.unpack_from(data, 10) + return SCPResult(result), sequence, data, 2 + + @overrides(SCAMPConnection.send_scp_request) + def send_scp_request(self, scp_request: AbstractSCPRequest): + self.send(self.get_scp_data(scp_request)) + + @overrides(SCAMPConnection.get_scp_data) + def get_scp_data( + self, scp_request: AbstractSCPRequest, x=None, y=None) -> bytes: + if x is None: + x = self.chip_x + if y is None: + y = self.chip_y + update_sdp_header_for_udp_send(scp_request.sdp_header, x, y) + return _TWO_SKIP + scp_request.bytestring diff --git a/spinnman/spalloc/spalloc_state.py b/spinnman/spalloc/spalloc_state.py new file mode 100644 index 000000000..9da10a28b --- /dev/null +++ b/spinnman/spalloc/spalloc_state.py @@ -0,0 +1,39 @@ +# Copyright (c) 2021 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from enum import IntEnum + + +class SpallocState(IntEnum): + """ + The possible states of a Spalloc Job. + + Jobs start as `QUEUED`, then move to `POWER` once they've been allocated. + Once the job's boards have been switched on and the hardware stabilised, + the job moves to state `READY`. (It goes back to `POWER` if you tell it to + switch off or on; this is not recommended.) Finally, it goes to `DESTROYED` + once the job is completed in any way. + + The `UNKNOWN` state is used when something odd is going on. It should + normally be ignored. + """ + #: The job is in an unknown state. + UNKNOWN = 0 + #: The job is queued waiting for allocation. + QUEUED = 1 + #: The job is queued waiting for boards to power on or off. + POWER = 2 + #: The job is ready for user code to run on it. + READY = 3 + #: The job has been destroyed. + DESTROYED = 4 diff --git a/spinnman/spalloc/utils.py b/spinnman/spalloc/utils.py new file mode 100644 index 000000000..71d768c13 --- /dev/null +++ b/spinnman/spalloc/utils.py @@ -0,0 +1,82 @@ +# Copyright (c) 2021 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Miscellaneous utilities for working with URLs relating to the Spalloc Client. +""" + +from typing import Iterable, Tuple +from urllib.parse import urlparse, urlsplit, urlunparse + + +def clean_url(url: str) -> str: + """ + Add a ``/`` to the end of the path part of a URL if there isn't one. + + :param str url: + :rtype: str + """ + r = urlparse(url) + parts = list(r) + # Add a / to the end of the path if it isn't there + if not parts[2].endswith("/"): + parts[2] += "/" + return urlunparse(parts) + + +def parse_service_url(url: str) -> Tuple[str, str, str]: + """ + Parses a combined service reference. + + :param str url: + :return: URL, username (may be `None`), password (may be `None`) + :rtype: tuple(str,str,str) + """ + pieces = urlparse(url) + user = pieces.username + password = pieces.password + netloc = pieces.hostname + if pieces.port is not None: + netloc += f":{pieces.port}" + url = urlunparse(( + pieces.scheme, netloc, pieces.path, None, None, None)) + return url, user, password + + +def get_hostname(url: str) -> str: + """ + Parses a URL and extracts the hostname part. + """ + return urlsplit(url).hostname + + +def is_server_address( + address: str, additional_schemes: Iterable[str] = ()) -> bool: + """ + Test if the given address is a likely Spalloc server URL. + + :param str address: The address to check + :param ~collections.abc.Iterable(str) additional_schemes: + Any additional URL schemes that should be considered to be successes; + typically ``{"spalloc"}`` when looser matching is required. + :rtype: bool + """ + schemes = {"http", "https"} + if additional_schemes: + schemes.update(additional_schemes) + try: + pieces = urlparse(address) + scheme = pieces.scheme.lower() + return scheme in schemes and pieces.netloc is not None + except Exception: # pylint: disable=broad-except + return False diff --git a/spinnman/spinnman.cfg b/spinnman/spinnman.cfg new file mode 100644 index 000000000..e530f855f --- /dev/null +++ b/spinnman/spinnman.cfg @@ -0,0 +1,25 @@ +# Adds or overwrites values in SpiNNMachine/spinn_machine/spinn_machine.cfg +# Which in turn adds or overwrites values in SpiNNUtils/spinn_utilities/spinn_utilities.cfg + +# This is a place holder for any future SpiNNMan level cfg settings +[Machine] +# machine name is typically a URL and then version is required +machine_name = None + +# When True if any non ethernet chip claims it has a IP address this is logged and ignored +# When False the whole chip is removed +ignore_bad_ethernets = True +report_waiting_logs = False +turn_off_machine = False + +# format is: +# bmp_names = [/(|board_id[,board_id]*) +# = - +# where: +# is the hostname or IP address of the BMP +# is a range of boards that the BMP can speak to +# is the ID of a single board in a frame +# Note this no longer supports multiple host nor cabinet or frame +bmp_names = None + +auto_detect_bmp = False diff --git a/spinnman/transceiver.py b/spinnman/transceiver.py index 61ec089a8..acc9fadd6 100644 --- a/spinnman/transceiver.py +++ b/spinnman/transceiver.py @@ -1,82 +1,74 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # pylint: disable=too-many-arguments +import io +import os import random import struct -from threading import Condition, RLock -try: - from collections.abc import defaultdict -except ImportError: - from collections import defaultdict +from threading import Condition +from collections import defaultdict +from contextlib import contextmanager, suppress import logging import socket import time -import os -from past.builtins import xrange -from six import raise_from +from spinn_utilities.config_holder import get_config_bool +from spinn_utilities.abstract_context_manager import AbstractContextManager from spinn_utilities.log import FormatAdapter from spinn_machine import CoreSubsets -from spinn_storage_handlers.abstract_classes import AbstractDataReader -from spinn_storage_handlers import FileDataReader from spinnman.constants import ( BMP_POST_POWER_ON_SLEEP_TIME, BMP_POWER_ON_TIMEOUT, BMP_TIMEOUT, - CPU_USER_0_START_ADDRESS, CPU_USER_1_START_ADDRESS, - CPU_USER_2_START_ADDRESS, CPU_USER_3_START_ADDRESS, + CPU_MAX_USER, CPU_USER_OFFSET, CPU_USER_START_ADDRESS, IPTAG_TIME_OUT_WAIT_TIMES, SCP_SCAMP_PORT, SYSTEM_VARIABLE_BASE_ADDRESS, UDP_BOOT_CONNECTION_DEFAULT_PORT, NO_ROUTER_DIAGNOSTIC_FILTERS, ROUTER_REGISTER_BASE_ADDRESS, ROUTER_DEFAULT_FILTERS_MAX_POSITION, ROUTER_FILTER_CONTROLS_OFFSET, ROUTER_DIAGNOSTIC_FILTER_SIZE, N_RETRIES, BOOT_RETRIES) +from spinnman.data import SpiNNManDataView from spinnman.exceptions import ( SpinnmanInvalidParameterException, SpinnmanException, SpinnmanIOException, SpinnmanTimeoutException, SpinnmanGenericProcessException, SpinnmanUnexpectedResponseCodeException, - SpinnmanUnsupportedOperationException, SpinnmanInvalidPacketException, SpiNNManCoresNotInStateException) -from spinnman.model import CPUInfos, DiagnosticFilter, MachineDimensions -from spinnman.model.enums import CPUState +from spinnman.model import MachineDimensions +from spinnman.model.enums import ( + CPUState, SDP_PORTS, SDP_RUNNING_MESSAGE_CODES) from spinnman.messages.scp.impl.get_chip_info import GetChipInfo -from spinn_machine.spinnaker_triad_geometry import SpiNNakerTriadGeometry +from spinnman.messages.sdp import SDPFlag, SDPHeader, SDPMessage from spinnman.messages.spinnaker_boot import ( SystemVariableDefinition, SpinnakerBootMessages) -from spinnman.messages.scp.enums import Signal, PowerCommand +from spinnman.messages.scp.enums import PowerCommand from spinnman.messages.scp.abstract_messages import AbstractSCPRequest from spinnman.messages.scp.impl import ( - BMPSetLed, BMPGetVersion, SetPower, ReadADC, ReadFPGARegister, - WriteFPGARegister, IPTagSetTTO, ReverseIPTagSet, ReadMemory, - CountState, WriteMemory, SetLED, ApplicationRun, SendSignal, AppStop, - IPTagSet, IPTagClear, RouterClear) -from spinnman.connections import ConnectionListener -from spinnman.connections.abstract_classes import ( - SpinnakerBootSender, SCPSender, SDPSender, - MulticastSender, SCPReceiver, Listenable) + BMPGetVersion, SetPower, ReadFPGARegister, + WriteFPGARegister, IPTagSetTTO, ReverseIPTagSet, + CountState, WriteMemory, SendSignal, AppStop, + IPTagSet, IPTagClear, RouterClear, DoSync) from spinnman.connections.udp_packet_connections import ( - BMPConnection, UDPConnection, BootConnection, SCAMPConnection) + BMPConnection, BootConnection, SCAMPConnection) from spinnman.processes import ( - DeAllocSDRAMProcess, GetMachineProcess, GetVersionProcess, + GetMachineProcess, GetVersionProcess, MallocSDRAMProcess, WriteMemoryProcess, ReadMemoryProcess, - GetCPUInfoProcess, ReadIOBufProcess, ApplicationRunProcess, GetHeapProcess, - FillProcess, FillDataType, LoadFixedRouteRoutingEntryProcess, - ReadFixedRouteRoutingEntryProcess, WriteMemoryFloodProcess, + GetCPUInfoProcess, GetExcludeCPUInfoProcess, GetIncludeCPUInfoProcess, + ReadIOBufProcess, ApplicationRunProcess, + LoadFixedRouteRoutingEntryProcess, FixedConnectionSelector, + ReadFixedRouteRoutingEntryProcess, LoadMultiCastRoutesProcess, GetTagsProcess, GetMultiCastRoutesProcess, SendSingleCommandProcess, ReadRouterDiagnosticsProcess, - MostDirectConnectionSelector) + MostDirectConnectionSelector, ApplicationCopyRunProcess) from spinnman.utilities.utility_functions import ( get_vcpu_address, work_out_bmp_from_machine_details) -from spinnman.utilities.appid_tracker import AppIdTracker logger = FormatAdapter(logging.getLogger(__name__)) @@ -94,83 +86,45 @@ _FOUR_BYTES = struct.Struct(" dict of IP address -> (connection, listener) - # for UDP connections. Note listener might be None if the connection - # has not been listened to before. - # Used to keep track of what connection is listening on what port - # to ensure only one type of traffic is received on any port for any - # interface - self._udp_receive_connections_by_port = defaultdict(dict) - - # A dict of class -> list of (connection, listener) for UDP connections - # that are listenable. Note that listener might be None if the - # connection has not be listened to before. - self._udp_listenable_connections_by_class = defaultdict(list) - - # A list of all connections that can be used to send SCP messages - # Note that some of these might not be able to receive SCP; this - # could be useful if they are just using SCP to send a command that - # doesn't expect a response - self._scp_sender_connections = list() - - # A list of all connections that can be used to send SDP messages - self._sdp_sender_connections = list() - - # A list of all connections that can be used to send Multicast messages - self._multicast_sender_connections = list() - # A dict of IP address -> SCAMP connection # These are those that can be used for setting up IP Tags self._udp_scamp_connections = dict() @@ -383,124 +231,97 @@ def __init__( # messages for SCAMP interaction self._scamp_connections = list() - # if there has been SCAMP connections given, build them - if scamp_connections is not None: - for socket_address in scamp_connections: - connections.append(SCAMPConnection( - remote_host=socket_address.hostname, - remote_port=socket_address.port_num, - chip_x=socket_address.chip_x, - chip_y=socket_address.chip_y)) - # The BMP connections - self._bmp_connections = list() + self._bmp_connection = None # build connection selectors for the processes. - self._bmp_connection_selectors = dict() + self._bmp_selector = None self._scamp_connection_selector = \ - self._identify_connections(connections) - - # The nearest neighbour start ID and lock - self._nearest_neighbour_id = 1 - self._nearest_neighbour_lock = RLock() - - # A lock against multiple flood fill writes - needed as SCAMP cannot - # cope with this - self._flood_write_lock = Condition() + self.__identify_connections(connections) # A lock against single chip executions (entry is (x, y)) # The condition should be acquired before the locks are # checked or updated # The write lock condition should also be acquired to avoid a flood # fill during an individual chip execute - self._chip_execute_locks = dict() + self._chip_execute_locks = defaultdict(Condition) self._chip_execute_lock_condition = Condition() self._n_chip_execute_locks = 0 # Check that the BMP connections are valid - self._check_bmp_connections() + self.__check_bmp_connection() self._machine_off = False - def _identify_connections(self, connections): + def _where_is_xy(self, x, y): + """ + Attempts to get where_is_x_y info from the machine + + If no machine will do its best. + + :param int x: + :param int y: + :rtype: str + """ + try: + if SpiNNManDataView.has_machine(): + return SpiNNManDataView.get_machine().where_is_xy(x, y) + return (f"No Machine. " + f"Root IP:{self._scamp_connections[0].remote_ip_address}" + f"x:{x} y:{y}") + except Exception as ex: # pylint: disable=broad-except + return str(ex) + + def __identify_connections(self, connections): for conn in connections: # locate the only boot send conn - if isinstance(conn, SpinnakerBootSender): + if isinstance(conn, BootConnection): if self._boot_send_connection is not None: raise SpinnmanInvalidParameterException( - "connections", "[... {} ...]".format(conn), - "Only a single SpinnakerBootSender can be" - " specified") + "connections", f"[... {conn} ...]", + "Only a single SpinnakerBootSender can be specified") self._boot_send_connection = conn - # Locate any connections listening on a UDP port - if isinstance(conn, UDPConnection): - self._udp_receive_connections_by_port[conn.local_port][ - conn.local_ip_address] = (conn, None) - if isinstance(conn, Listenable): - self._udp_listenable_connections_by_class[ - conn.__class__].append((conn, None)) - - # Locate any connections that can send SCP - # (that are not BMP connections) - if (isinstance(conn, SCPSender) and - not isinstance(conn, BMPConnection)): - self._scp_sender_connections.append(conn) - - # Locate any connections that can send SDP - if isinstance(conn, SDPSender): - self._sdp_sender_connections.append(conn) - - # Locate any connections that can send Multicast - if isinstance(conn, MulticastSender): - self._multicast_sender_connections.append(conn) - - # Locate any connections that can send and receive SCP - if isinstance(conn, SCPSender) and isinstance(conn, SCPReceiver): + # Locate any connections that talk to a BMP + if isinstance(conn, BMPConnection): # If it is a BMP conn, add it here - if isinstance(conn, BMPConnection): - self._bmp_connections.append(conn) - self._bmp_connection_selectors[conn.cabinet, conn.frame] =\ - MostDirectConnectionSelector(None, [conn]) - else: - self._scamp_connections.append(conn) - - # If also a UDP conn, add it here (for IP tags) - if isinstance(conn, UDPConnection): - board_address = conn.remote_ip_address - self._udp_scamp_connections[board_address] = conn + if self._bmp_connection is not None: + raise NotImplementedError( + "Only one BMP connection supported") + self._bmp_connection = conn + self._bmp_selector = FixedConnectionSelector(conn) + # Otherwise, check if it can send and receive SCP (talk to SCAMP) + elif isinstance(conn, SCAMPConnection): + self._scamp_connections.append(conn) # update the transceiver with the conn selectors. - return MostDirectConnectionSelector( - self._machine, self._scamp_connections) + return MostDirectConnectionSelector(self._scamp_connections) - def _check_bmp_connections(self): - """ Check that the BMP connections are actually connected to valid BMPs + def __check_bmp_connection(self): + """ + Check that the BMP connections are actually connected to valid BMPs. - :rtype: None - :raise SpinnmanIOException: when the conn is not linked to a BMP s + :raise SpinnmanIOException: when a connection is not linked to a BMP """ # check that the UDP BMP conn is actually connected to a BMP # via the sver command - for conn in self._bmp_connections: + if self._bmp_connection is not None: + conn = self._bmp_connection # try to send a BMP sver to check if it responds as expected try: - version_info = self.get_scamp_version( - conn.chip_x, conn.chip_y, - self._bmp_connection_selectors[conn.cabinet, conn.frame]) + version_info = self._get_scamp_version( + conn.chip_x, conn.chip_y, self._bmp_selector) fail_version_name = version_info.name != _BMP_NAME fail_version_num = \ version_info.version_number[0] not in _BMP_MAJOR_VERSIONS if fail_version_name or fail_version_num: raise SpinnmanIOException( - "The BMP at {} is running {} {} which is incompatible " - "with this transceiver, required version is {} {}" - .format( - conn.remote_ip_address, - version_info.name, version_info.version_string, - _BMP_NAME, _BMP_MAJOR_VERSIONS)) + f"The BMP at {conn.remote_ip_address} is running " + f"{version_info.name} {version_info.version_string} " + "which is incompatible with this transceiver, required" + f" version is {_BMP_NAME} {_BMP_MAJOR_VERSIONS}") logger.info("Using BMP at {} with version {} {}", conn.remote_ip_address, version_info.name, @@ -509,9 +330,9 @@ def _check_bmp_connections(self): # If it fails to respond due to timeout, maybe that the connection # isn't valid except SpinnmanTimeoutException as e: - raise_from(SpinnmanException( - "BMP connection to {} is not responding".format( - conn.remote_ip_address)), e) + raise SpinnmanException( + f"BMP connection to {conn.remote_ip_address} is " + "not responding") from e except Exception: logger.exception("Failed to speak to BMP at {}", conn.remote_ip_address) @@ -519,321 +340,183 @@ def _check_bmp_connections(self): def _check_connection( self, connection_selector, chip_x, chip_y): - """ Check that the given connection to the given chip works + """ + Check that the given connection to the given chip works. :param connection_selector: the connection selector to use - :param chip_x: the chip x coordinate to try to talk to - :type chip_x: int - :param chip_y: the chip y coordinate to try to talk to - :type chip_y: int + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + :param int chip_x: the chip x coordinate to try to talk to + :param int chip_y: the chip y coordinate to try to talk to :return: True if a valid response is received, False otherwise + :rtype: ChipInfo or None """ - for _ in xrange(_CONNECTION_CHECK_RETRIES): + for _ in range(_CONNECTION_CHECK_RETRIES): try: sender = SendSingleCommandProcess(connection_selector) - chip_info = sender.execute( + chip_info = sender.execute( # pylint: disable=no-member GetChipInfo(chip_x, chip_y)).chip_info if not chip_info.is_ethernet_available: time.sleep(0.1) else: - return True + return chip_info except (SpinnmanGenericProcessException, SpinnmanTimeoutException, SpinnmanUnexpectedResponseCodeException): pass except SpinnmanIOException: break - return False + return None - def _get_chip_execute_lock(self, x, y): - """ Get a lock for executing an executable on a chip + @contextmanager + def __flood_execute_lock(self): """ - - key = (x, y) - # Check if there is a lock for the given chip - with self._chip_execute_lock_condition: - if key not in self._chip_execute_locks: - chip_lock = Condition() - self._chip_execute_locks[key] = chip_lock - else: - chip_lock = self._chip_execute_locks[key] - - # Get the lock for the chip - chip_lock.acquire() - - # Increment the lock counter (used for the flood lock) - with self._chip_execute_lock_condition: - self._n_chip_execute_locks += 1 - - def _release_chip_execute_lock(self, x, y): - """ Release the lock for executing on a chip + Get a lock for executing a flood fill of an executable. """ - - # Get the chip lock - with self._chip_execute_lock_condition: - chip_lock = self._chip_execute_locks[x, y] - - # Release the chip lock - chip_lock.release() - - # Decrement the lock and notify - self._n_chip_execute_locks -= 1 - self._chip_execute_lock_condition.notify_all() - - def _get_flood_execute_lock(self): - """ Get a lock for executing a flood fill of an executable - """ - # Get the execute lock all together, so nothing can access it - self._chip_execute_lock_condition.acquire() - - # Wait until nothing is executing - while self._n_chip_execute_locks > 0: - self._chip_execute_lock_condition.wait() - - # When nothing is executing, we can return here - - def _release_flood_execute_lock(self): - """ Release the lock for executing a flood fill - """ - - # Release the execute lock - self._chip_execute_lock_condition.release() + with self._chip_execute_lock_condition: + # Wait until nothing is executing + self._chip_execute_lock_condition.wait_for( + lambda: self._n_chip_execute_locks < 1) + yield self._chip_execute_lock_condition @staticmethod def _get_random_connection(connections): - """ Returns the given connection, or else picks one at random + """ + Returns the given connection, or else picks one at random. - :param connections: the list of connections to locate a random one from - :type connections: list of \ - :py:class:`spinnman.connections.abstract_classes.Connection` + :param list(Connection) connections: + the list of connections to locate a random one from :return: a connection object - :rtype: \ - :py:class:`spinnman.connections.abstract_classes.Connection` + :rtype: Connection or None """ if not connections: return None return connections[random.randint(0, len(connections) - 1)] - def send_scp_message(self, message, connection=None): - """ Sends an SCP message, without expecting a response - - :param message: The message to send - :type message:\ - :py:class:`spinnman.messages.scp.abstract_messages.AbstractSCPRequest` - :param connection: The connection to use (omit to pick a random one) - :type connection:\ - :py:class:`spinnman.connections.abstract_classes.Connection` - :return: The received response, or the callback if get_callback is True - :rtype:\ - :py:class:`spinnman.messages.scp.abstract_messages.AbstractSCPResponse` - :raise spinnman.exceptions.SpinnmanTimeoutException: \ - If there is a timeout before a message is received - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ - If one of the fields of the received message is invalid - :raise spinnman.exceptions.SpinnmanInvalidPacketException: - * If the message is not a recognised packet type - * If a packet is received that is not a valid response - :raise spinnman.exceptions.SpinnmanUnsupportedOperationException: \ - If no connection can send the type of message given - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error sending the message or receiving the response - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ - If the response is not one of the expected codes - """ - if connection is None: - connection = self._get_random_connection( - self._scp_sender_connections) - connection.send_scp_request(message) - def send_sdp_message(self, message, connection=None): - """ Sends an SDP message using one of the connections. + """ + Sends an SDP message using one of the connections. - :param message: The message to send - :type message: \ - :py:class:`spinnman.messages.sdp.SDPMessage` - :param connection: An optional connection to use - :type connection:\ - :py:class:`spinnman.connections.abstract_classes.Connection` - :rtype: None + :param SDPMessage message: The message to send + :param SDPConnection connection: An optional connection to use """ if connection is None: connection_to_use = self._get_random_connection( - self._sdp_sender_connections) + self._scamp_connections) else: connection_to_use = connection connection_to_use.send_sdp_message(message) - def send_multicast_message(self, x, y, multicast_message, connection=None): - """ Sends a multicast message to the board (currently unsupported) - - :param x: The x-coordinate of the chip where the message should first\ - arrive on the board - :type x: int - :param y: The y-coordinate of the chip where the message should first\ - arrive on the board - :type y: int - :param multicast_message: A multicast message to send - :type multicast_message:\ - :py:class:`spinnman.messages.multicast_message.MulticastMessage` - :param connection: A specific connection over which to send the\ - message. If not specified, an appropriate connection is chosen\ - automatically. - :type connection:\ - :py:class:`spinnman.connections.abstract_classes.MulticastSender` - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanUnsupportedOperationException: - * If there is no connection that supports sending over multicast\ - (or the given connection does not) - * If there is no connection that can make the packet arrive at\ - the selected chip (ignoring routing tables) + def _check_and_add_scamp_connections(self, x, y, ip_address): """ - raise SpinnmanUnsupportedOperationException( - "This operation is currently not supported in spinnman.") + :param int x: X coordinate of target chip + :param int y: Y coordinate of target chip + :param str ip_address: IP address of target chip - def _update_machine(self): - """ Get the current machine status and store it + :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 a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange """ + conn = SCAMPConnection(remote_host=ip_address, chip_x=x, chip_y=y) - # Get the width and height of the machine - self.get_machine_dimensions() - - # Get the coordinates of the boot chip - version_info = self.get_scamp_version() - - # Get the details of all the chips - get_machine_process = GetMachineProcess( - self._scamp_connection_selector, self._ignore_chips, - self._ignore_cores, self._ignore_links, self._max_core_id, - self._max_sdram_size) - self._machine = get_machine_process.get_machine_details( - version_info.x, version_info.y, self._width, self._height, - self._repair_machine, self._ignore_bad_ethernets) - - # update the SCAMP selector with the machine - self._scamp_connection_selector.set_machine(self._machine) - - # Work out and add the SpiNNaker links and FPGA links - self._machine.add_spinnaker_links() - self._machine.add_fpga_links() + # check if it works + chip_info = self._check_connection(FixedConnectionSelector(conn), x, y) + if chip_info is not None: + self._all_connections.add(conn) + self._udp_scamp_connections[chip_info.ethernet_ip_address] = conn + self._scamp_connections.append(conn) + else: + logger.warning( + "Additional Ethernet connection on {} at chip {}, {} " + "cannot be contacted", ip_address, x, y) - # TODO: Actually get the existing APP_IDs in use - self._app_id_tracker = AppIdTracker() + def discover_scamp_connections(self): + """ + Find connections to the board and store these for future use. - logger.info("Detected a machine on IP address {} which has {}", - self._boot_send_connection.remote_ip_address, - self._machine.cores_and_link_output_string()) + .. note:: + An exception will be thrown if no initial connections can be + found to the board. - def discover_scamp_connections(self): - """ Find connections to the board and store these for future use.\ - Note that connections can be empty, in which case another local\ - discovery mechanism will be used. Note that an exception will be\ - thrown if no initial connections can be found to the board. - - :return: An iterable of discovered connections, not including the\ - initially given connections in the constructor - :rtype: iterable of\ - :py:class:`spinnman.connections.abstract_classes.Connection` - :raise spinnman.exceptions.SpinnmanIOException: \ + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + :raise SpinnmanInvalidParameterException: If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - # Currently, this only finds other UDP connections given a connection # that supports SCP - this is done via the machine if not self._scamp_connections: return list() # Get the machine dimensions - dims = self.get_machine_dimensions() + dims = self._get_machine_dimensions() # Find all the new connections via the machine Ethernet-connected chips - new_connections = list() - geometry = SpiNNakerTriadGeometry.get_spinn5_geometry() - for x, y in geometry.get_potential_ethernet_chips( + version = SpiNNManDataView.get_machine_version() + for x, y in version.get_potential_ethernet_chips( dims.width, dims.height): ip_addr_item = SystemVariableDefinition.ethernet_ip_address - ip_address_data = _FOUR_BYTES.unpack_from( - self.read_memory( + try: + # TODO avoid here_is_x,y if read_memory fails + data = self.read_memory( x, y, - SYSTEM_VARIABLE_BASE_ADDRESS + ip_addr_item.offset, 4)) - ip_address = "{}.{}.{}.{}".format(*ip_address_data) - - if ip_address in self._udp_scamp_connections: + SYSTEM_VARIABLE_BASE_ADDRESS + ip_addr_item.offset, 4) + except SpinnmanGenericProcessException: continue - conn = self._search_for_proxies(x, y) - - # if no data, no proxy - if conn is None: - conn = SCAMPConnection( - remote_host=ip_address, chip_x=x, chip_y=y) - else: - # proxy, needs an adjustment - if conn.remote_ip_address in self._udp_scamp_connections: - del self._udp_scamp_connections[conn.remote_ip_address] - - # check if it works - if self._check_connection( - MostDirectConnectionSelector(None, [conn]), x, y): - self._scp_sender_connections.append(conn) - self._all_connections.add(conn) - self._udp_scamp_connections[ip_address] = conn - self._scamp_connections.append(conn) - new_connections.append(conn) - else: - logger.warning( - "Additional Ethernet connection on {} at chip {}, {} " - "cannot be contacted", ip_address, x, y) - - # Update the connection queues after finding new connections - return new_connections - - def _search_for_proxies(self, x, y): - """ Looks for an entry within the UDP SCAMP connections which is\ - linked to a given chip + ip = _FOUR_BYTES.unpack_from(data) + ip_address = f"{ip[0]}.{ip[1]}.{ip[2]}.{ip[3]}" + logger.info(ip_address) + self._check_and_add_scamp_connections(x, y, ip_address) + self._scamp_connection_selector = MostDirectConnectionSelector( + self._scamp_connections) - :param x: The x-coordinate of the chip - :param y: The y-coordinate of the chip - :return: connection or None - :rtype: None or SCAMPConnection + def add_scamp_connections(self, connections): """ - for connection in self._scamp_connections: - if connection.chip_x == x and connection.chip_y == y: - return connection - return None + Check connections to the board and store these for future use. - def get_connections(self): - """ Get the currently known connections to the board, made up of those\ - passed in to the transceiver and those that are discovered during\ - calls to discover_connections. No further discovery is done here. + .. note:: + An exception will be thrown if no initial connections can be + found to the board. - :return: An iterable of connections known to the transceiver - :rtype: iterable of\ - :py:class:`spinnman.connections.abstract_classes.Connection` - :raise None: No known exceptions are raised + :param dict((int,int),str) connections: + Dict of (`x`,`y`) to IP address + :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 a packet is received that has invalid parameters + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange """ - return self._all_connections + for ((x, y), ip_address) in connections.items(): + self._check_and_add_scamp_connections(x, y, ip_address) + self._scamp_connection_selector = MostDirectConnectionSelector( + self._scamp_connections) - def get_machine_dimensions(self): - """ Get the maximum chip x-coordinate and maximum chip y-coordinate of\ - the chips in the machine + def _get_machine_dimensions(self): + """ + Get the maximum chip X-coordinate and maximum chip Y-coordinate of + the chips in the machine. :return: The dimensions of the machine - :rtype: :py:class:`spinnman.model.machine_dimensions.MachineDimensions` - :raise spinnman.exceptions.SpinnmanIOException: \ + :rtype: MachineDimensions + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + :raise SpinnmanInvalidParameterException: If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ if self._width is None or self._height is None: @@ -847,72 +530,64 @@ def get_machine_dimensions(self): return MachineDimensions(self._width, self._height) def get_machine_details(self): - """ Get the details of the machine made up of chips on a board and how\ - they are connected to each other. + """ + Get the details of the machine made up of chips on a board and how + they are connected to each other. :return: A machine description - :rtype: :py:class:`spinn_machine.Machine` - :raise spinnman.exceptions.SpinnmanIOException: \ + :rtype: ~spinn_machine.Machine + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + :raise SpinnmanInvalidParameterException: If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - if self._machine is None: - self._update_machine() - return self._machine + # Get the width and height of the machine + self._get_machine_dimensions() - @property - def app_id_tracker(self): - """ Get the app ID tracker for this transceiver + # Get the coordinates of the boot chip + version_info = self._get_scamp_version() - :rtype: :py:class:`spinnman.utilities.appid_tracker.AppIdTracker` - """ - if self._app_id_tracker is None: - self._update_machine() - return self._app_id_tracker + # Get the details of all the chips + get_machine_process = GetMachineProcess( + self._scamp_connection_selector) + machine = get_machine_process.get_machine_details( + version_info.x, version_info.y, self._width, self._height) - def is_connected(self, connection=None): - """ Determines if the board can be contacted + # Work out and add the SpiNNaker links and FPGA links + machine.add_spinnaker_links() + machine.add_fpga_links() - :param connection: The connection which is to be tested. If None,\ - all connections will be tested, and the board will be considered\ - to be connected if any one connection works. - :type connection:\ - :py:class:`spinnman.connections.abstract_classes.Connection` - :return: True if the board can be contacted, False otherwise - :rtype: bool - :raise None: No known exceptions are raised - """ - if connection is not None: - return connection.is_connected() - return any(c.is_connected() for c in self._scamp_connections) + if self._boot_send_connection: + logger.info(f"Detected {machine.summary_string()}") + return machine - def get_scamp_version( + def _get_scamp_version( self, chip_x=AbstractSCPRequest.DEFAULT_DEST_X_COORD, chip_y=AbstractSCPRequest.DEFAULT_DEST_Y_COORD, connection_selector=None, n_retries=N_RETRIES): - """ Get the version of SCAMP which is running on the board. - - :param connection_selector: the connection to send the SCAMP\ - version or none (if none then a random SCAMP connection is used). - :type connection_selector: \ - :py:class:`AbstractMultiConnectionProcessConnectionSelector` - :param chip_x: the chip's x coordinate to query for SCAMP version - :type chip_x: int - :param chip_y: the chip's y coordinate to query for SCAMP version - :type chip_y: int + """ + Get the version of SCAMP which is running on the board. + + :param int chip_x: the chip's x coordinate to query for SCAMP version + :param int chip_y: the chip's y coordinate to query for SCAMP version + :param connection_selector: the connection to send the SCAMP + version or `None` (if `None` then a random SCAMP connection is + used). + :type connection_selector: + AbstractMultiConnectionProcessConnectionSelector + :param int n_retries: :return: The version identifier - :rtype: :py:class:`VersionInfo` - :raise spinnman.exceptions.SpinnmanIOException: \ + :rtype: VersionInfo + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + :raise SpinnmanInvalidParameterException: If the timeout is less than 1 - :raise spinnman.exceptions.SpinnmanTimeoutException: \ - If none of the retries resulted in a response before the timeout\ + :raise SpinnmanTimeoutException: + If none of the retries resulted in a response before the timeout (suggesting that the board is not booted). """ if connection_selector is None: @@ -920,30 +595,24 @@ def get_scamp_version( process = GetVersionProcess(connection_selector, n_retries) return process.get_version(x=chip_x, y=chip_y, p=0) - def boot_board( - self, number_of_boards=None, width=None, height=None, - extra_boot_values=None): - """ Attempt to boot the board. No check is performed to see if the\ - board is already booted. - - :param number_of_boards: this parameter is deprecated - :type number_of_boards: int - :param width: this parameter is deprecated - :type width: int or None - :param height: this parameter is deprecated - :type height: int or None - :param extra_boot_values: extra values to set during boot - :type extra_boot_values: dict of SystemVariableDefinition to value - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + def _boot_board(self, extra_boot_values=None): + """ + Attempt to boot the board. No check is performed to see if the + board is already booted. + + :param dict(SystemVariableDefinition,object) extra_boot_values: + extra values to set during boot + Any additional or overwrite values to set during boot. + This should only be used for values which are not standard + based on the board version. + :raise SpinnmanInvalidParameterException: If the board version is not known - :raise spinnman.exceptions.SpinnmanIOException: \ + :raise SpinnmanIOException: If there is an error communicating with the board """ - if (width is not None or height is not None or - number_of_boards is not None): - logger.warning( - "The width, height and number_of_boards are no longer" - " supported, and might be removed in a future version") + if not self._boot_send_connection: + # No can do. Can't boot without a boot connection. + raise SpinnmanIOException("no boot connection available") boot_messages = SpinnakerBootMessages( board_version=self._version, extra_boot_values=extra_boot_values) for boot_message in boot_messages.messages: @@ -951,12 +620,12 @@ def boot_board( time.sleep(2.0) @staticmethod - def is_scamp_version_compabible(version): - """ Determine if the version of SCAMP is compatible with this\ - transceiver + def _is_scamp_version_compabible(version): + """ + Determine if the version of SCAMP is compatible with this transceiver. - :param version: The version to test - :type version: (int, int, int) + :param tuple(int,int,int) version: The version to test + :rtype: bool """ # The major version must match exactly @@ -972,55 +641,38 @@ def is_scamp_version_compabible(version): # version is irrelevant return version[1] > _SCAMP_VERSION[1] - def ensure_board_is_ready( - self, number_of_boards=None, width=None, height=None, - n_retries=5, extra_boot_values=None): - """ Ensure that the board is ready to interact with this version\ - of the transceiver. Boots the board if not already booted and\ - verifies that the version of SCAMP running is compatible with\ - this transceiver. - - :param number_of_boards: \ - this parameter is deprecated and will be ignored - :type number_of_boards: int - :param width: this parameter is deprecated and will be ignored - :type width: int or None - :param height: this parameter is deprecated and will be ignored - :type height: int or None - :param n_retries: The number of times to retry booting - :type n_retries: int - :param extra_boot_values: Any additional values to set during boot - :type extra_boot_values: dict of SystemVariableDefinition to value + def ensure_board_is_ready(self, n_retries=5, extra_boot_values=None): + """ + Ensure that the board is ready to interact with this version of the + transceiver. Boots the board if not already booted and verifies that + the version of SCAMP running is compatible with this transceiver. + + :param int n_retries: The number of times to retry booting + :param dict(SystemVariableDefinition,object) extra_boot_values: + Any additional or overwrite values to set during boot. + This should only be used for values which are not standard + based on the board version. :return: The version identifier - :rtype: :py:class:`spinnman.model.version_info.VersionInfo` - :raise: spinnman.exceptions.SpinnmanIOException: + :rtype: VersionInfo + :raise SpinnmanIOException: * If there is a problem booting the board - * If the version of software on the board is not compatible with\ - this transceiver + * If the version of software on the board is not compatible with + this transceiver """ - - # if the machine sizes not been given, calculate from assumption - if (width is not None or height is not None or - number_of_boards is not None): - logger.warning( - "The width, height and number_of_boards are no longer" - " supported, and might be removed in a future version") - # try to get a SCAMP version once logger.info("Working out if machine is booted") if self._machine_off: version_info = None else: version_info = self._try_to_find_scamp_and_boot( - INITIAL_FIND_SCAMP_RETRIES_COUNT, number_of_boards, - width, height, extra_boot_values) + INITIAL_FIND_SCAMP_RETRIES_COUNT, extra_boot_values) # If we fail to get a SCAMP version this time, try other things - if version_info is None and self._bmp_connections: + if version_info is None and self._bmp_connection is not None: # start by powering up each BMP connection logger.info("Attempting to power on machine") - self.power_on_machine() + self._power_on_machine() # Sleep a bit to let things get going time.sleep(2.0) @@ -1028,21 +680,20 @@ def ensure_board_is_ready( # retry to get a SCAMP version, this time trying multiple times version_info = self._try_to_find_scamp_and_boot( - n_retries, number_of_boards, width, height, extra_boot_values) + n_retries, extra_boot_values) # verify that the version is the expected one for this transceiver if version_info is None: raise SpinnmanIOException( "Failed to communicate with the machine") if (version_info.name != _SCAMP_NAME or - not self.is_scamp_version_compabible( + not self._is_scamp_version_compabible( version_info.version_number)): raise SpinnmanIOException( - "The machine is currently booted with {}" - " {} which is incompatible with this transceiver, " - "required version is {} {}".format( - version_info.name, version_info.version_number, - _SCAMP_NAME, _SCAMP_VERSION)) + f"The machine is currently booted with {version_info.name}" + f" {version_info.version_number} which is incompatible with " + "this transceiver, required version is " + f"{_SCAMP_NAME} {_SCAMP_VERSION}") logger.info("Machine communication successful") @@ -1052,7 +703,18 @@ def ensure_board_is_ready( process = SendSingleCommandProcess(self._scamp_connection_selector) process.execute(IPTagSetTTO( scamp_connection.chip_x, scamp_connection.chip_y, - IPTAG_TIME_OUT_WAIT_TIMES.TIMEOUT_2560_ms.value)) + IPTAG_TIME_OUT_WAIT_TIMES.TIMEOUT_2560_ms)) + + chip_info = self._check_connection( + FixedConnectionSelector(scamp_connection), + scamp_connection.chip_x, scamp_connection.chip_y) + if chip_info is not None: + self._udp_scamp_connections[chip_info.ethernet_ip_address] = \ + scamp_connection + + # Update the connection selector so that it can ask for processor ids + self._scamp_connection_selector = MostDirectConnectionSelector( + self._scamp_connections) return version_info @@ -1060,200 +722,226 @@ def __is_default_destination(self, version_info): return (version_info.x == AbstractSCPRequest.DEFAULT_DEST_X_COORD and version_info.y == AbstractSCPRequest.DEFAULT_DEST_Y_COORD) - def _try_to_find_scamp_and_boot( - self, tries_to_go, number_of_boards, width, height, - extra_boot_values): - """ Try to detect if SCAMP is running, and if not, boot the machine - - :param tries_to_go: how many attempts should be supported - :param number_of_boards: \ - the number of boards that this machine is built out of - :param width: The width of the machine in chips - :param height: The height of the machine in chips - :param extra_boot_values: Any additional values to set during boot - :return: version_info - :raise SpinnmanIOException: \ + def _try_to_find_scamp_and_boot(self, tries_to_go, extra_boot_values): + """ + Try to detect if SCAMP is running, and if not, boot the machine. + + :param int tries_to_go: how many attempts should be supported + :param dict(SystemVariableDefinition,object) extra_boot_values: + Any additional or overwrite values to set during boot. + This should only be used for values which are not standard + based on the board version. + :return: version info + :rtype: VersionInfo + :raise SpinnmanIOException: If there is a problem communicating with the machine """ version_info = None current_tries_to_go = tries_to_go while version_info is None and current_tries_to_go > 0: try: - version_info = self.get_scamp_version(n_retries=BOOT_RETRIES) + version_info = self._get_scamp_version(n_retries=BOOT_RETRIES) if self.__is_default_destination(version_info): version_info = None time.sleep(0.1) except SpinnmanGenericProcessException as e: if isinstance(e.exception, SpinnmanTimeoutException): logger.info("Attempting to boot machine") - self.boot_board( - number_of_boards, width, height, extra_boot_values) + self._boot_board(extra_boot_values) current_tries_to_go -= 1 elif isinstance(e.exception, SpinnmanIOException): - raise_from(SpinnmanIOException( - "Failed to communicate with the machine"), e) + raise SpinnmanIOException( + "Failed to communicate with the machine") from e else: raise except SpinnmanTimeoutException: logger.info("Attempting to boot machine") - self.boot_board( - number_of_boards, width, height, extra_boot_values) + self._boot_board(extra_boot_values) current_tries_to_go -= 1 except SpinnmanIOException as e: - raise_from(SpinnmanIOException( - "Failed to communicate with the machine"), e) + raise SpinnmanIOException( + "Failed to communicate with the machine") from e # The last thing we tried was booting, so try again to get the version if version_info is None: - try: - version_info = self.get_scamp_version() + with suppress(SpinnmanException): + version_info = self._get_scamp_version() if self.__is_default_destination(version_info): version_info = None - except SpinnmanException: - pass if version_info is not None: logger.info("Found board with version {}", version_info) return version_info - def get_cpu_information(self, core_subsets=None): - """ Get information about the processors on the board + def get_cpu_infos( + self, core_subsets=None, states=None, include=True): + """ + Get information about the processors on the board. - :param core_subsets: A set of chips and cores from which to get the\ - information. If not specified, the information from all of the\ + :param ~spinn_machine.CoreSubsets core_subsets: + A set of chips and cores from which to get the + information. If not specified, the information from all of the cores on all of the chips on the board are obtained. - :type core_subsets: \ - :py:class:`spinn_machine.CoreSubsets` - :return: An iterable of the CPU information for the selected cores, or\ - all cores if core_subsets is not specified - :rtype: iterable of \ - :py:class:`spinnman.model.CPUInfo` - :raise spinnman.exceptions.SpinnmanIOException: \ + :param states: The state or states to filter on (if any) + :type states: None, CPUState or collection(CPUState) + :param bool include: + If True includes only infos in the requested state(s). + If False includes only infos NOT in the requested state(s). + Ignored if states is None. + :return: The CPU information for the selected cores and States, or + all cores/states if core_subsets/states is not specified + :rtype: ~spinnman.model.CPUInfos + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If chip_and_cores contains invalid items * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - # Get all the cores if the subsets are not given if core_subsets is None: - if self._machine is None: - self._update_machine() core_subsets = CoreSubsets() - for chip in self._machine.chips: + for chip in SpiNNManDataView.get_machine().chips: for processor in chip.processors: core_subsets.add_processor( chip.x, chip.y, processor.processor_id) - process = GetCPUInfoProcess(self._scamp_connection_selector) + if states is None: + process = GetCPUInfoProcess(self._scamp_connection_selector) + else: + if isinstance(states, CPUState): + new_states = set() + new_states.add(states) + states = new_states + if include: + process = GetIncludeCPUInfoProcess( + self._scamp_connection_selector, states) + else: + process = GetExcludeCPUInfoProcess( + self._scamp_connection_selector, states) + cpu_info = process.get_cpu_info(core_subsets) return cpu_info + def get_clock_drift(self, x, y): + """ + Get the clock drift + :param int x: The x-coordinate of the chip to get drift for + :param int y: The y-coordinate of the chip to get drift for + """ + DRIFT_FP = 1 << 17 + + drift = self._get_sv_data(x, y, SystemVariableDefinition.clock_drift) + drift = struct.unpack(" CPU_MAX_USER: + raise ValueError( + f"Incorrect user number {user}") + return (get_vcpu_address(p) + CPU_USER_START_ADDRESS + + CPU_USER_OFFSET * user) - :param p: The ID of the processor to get the user 2 address from - :type p: int - :return: The address for user 2 register for this processor - :rtype: int + def read_user(self, x, y, p, user): """ - return get_vcpu_address(p) + CPU_USER_2_START_ADDRESS + Get the contents of the this user register for the given processor. - @staticmethod - def get_user_3_register_address_from_core(p): - """ Get the address of user 3 for a given processor on the board + .. note:: + Conventionally, user_0 usually holds the address of the table of + memory regions. - :param p: The ID of the processor to get the user 3 address from - :type p: int - :return: The address for user 3 register for this processor + :param int x: X coordinate of the chip + :param int y: Y coordinate of the chip + :param int p: Virtual processor identifier on the chip + :param int user: The user number to read data for :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 does not identify a valid processor + :raise SpinnmanUnexpectedResponseCodeException: + If a response indicates an error during the exchange """ - return get_vcpu_address(p) + CPU_USER_3_START_ADDRESS + addr = self.__get_user_register_address_from_core(p, user) + return self.read_word(x, y, addr) def get_cpu_information_from_core(self, x, y, p): - """ Get information about a specific processor on the board - - :param x: The x-coordinate of the chip containing the processor - :type x: int - :param y: The y-coordinate of the chip containing the processor - :type y: int - :param p: The ID of the processor to get the information about - :type p: int + """ + Get information about a specific processor on the board. + + :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 :return: The CPU information for the selected core - :rtype: :py:class:`spinnman.model.cpu_info.CPUInfo` - :raise spinnman.exceptions.SpinnmanIOException: \ + :rtype: CPUInfo + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If x, y, p is not a valid processor * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - core_subsets = CoreSubsets() core_subsets.add_processor(x, y, p) - return next(iter(self.get_cpu_information(core_subsets))) + cpu_infos = self.get_cpu_infos(core_subsets) + return cpu_infos.get_cpu_info(x, y, p) def get_iobuf(self, core_subsets=None): - """ Get the contents of the IOBUF buffer for a number of processors + """ + Get the contents of the IOBUF buffer for a number of processors. - :param core_subsets: A set of chips and cores from which to get the\ - buffers. If not specified, the buffers from all of the cores on \ - all of the chips on the board are obtained. - :type core_subsets: :py:class:`spinn_machine.CoreSubsets` - :return: An iterable of the buffers, which may not be in the order\ + :param ~spinn_machine.CoreSubsets core_subsets: + A set of chips and cores from which to get the buffers. If not + specified, the buffers from all of the cores on all of the chips + on the board are obtained. + :return: An iterable of the buffers, which may not be in the order of core_subsets - :rtype: iterable of :py:class:`spinnman.model.IOBuffer` - :raise spinnman.exceptions.SpinnmanIOException: \ + :rtype: iterable(IOBuffer) + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If chip_and_cores contains invalid items * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - # making the assumption that all chips have the same iobuf size. if self._iobuf_size is None: self._iobuf_size = self._get_sv_data( @@ -1265,335 +953,148 @@ def get_iobuf(self, core_subsets=None): process = ReadIOBufProcess(self._scamp_connection_selector) return process.read_iobuf(self._iobuf_size, core_subsets) - def set_watch_dog_on_chip(self, x, y, watch_dog): - """ Enable, disable or set the value of the watch dog timer on a\ - specific chip - - :param x: chip x coord to write new watchdog param to - :type x: int - :param y: chip y coord to write new watchdog param to - :type y: int - :param watch_dog:\ - Either a boolean indicating whether to enable (True) or \ - disable (False) the watchdog timer, or an int value to set the \ - timer count to - :type watch_dog: bool or int - :rtype: None - """ - - # build what we expect it to be - value_to_set = watch_dog - WATCHDOG = SystemVariableDefinition.software_watchdog_count - if isinstance(watch_dog, bool): - value_to_set = WATCHDOG.default if watch_dog else 0 - - # build data holder - data = _ONE_BYTE.pack(value_to_set) - - # write data - address = SYSTEM_VARIABLE_BASE_ADDRESS + WATCHDOG.offset - self.write_memory(x=x, y=y, base_address=address, data=data) - - def set_watch_dog(self, watch_dog): - """ Enable, disable or set the value of the watch dog timer - - :param watch_dog:\ - Either a boolean indicating whether to enable (True) or \ - disable (False) the watch dog timer, or an int value to set the \ - timer count to. - :type watch_dog: bool or int - :rtype: None - """ - if self._machine is None: - self._update_machine() - for x, y in self._machine.chip_coordinates: - self.set_watch_dog_on_chip(x, y, watch_dog) - - def get_iobuf_from_core(self, x, y, p): - """ Get the contents of IOBUF for a given core - - :param x: The x-coordinate of the chip containing the processor - :type x: int - :param y: The y-coordinate of the chip containing the processor - :type y: int - :param p: The ID of the processor to get the IOBUF for - :type p: int - :return: An IOBUF buffer - :rtype: :py:class:`spinnman.model.IOBuffer` - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ - If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: - * If chip_and_cores contains invalid items - * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ - If a response indicates an error during the exchange - """ - core_subsets = CoreSubsets() - core_subsets.add_processor(x, y, p) - return next(self.get_iobuf(core_subsets)) - def get_core_state_count(self, app_id, state): - """ Get a count of the number of cores which have a given state + """ + Get a count of the number of cores which have a given state. - :param app_id: The ID of the application from which to get the count. - :type app_id: int - :param state: The state count to get - :type state: :py:class:`spinnman.model.CPUState` + :param int app_id: + The ID of the application from which to get the count. + :param CPUState state: The state count to get :return: A count of the cores with the given status :rtype: int - :raise spinnman.exceptions.SpinnmanIOException: \ + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If state is not a valid status * If app_id is not a valid application ID * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ process = SendSingleCommandProcess(self._scamp_connection_selector) response = process.execute(CountState(app_id, state)) - return response.count - - def execute( - self, x, y, processors, executable, app_id, n_bytes=None, - wait=False, is_filename=False): - """ Start an executable running on a single chip - - :param x: The x-coordinate of the chip on which to run the executable - :type x: int - :param y: The y-coordinate of the chip on which to run the executable - :type y: int - :param processors: \ - The cores on the chip on which to run the application - :type processors: iterable of int - :param executable: \ - The data that is to be executed. Should be one of the following: - * An instance of AbstractDataReader - * A bytearray - * A filename of a file containing the executable (in which case\ - is_filename must be set to True) - :type executable:\ - :py:class:`spinn_storage_handlers.abstract_classes.AbstractDataReader`\ - or bytearray or str - :param app_id: \ - The ID of the application with which to associate the executable - :type app_id: int - :param n_bytes: \ - The size of the executable data in bytes. If not specified: - * If executable is an AbstractDataReader, an error is raised - * If executable is a bytearray, the length of the bytearray will \ - be used - * If executable is an int, 4 will be used - * If executable is a str, the length of the file will be used - :type n_bytes: int - :param wait: True if the binary should enter a "wait" state on loading - :type wait: bool - :param is_filename: True if executable is a filename - :type is_filename: bool - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: - * If there is an error communicating with the board - * If there is an error reading the executable - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ - If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: - * If x, y, p does not lead to a valid core - * If app_id is an invalid application ID - * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ - If a response indicates an error during the exchange - """ - - # Lock against updates - self._get_chip_execute_lock(x, y) - - # Write the executable - EXECUTABLE_ADDRESS = 0x67800000 - self.write_memory( - x, y, EXECUTABLE_ADDRESS, executable, n_bytes, - is_filename=is_filename) - - # Request the start of the executable - process = SendSingleCommandProcess(self._scamp_connection_selector) - process.execute(ApplicationRun(app_id, x, y, processors, wait)) - - # Release the lock - self._release_chip_execute_lock(x, y) - - def _get_next_nearest_neighbour_id(self): - with self._nearest_neighbour_lock: - next_nearest_neighbour_id = (self._nearest_neighbour_id + 1) % 127 - self._nearest_neighbour_id = next_nearest_neighbour_id - return next_nearest_neighbour_id + return response.count # pylint: disable=no-member def execute_flood( self, core_subsets, executable, app_id, n_bytes=None, wait=False, is_filename=False): - """ Start an executable running on multiple places on the board. This\ - will be optimised based on the selected cores, but it may still\ - require a number of communications with the board to execute. - - :param core_subsets: Which cores on which chips to start the executable - :type core_subsets: \ - :py:class:`spinn_machine.CoreSubsets` - :param executable: \ + """ + Start an executable running on multiple places on the board. This + will be optimised based on the selected cores, but it may still + require a number of communications with the board to execute. + + :param ~spinn_machine.CoreSubsets core_subsets: + Which cores on which chips to start the executable + :param executable: The data that is to be executed. Should be one of the following: - * An instance of AbstractDataReader + + * An instance of RawIOBase * A bytearray - * A filename of an executable (in which case is_filename must be\ - set to True) - :type executable:\ - :py:class:`spinn_storage_handlers.abstract_classes.AbstractDataReader`\ - or bytearray or str - :param app_id: \ + * A filename of an executable (in which case `is_filename` must be + set to True) + :type executable: + ~io.RawIOBase or bytes or bytearray or str + :param int app_id: The ID of the application with which to associate the executable - :type app_id: int - :param n_bytes: \ + :param int n_bytes: The size of the executable data in bytes. If not specified: - * If executable is an AbstractDataReader, an error is raised - * If executable is a bytearray, the length of the bytearray will \ - be used - * If executable is an int, 4 will be used - * If executable is a str, the length of the file will be used - :type n_bytes: int - :param wait: \ + + * If `executable` is an RawIOBase, an error is raised + * If `executable` is a bytearray, the length of the bytearray will + be used + * If `executable` is an int, 4 will be used + * If `executable` is a str, the length of the file will be used + :param bool wait: True if the processors should enter a "wait" state on loading - :type wait: bool - :param is_filename: True if the data is a filename - :type is_filename: bool - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: + :param bool is_filename: True if the data is a filename + :raise SpinnmanIOException: * If there is an error communicating with the board * If there is an error reading the executable - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If one of the specified cores is not valid - * If app_id is an invalid application ID + * If `app_id` is an invalid application ID * If a packet is received that has invalid parameters - * If data is an AbstractDataReader but n_bytes is not specified - * If data is an int and n_bytes is more than 4 - * If n_bytes is less than 0 - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + * If `executable` is an RawIOBase but `n_bytes` is not specified + * If `executable` is an int and `n_bytes` is more than 4 + * If `n_bytes` is less than 0 + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ # Lock against other executable's - self._get_flood_execute_lock() - - # Flood fill the system with the binary - self.write_memory_flood( - 0x67800000, executable, n_bytes, is_filename=is_filename) - - # Execute the binary on the cores on the chips where required - process = ApplicationRunProcess(self._scamp_connection_selector) - process.run(app_id, core_subsets, wait) - - # Release the lock - self._release_flood_execute_lock() - - def execute_application(self, executable_targets, app_id): - """ Execute a set of binaries that make up a complete application\ - on specified cores, wait for them to be ready and then start\ - all of the binaries. Note this will get the binaries into c_main\ - but will not signal the barrier. - - :param executable_targets: \ - The binaries to be executed and the cores to execute them on - :type executable_targets:\ - :py:class:`spinnman.model.executable_targets.ExecutableTargets` - :param app_id: The app_id to give this application - :type app_id: int - """ - - # Execute each of the binaries and get them in to a "wait" state - for binary in executable_targets.binaries: - core_subsets = executable_targets.get_cores_for_binary(binary) - self.execute_flood( - core_subsets, binary, app_id, wait=True, is_filename=True) - - # Sleep to allow cores to get going - time.sleep(0.5) - - # Check that the binaries have reached a wait state - count = self.get_core_state_count(app_id, CPUState.READY) - if count < executable_targets.total_processors: - cores_ready = self.get_cores_not_in_state( - executable_targets.all_core_subsets, [CPUState.READY]) - if len(cores_ready) > 0: - raise SpinnmanException( - "Only {} of {} cores reached ready state: {}".format( - count, executable_targets.total_processors, - self.get_core_status_string(cores_ready))) + with self.__flood_execute_lock(): + # Flood fill the system with the binary + n_bytes, chksum = self.write_memory( + 0, 0, _EXECUTABLE_ADDRESS, executable, n_bytes, + is_filename=is_filename, get_sum=True) - # Send a signal telling the application to start - self.send_signal(app_id, Signal.START) + # Execute the binary on the cores on 0, 0 if required + if core_subsets.is_chip(0, 0): + boot_subset = CoreSubsets() + boot_subset.add_core_subset( + core_subsets.get_core_subset_for_chip(0, 0)) + process = ApplicationRunProcess( + self._scamp_connection_selector) + process.run(app_id, boot_subset, wait) - def power_on_machine(self): - """ Power on the whole machine + process = ApplicationCopyRunProcess( + self._scamp_connection_selector) + process.run(n_bytes, app_id, core_subsets, chksum, wait) + + def _power_on_machine(self): + """ + Power on the whole machine. + + :rtype bool + :return success of failure to power on machine """ - if not self._bmp_connections: + if self._bmp_connection is None: logger.warning("No BMP connections, so can't power on") - for bmp_connection in self._bmp_connections: - self.power_on(bmp_connection.boards, bmp_connection.cabinet, - bmp_connection.frame) + return False + self.power_on(self._bmp_connection) + return True - def power_on(self, boards=0, cabinet=0, frame=0): - """ Power on a set of boards in the machine + def power_on(self, boards=0): + """ + Power on a set of boards in the machine. - :param boards: The board or boards to power on - :param cabinet: the ID of the cabinet containing the frame, or 0 \ - if the frame is not in a cabinet - :param frame: the ID of the frame in the cabinet containing the\ - board(s), or 0 if the board is not in a frame + :param int boards: The board or boards to power on """ - self._power(PowerCommand.POWER_ON, boards, cabinet, frame) + self._power(PowerCommand.POWER_ON, boards) def power_off_machine(self): - """ Power off the whole machine """ - if not self._bmp_connections: - logger.warning("No BMP connections, so can't power off") - for bmp_connection in self._bmp_connections: - self.power_off(bmp_connection.boards, bmp_connection.cabinet, - bmp_connection.frame) + Power off the whole machine. - def power_off(self, boards=0, cabinet=0, frame=0): - """ Power off a set of boards in the machine + :rtype bool + :return success or failure to power off the machine + """ + if self._bmp_connection is None: + logger.warning("No BMP connections, so can't power off") + return False + logger.info("Turning off machine") + self.power_off(self._bmp_connection) + return True - :param boards: The board or boards to power off - :param cabinet: the ID of the cabinet containing the frame, or 0 \ - if the frame is not in a cabinet - :param frame: the ID of the frame in the cabinet containing the\ - board(s), or 0 if the board is not in a frame + def power_off(self, boards=0): """ - self._power(PowerCommand.POWER_OFF, boards, cabinet, frame) + Power off a set of boards in the machine. - def _bmp_connection(self, cabinet, frame): - key = (cabinet, frame) - if key not in self._bmp_connection_selectors: - raise SpinnmanInvalidParameterException( - "cabinet and frame", "{} and {}".format(cabinet, frame), - "Unknown combination") - return self._bmp_connection_selectors[key] + :param int boards: The board or boards to power off + """ + self._power(PowerCommand.POWER_OFF, boards) - def _power(self, power_command, boards=0, cabinet=0, frame=0): - """ Send a power request to the machine + def _power(self, power_command, boards=0): + """ + Send a power request to the machine. - :param power_command: The power command to send + :param PowerCommand power_command: The power command to send :param boards: The board or boards to send the command to - :param cabinet: the ID of the cabinet containing the frame, or 0 \ - if the frame is not in a cabinet - :param frame: the ID of the frame in the cabinet containing the\ - board(s), or 0 if the board is not in a frame - :rtype: None """ - connection_selector = self._bmp_connection(cabinet, frame) + connection_selector = self._bmp_selector timeout = ( BMP_POWER_ON_TIMEOUT if power_command == PowerCommand.POWER_ON @@ -1607,395 +1108,228 @@ def _power(self, power_command, boards=0, cabinet=0, frame=0): if not self._machine_off: time.sleep(BMP_POST_POWER_ON_SLEEP_TIME) - def set_led(self, led, action, board, cabinet, frame): - """ Set the LED state of a board in the machine - - :param led: \ - Number of the LED or an iterable of LEDs to set the state of (0-7) - :type led: int or iterable of int - :param action: State to set the LED to, either on, off or toggle - :type action:\ - :py:class:`spinnman.messages.scp.scp_led_action.SCPLEDAction` - :param board: Specifies the board to control the LEDs of. This may \ - also be an iterable of multiple boards (in the same frame). The\ - command will actually be sent to the first board in the iterable. - :type board: int or iterable - :param cabinet: the cabinet this is targeting - :type cabinet: int - :param frame: the frame this is targeting - :type frame: int - :rtype: None + def read_fpga_register( + self, fpga_num, register, board=0): """ - process = SendSingleCommandProcess( - self._bmp_connection(cabinet, frame)) - process.execute(BMPSetLed(led, action, board)) + Read a register on a FPGA of a board. The meaning of the + register's contents will depend on the FPGA's configuration. - def read_fpga_register(self, fpga_num, register, cabinet, frame, board): - """ Read a register on a FPGA of a board. The meaning of the\ - register's contents will depend on the FPGA's configuration. - - :param fpga_num: FPGA number (0, 1 or 2) to communicate with. - :type fpga_num: int - :param register: Register address to read to (will be rounded down to\ + :param int fpga_num: FPGA number (0, 1 or 2) to communicate with. + :param int register: + Register address to read to (will be rounded down to the nearest 32-bit word boundary). - :type register: int - :param cabinet: cabinet: the cabinet this is targeting - :type cabinet: int - :param frame: the frame this is targeting - :type frame: int - :param board: which board to request the FPGA register from + :param int board: which board to request the FPGA register from :return: the register data + :rtype: int """ - process = SendSingleCommandProcess( - self._bmp_connection(cabinet, frame), timeout=1.0) + process = SendSingleCommandProcess(self._bmp_selector, timeout=1.0) response = process.execute( ReadFPGARegister(fpga_num, register, board)) - return response.fpga_register + return response.fpga_register # pylint: disable=no-member - def write_fpga_register(self, fpga_num, register, value, cabinet, frame, - board): - """ Write a register on a FPGA of a board. The meaning of setting the\ - register's contents will depend on the FPGA's configuration. + def write_fpga_register(self, fpga_num, register, value, board=0): + """ + Write a register on a FPGA of a board. The meaning of setting the + register's contents will depend on the FPGA's configuration. - :param fpga_num: FPGA number (0, 1 or 2) to communicate with. - :type fpga_num: int - :param register: Register address to read to (will be rounded down to\ + :param int fpga_num: FPGA number (0, 1 or 2) to communicate with. + :param int register: + Register address to read to (will be rounded down to the nearest 32-bit word boundary). - :type register: int - :param value: the value to write into the FPGA register - :type value: int - :param cabinet: cabinet: the cabinet this is targeting - :type cabinet: int - :param frame: the frame this is targeting - :type frame: int - :param board: which board to write the FPGA register to - :rtype: None + :param int value: the value to write into the FPGA register + :param int board: which board to write the FPGA register to """ - process = SendSingleCommandProcess( - self._bmp_connection(cabinet, frame)) + process = SendSingleCommandProcess(self._bmp_selector) process.execute( WriteFPGARegister(fpga_num, register, value, board)) - def read_adc_data(self, board, cabinet, frame): - """ Read the BMP ADC data - - :param cabinet: cabinet: the cabinet this is targeting - :type cabinet: int - :param frame: the frame this is targeting - :type frame: int - :param board: which board to request the ADC data from - :return: the FPGA's ADC data object + def read_bmp_version(self, board): """ - process = SendSingleCommandProcess( - self._bmp_connection(cabinet, frame)) - response = process.execute(ReadADC(board)) - return response.adc_info - - def read_bmp_version(self, board, cabinet, frame): - """ Read the BMP version - - :param cabinet: cabinet: the cabinet this is targeting - :type cabinet: int - :param frame: the frame this is targeting - :type frame: int - :param board: which board to request the data from + Read the BMP version. + + :param int board: which board to request the data from :return: the sver from the BMP """ - process = SendSingleCommandProcess( - self._bmp_connection(cabinet, frame)) + process = SendSingleCommandProcess(self._bmp_selector) response = process.execute(BMPGetVersion(board)) - return response.version_info + return response.version_info # pylint: disable=no-member def write_memory(self, x, y, base_address, data, n_bytes=None, offset=0, - cpu=0, is_filename=False): - """ Write to the SDRAM on the board + cpu=0, is_filename=False, get_sum=False): + """ + Write to the SDRAM on the board. - :param x: \ + :param int x: The x-coordinate of the chip where the memory is to be written to - :type x: int - :param y: \ + :param int y: The y-coordinate of the chip where the memory is to be written to - :type y: int - :param base_address: The address in SDRAM where the region of memory\ - is to be written - :type base_address: int + :param int base_address: + The address in SDRAM where the region of memory is to be written :param data: The data to write. Should be one of the following: - * An instance of AbstractDataReader - * A bytearray + + * An instance of RawIOBase + * A bytearray/bytes * A single integer - will be written in little-endian byte order - * A filename of a data file (in which case is_filename must be\ - set to True) - :type data:\ - :py:class:`spinn_storage_handlers.abstract_classes.AbstractDataReader`\ - or bytearray or int or str - :param n_bytes: \ + * A filename of a data file (in which case `is_filename` must be + set to True) + :type data: + ~io.RawIOBase or bytes or bytearray or int or str + :param int n_bytes: The amount of data to be written in bytes. If not specified: - * If data is an AbstractDataReader, an error is raised - * If data is a bytearray, the length of the bytearray will be used - * If data is an int, 4 will be used - * If data is a str, the length of the file will be used - :type n_bytes: int - :param offset: The offset from which the valid data begins - :type offset: int - :param cpu: The optional CPU to write to - :type cpu: int - :param is_filename: True if the data is a filename - :type is_filename: bool - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: + + * If `data` is an RawIOBase, an error is raised + * If `data` is a bytearray, the length of the bytearray will be + used + * If `data` is an int, 4 will be used + * If `data` is a str, the length of the file will be used + :param int offset: The offset from which the valid data begins + :param int cpu: The optional CPU to write to + :param bool is_filename: True if `data` is a filename + :param bool get_sum: whether to return a checksum or 0 + :return: The number of bytes written, the checksum (0 if get_sum=False) + :rtype: int, int + :raise SpinnmanIOException: * If there is an error communicating with the board * If there is an error reading the data - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: - * If x, y does not lead to a valid chip + :raise SpinnmanInvalidParameterException: + * If `x, y` does not lead to a valid chip * If a packet is received that has invalid parameters - * If base_address is not a positive integer - * If data is an AbstractDataReader but n_bytes is not specified - * If data is an int and n_bytes is more than 4 - * If n_bytes is less than 0 - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + * If `base_address` is not a positive integer + * If `data` is an RawIOBase but `n_bytes` is not specified + * If `data` is an int and `n_bytes` is more than 4 + * If `n_bytes` is less than 0 + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ process = WriteMemoryProcess(self._scamp_connection_selector) - if isinstance(data, AbstractDataReader): - process.write_memory_from_reader( - x, y, cpu, base_address, data, n_bytes) + if isinstance(data, io.RawIOBase): + chksum = process.write_memory_from_reader( + x, y, cpu, base_address, data, n_bytes, get_sum) elif isinstance(data, str) and is_filename: if n_bytes is None: n_bytes = os.stat(data).st_size - with FileDataReader(data) as reader: - process.write_memory_from_reader( - x, y, cpu, base_address, reader, n_bytes) + with open(data, "rb") as reader: + chksum = process.write_memory_from_reader( + x, y, cpu, base_address, reader, n_bytes, get_sum) elif isinstance(data, int): + n_bytes = 4 data_to_write = _ONE_WORD.pack(data) - process.write_memory_from_bytearray( - x, y, cpu, base_address, data_to_write, 0, 4) + chksum = process.write_memory_from_bytearray( + x, y, cpu, base_address, data_to_write, 0, n_bytes, get_sum) else: if n_bytes is None: n_bytes = len(data) - process.write_memory_from_bytearray( - x, y, cpu, base_address, data, offset, n_bytes) - - def write_neighbour_memory(self, x, y, link, base_address, data, - n_bytes=None, offset=0, cpu=0): - """ Write to the memory of a neighbouring chip using a LINK_READ SCP\ - command. If sent to a BMP, this command can be used to communicate\ - with the FPGAs' debug registers. - - :param x: \ - The x-coordinate of the chip whose neighbour is to be written to - :type x: int - :param y: \ - The y-coordinate of the chip whose neighbour is to be written to - :type y: int - :param link: \ - The link index to send the request to (or if BMP, the FPGA number) - :type link: int - :param base_address: \ - The address in SDRAM where the region of memory is to be written - :type base_address: int - :param data: The data to write. Should be one of the following: - * An instance of AbstractDataReader - * A bytearray - * A single integer; will be written in little-endian byte order - :type data:\ - :py:class:`spinn_storage_handlers.abstract_classes.AbstractDataReader`\ - or bytearray or int - :param n_bytes: \ - The amount of data to be written in bytes. If not specified: - * If data is an AbstractDataReader, an error is raised - * If data is a bytearray, the length of the bytearray will be used - * If data is an int, 4 will be used - :type n_bytes: int - :param offset: The offset where the valid data starts (if the data is \ - an int then offset will be ignored and used 0 - :type offset: int - :param cpu: The CPU to use, typically 0 (or if a BMP, the slot number) - :type cpu: int - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: - * If there is an error communicating with the board - * If there is an error reading the data - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ - If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: - * If x, y does not lead to a valid chip - * If a packet is received that has invalid parameters - * If base_address is not a positive integer - * If data is an AbstractDataReader but n_bytes is not specified - * If data is an int and n_bytes is more than 4 - * If n_bytes is less than 0 - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ - If a response indicates an error during the exchange + chksum = process.write_memory_from_bytearray( + x, y, cpu, base_address, data, offset, n_bytes, get_sum) + return n_bytes, chksum + + def write_user(self, x, y, p, user, value): """ - process = WriteMemoryProcess(self._scamp_connection_selector) - if isinstance(data, AbstractDataReader): - process.write_link_memory_from_reader( - x, y, cpu, link, base_address, data, n_bytes) - elif isinstance(data, int): - data_to_write = _ONE_WORD.pack(data) - process.write_link_memory_from_bytearray( - x, y, cpu, link, base_address, data_to_write, 0, 4) - else: - if n_bytes is None: - n_bytes = len(data) - process.write_link_memory_from_bytearray( - x, y, cpu, link, base_address, data, offset, n_bytes) + Write to the this user register for the given processor. - def write_memory_flood( - self, base_address, data, n_bytes=None, offset=0, - is_filename=False): - """ Write to the SDRAM of all chips. + .. note:: + Conventionally, user_0 usually holds the address of the table of + memory regions. - :param base_address: \ - The address in SDRAM where the region of memory is to be written - :type base_address: int - :param data: \ - The data that is to be written. Should be one of the following: - * An instance of AbstractDataReader - * A bytearray or bytestring - * A single integer - * A file name of a file to read (in which case is_filename should\ - be set to True) - :type data:\ - :py:class:`spinn_storage_handlers.abstract_classes.AbstractDataReader`\ - or bytearray or int or str - :param n_bytes: \ - The amount of data to be written in bytes. If not specified: - * If data is an AbstractDataReader, an error is raised - * If data is a bytearray, the length of the bytearray will be used - * If data is an int, 4 will be used - * If data is a str, the size of the file will be used - :type n_bytes: int - :param offset: The offset where the valid data starts, if the data is \ - a int, then the offset will be ignored and 0 is used. - :type offset: int - :param is_filename: \ - True if the data should be interpreted as a file name - :type is_filename: bool - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: - * If there is an error communicating with the board - * If there is an error reading the executable - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :param int x: X coordinate of the chip + :param int y: Y coordinate of the chip + :param int p: Virtual processor identifier on the chip + :param int user: The user number of write data for + :param int value: The value to write + :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 spinnman.exceptions.SpinnmanInvalidParameterException: - * If one of the specified chips is not valid - * If app_id is an invalid application ID - * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanInvalidParameterException: + If x, y, p does not identify a valid processor + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - process = WriteMemoryFloodProcess(self._scamp_connection_selector) - # Ensure only one flood fill occurs at any one time - with self._flood_write_lock: - # Start the flood fill - nearest_neighbour_id = self._get_next_nearest_neighbour_id() - if isinstance(data, AbstractDataReader): - process.write_memory_from_reader( - nearest_neighbour_id, base_address, data, n_bytes) - elif isinstance(data, str) and is_filename: - if n_bytes is None: - n_bytes = os.stat(data).st_size - with FileDataReader(data) as reader: - process.write_memory_from_reader( - nearest_neighbour_id, base_address, reader, n_bytes) - elif isinstance(data, int): - data_to_write = _ONE_WORD.pack(data) - process.write_memory_from_bytearray( - nearest_neighbour_id, base_address, data_to_write, 0) - else: - if n_bytes is None: - n_bytes = len(data) - process.write_memory_from_bytearray( - nearest_neighbour_id, base_address, data, offset, n_bytes) + addr = self.__get_user_register_address_from_core(p, user) + self.write_memory(x, y, addr, int(value)) def read_memory(self, x, y, base_address, length, cpu=0): - """ Read some areas of SDRAM from the board + """ + Read some areas of memory (usually SDRAM) from the board. - :param x: \ + :param int x: The x-coordinate of the chip where the memory is to be read from - :type x: int - :param y: \ + :param int y: The y-coordinate of the chip where the memory is to be read from - :type y: int - :param base_address: \ + :param int base_address: The address in SDRAM where the region of memory to be read starts - :type base_address: int - :param length: The length of the data to be read in bytes - :type length: int - :param cpu: the core ID used to read the memory of - :type cpu: int + :param int length: The length of the data to be read in bytes + :param int cpu: + the core ID used to read the memory of; should usually be 0 when + reading from SDRAM, but may be other values when reading from DTCM. :return: A bytearray of data read - :rtype: bytearray - :raise spinnman.exceptions.SpinnmanIOException: \ + :rtype: bytes + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: - * If one of x, y, p, base_address or length is invalid + :raise SpinnmanInvalidParameterException: + * If one of `x`, `y`, `cpu`, `base_address` or `length` is invalid * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - - process = ReadMemoryProcess(self._scamp_connection_selector) - return process.read_memory(x, y, cpu, base_address, length) - - def read_neighbour_memory(self, x, y, link, base_address, length, cpu=0): - """ Read some areas of memory on a neighbouring chip using a LINK_READ - SCP command. If sent to a BMP, this command can be used to\ - communicate with the FPGAs' debug registers. - - :param x: \ - The x-coordinate of the chip whose neighbour is to be read from - :type x: int - :param y: \ - The y-coordinate of the chip whose neighbour is to be read from - :type y: int - :param cpu: The CPU to use, typically 0 (or if a BMP, the slot number) - :type cpu: int - :param link: \ - The link index to send the request to (or if BMP, the FPGA number) - :type link: int - :param base_address: \ - The address in SDRAM where the region of memory to be read starts - :type base_address: int - :param length: The length of the data to be read in bytes - :type length: int - :return: An iterable of chunks of data read in order - :rtype: iterable of bytearray - :raise spinnman.exceptions.SpinnmanIOException: \ + try: + process = ReadMemoryProcess(self._scamp_connection_selector) + return process.read_memory(x, y, cpu, base_address, length) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise + + def read_word(self, x, y, base_address, cpu=0): + """ + Read a word (usually of SDRAM) from the board. + + :param int x: + The x-coordinate of the chip where the word is to be read from + :param int y: + The y-coordinate of the chip where the word is to be read from + :param int base_address: + The address (usually in SDRAM) where the word to be read starts + :param int cpu: + the core ID used to read the word; should usually be 0 when reading + from SDRAM, but may be other values when reading from DTCM. + :return: The unsigned integer value at ``base_address`` + :rtype: int + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: - * If one of x, y, p, base_address or length is invalid + :raise SpinnmanInvalidParameterException: + * If one of `x`, `y`, `cpu` or `base_address` is invalid * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - - process = ReadMemoryProcess(self._scamp_connection_selector) - return process.read_link_memory(x, y, cpu, link, base_address, length) + try: + process = ReadMemoryProcess(self._scamp_connection_selector) + data = process.read_memory(x, y, cpu, base_address, _ONE_WORD.size) + (value, ) = _ONE_WORD.unpack(data) + return value + except Exception: + logger.info(self._where_is_xy(x, y)) + raise def stop_application(self, app_id): - """ Sends a stop request for an app_id + """ + Sends a stop request for an app_id. - :param app_id: The ID of the application to send to - :type app_id: int - :raise spinnman.exceptions.SpinnmanIOException: \ + :param int app_id: The ID of the application to send to + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If app_id is not a valid application ID * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ @@ -2007,36 +1341,55 @@ def stop_application(self, app_id): "You are calling a app stop on a turned off machine. " "Please fix and try again") + def __log_where_is_info(self, cpu_infos): + """ + Logs the where_is info for each chip in cpu_infos. + + :param cpu_infos: + """ + xys = set() + for cpu_info in cpu_infos: + if isinstance(cpu_info, tuple): + xys.add((cpu_info[0], cpu_info[1])) + else: + xys.add((cpu_info.x, cpu_info.y)) + for (x, y) in xys: + logger.info(self._where_is_xy(x, y)) + def wait_for_cores_to_be_in_state( self, all_core_subsets, app_id, cpu_states, timeout=None, time_between_polls=0.1, error_states=frozenset({ CPUState.RUN_TIME_EXCEPTION, CPUState.WATCHDOG}), - counts_between_full_check=100): - """ Waits for the specified cores running the given application to be\ - in some target state or states. Handles failures. - - :param all_core_subsets: the cores to check are in a given sync state - :param app_id: the application ID that being used by the simulation - :param cpu_states:\ - The expected states once the applications are ready; success is\ + counts_between_full_check=100, progress_bar=None): + """ + Waits for the specified cores running the given application to be + in some target state or states. Handles failures. + + :param ~spinn_machine.CoreSubsets all_core_subsets: + the cores to check are in a given sync state + :param int app_id: the application ID that being used by the simulation + :param set(CPUState) cpu_states: + The expected states once the applications are ready; success is when each application is in one of these states - :param timeout:\ - The amount of time to wait in seconds for the cores to reach one\ + :param float timeout: + The amount of time to wait in seconds for the cores to reach one of the states - :param time_between_polls: Time between checking the state - :param error_states:\ - Set of states that the application can be in that indicate an\ + :param float time_between_polls: Time between checking the state + :param set(CPUState) error_states: + Set of states that the application can be in that indicate an error, and so should raise an exception - :param counts_between_full_check:\ - The number of times to use the count signal before instead using\ + :param int counts_between_full_check: + The number of times to use the count signal before instead using the full CPU state check - :raise spinnman.exceptions.SpinnmanTimeoutException: \ + :param progress_bar: Possible progress bar to update. + :type progress_bar: ~spinn_utilities.progress_bar.ProgressBar or None + :raise SpinnmanTimeoutException: If a timeout is specified and exceeded. """ - # check that the right number of processors are in the states processors_ready = 0 + max_processors_ready = 0 timeout_time = None if timeout is None else time.time() + timeout tries = 0 while (processors_ready < len(all_core_subsets) and @@ -2047,7 +1400,11 @@ def wait_for_cores_to_be_in_state( for cpu_state in cpu_states: processors_ready += self.get_core_state_count( app_id, cpu_state) - + if progress_bar: + if processors_ready > max_processors_ready: + progress_bar.update( + processors_ready - max_processors_ready) + max_processors_ready = processors_ready # If the count is too small, check for error states if processors_ready < len(all_core_subsets): is_error = False @@ -2056,9 +1413,10 @@ def wait_for_cores_to_be_in_state( if error_cores > 0: is_error = True if is_error: - error_core_states = self.get_cores_in_state( - all_core_subsets, error_states) - if len(error_states) > 0: + error_core_states = self.get_cpu_infos( + all_core_subsets, error_states, True) + if len(error_core_states) > 0: + self.__log_where_is_info(error_core_states) raise SpiNNManCoresNotInStateException( timeout, cpu_states, error_core_states) @@ -2066,188 +1424,93 @@ def wait_for_cores_to_be_in_state( # do a full check if required tries += 1 if tries >= counts_between_full_check: - cores_in_state = self.get_cores_in_state( - all_core_subsets, cpu_states) + cores_in_state = self.get_cpu_infos( + all_core_subsets, cpu_states, True) processors_ready = len(cores_in_state) tries = 0 + # iterate over the cores waiting to finish and see + # which ones we're missing + if get_config_bool("Machine", "report_waiting_logs"): + 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()): + logger.warning( + "waiting on {}:{}:{}", + core_subset.x, core_subset.y, p) + # If we're still not in the correct state, wait a bit if processors_ready < len(all_core_subsets): time.sleep(time_between_polls) # If we haven't reached the final state, do a final full check if processors_ready < len(all_core_subsets): - cores_not_in_state = self.get_cores_not_in_state( - all_core_subsets, cpu_states) + cores_not_in_state = self.get_cpu_infos( + all_core_subsets, cpu_states, False) # If we are sure we haven't reached the final state, # report a timeout error if len(cores_not_in_state) != 0: + self.__log_where_is_info(cores_not_in_state) raise SpiNNManCoresNotInStateException( timeout, cpu_states, cores_not_in_state) - def get_cores_in_state(self, all_core_subsets, states): - """ Get all cores that are in a given state or set of states - - :param all_core_subsets: The cores to filter - :type all_core_subsets:\ - :py:class:`spinn_machine.CoreSubsets` - :param states: The state or states to filter on - :type states:\ - :py:class:`spinnman.model.enums.CPUState` \ - or\ - set(:py:class:`spinnman.model.enums.CPUState`) - :return: Core subsets object containing cores in the given state(s) - """ - core_infos = self.get_cpu_information(all_core_subsets) - cores_in_state = CPUInfos() - for core_info in core_infos: - if hasattr(states, "__iter__"): - if core_info.state in states: - cores_in_state.add_processor( - core_info.x, core_info.y, core_info.p, core_info) - elif core_info.state == states: - cores_in_state.add_processor( - core_info.x, core_info.y, core_info.p, core_info) - - return cores_in_state - - def get_cores_not_in_state(self, all_core_subsets, states): - """ Get all cores that are not in a given state or set of states - - :param all_core_subsets: The cores to filter - :type all_core_subsets:\ - :py:class:`spinn_machine.CoreSubsets` - :param states: The state or states to filter on - :type states:\ - :py:class:`spinnman.model.enums.CPUState` \ - or \ - set(:py:class:`spinnman.model.enums.CPUState`) - :return: Core subsets object containing cores not in the given state(s) - """ - core_infos = self.get_cpu_information(all_core_subsets) - cores_not_in_state = CPUInfos() - for core_info in core_infos: - if hasattr(states, "__iter__"): - if core_info.state not in states: - cores_not_in_state.add_processor( - core_info.x, core_info.y, core_info.p, core_info) - elif core_info.state != states: - cores_not_in_state.add_processor( - core_info.x, core_info.y, core_info.p, core_info) - return cores_not_in_state - - def get_core_status_string(self, cpu_infos): - """ Get a string indicating the status of the given cores - - :param cpu_infos: A CPUInfos objects - :type cpu_infos: \ - :py:class:`spinnman.model.cpu_infos.CPUInfos` - """ - break_down = "\n" - for (x, y, p), core_info in cpu_infos.cpu_infos: - if core_info.state == CPUState.RUN_TIME_EXCEPTION: - break_down += " {}:{}:{} in state {}:{}\n".format( - x, y, p, 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={}".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) - return break_down - def send_signal(self, app_id, signal): - """ Send a signal to an application - - :param app_id: The ID of the application to send to - :type app_id: int - :param signal: The signal to send - :type signal: :py:class:`spinnman.messages.scp.Signal` - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: \ + """ + Send a signal to an application. + + :param int app_id: The ID of the application to send to + :param ~spinnman.messages.scp.enums.Signal signal: The signal to send + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If signal is not a valid signal * If app_id is not a valid application ID * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ process = SendSingleCommandProcess(self._scamp_connection_selector) process.execute(SendSignal(app_id, signal)) - def set_leds(self, x, y, cpu, led_states): - """ Set SetLED states. - - :param x: The x-coordinate of the chip on which to set the LEDs - :type x: int - :param y: The x-coordinate of the chip on which to set the LEDs - :type y: int - :param cpu: The CPU of the chip on which to set the LEDs - :type cpu: int - :param led_states: A dictionary mapping SetLED index to state with\ - 0 being off, 1 on and 2 inverted. - :type led_states: dict - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: \ - If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ - If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ - If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ - If a response indicates an error during the exchange + def _locate_spinnaker_connection_for_board_address(self, board_address): """ - process = SendSingleCommandProcess(self._scamp_connection_selector) - process.execute(SetLED(x, y, cpu, led_states)) - - def locate_spinnaker_connection_for_board_address(self, board_address): - """ Find a connection that matches the given board IP address + Find a connection that matches the given board IP address. - :param board_address: \ + :param str board_address: The IP address of the Ethernet connection on the board - :type board_address: str - :return: A connection for the given IP address, or None if no such\ + :return: A connection for the given IP address, or `None` if no such connection exists - :rtype:\ - :py:class:`spinnman.connections.udp_packet_connections.SCAMPConnection` + :rtype: SCAMPConnection """ return self._udp_scamp_connections.get(board_address, None) def set_ip_tag(self, ip_tag, use_sender=False): - """ Set up an IP tag + """ + Set up an IP tag. + + :param ~spinn_machine.tags.IPTag ip_tag: + The tag to set up. - :param ip_tag: The tag to set up; note board_address can be None, in\ - which case, the tag will be assigned to all boards - :type ip_tag: :py:class:`spinn_machine.tags.IPTag` - :param use_sender: Optionally use the sender host and port instead of\ + .. note:: + `board_address` can be `None`, in which case, the tag will be + assigned to all boards. + :param bool use_sender: + Optionally use the sender host and port instead of the given host and port in the tag - :param use_sender: bool - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: \ + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If the IP tag fields are incorrect * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - # Check that the tag has a port assigned if ip_tag.port is None: raise SpinnmanInvalidParameterException( @@ -2262,7 +1525,6 @@ def set_ip_tag(self, ip_tag, use_sender=False): "The given board address is not recognised") for connection in connections: - # Convert the host string host_string = ip_tag.ip_address if host_string in ("localhost", ".", "0.0.0.0"): @@ -2276,53 +1538,50 @@ def set_ip_tag(self, ip_tag, use_sender=False): ip_tag.tag, strip=ip_tag.strip_sdp, use_sender=use_sender)) def __get_connection_list(self, connection=None, board_address=None): - """ Get the connections for talking to a board. + """ + Get the connections for talking to a board. - :param connection: \ + :param SCAMPConnection connection: Optional param that directly gives the connection to use. - :type connection: \ - :py:class:`spinnman.connections.abstract_classes.SCPSender` - :param board_address: \ + :param str board_address: Optional param that gives the address of the board to talk to. - :type board_address: str - :return: List of length 1 or 0 (the latter only if the search for \ + :return: List of length 1 or 0 (the latter only if the search for the given board address fails). - :rtype: list of \ - :py:class:`spinnman.connections.abstract_classes.SCPSender` + :rtype: list(SCAMPConnection) """ if connection is not None: return [connection] elif board_address is None: return self._scamp_connections - connection = self.locate_spinnaker_connection_for_board_address( + connection = self._locate_spinnaker_connection_for_board_address( board_address) if connection is None: return [] return [connection] def set_reverse_ip_tag(self, reverse_ip_tag): - """ Set up a reverse IP tag - - :param reverse_ip_tag: The reverse tag to set up; note board_address\ - can be None, in which case, the tag will be assigned to all boards - :type reverse_ip_tag:\ - :py:class:`spinn_machine.tags.ReverseIPTag` - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: \ + """ + Set up a reverse IP tag. + + :param ~spinn_machine.tags.ReverseIPTag reverse_ip_tag: + The reverse tag to set up. + + .. note:: + The `board_address` field can be `None`, in which case, the tag + will be assigned to all boards. + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If the reverse IP tag fields are incorrect * If a packet is received that has invalid parameters - * If the UDP port is one that is already used by SpiNNaker for \ + * If the UDP port is one that is already used by SpiNNaker for system functions - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - if reverse_ip_tag.port is None: raise SpinnmanInvalidParameterException( "reverse_ip_tag.port", "None", @@ -2333,8 +1592,8 @@ def set_reverse_ip_tag(self, reverse_ip_tag): raise SpinnmanInvalidParameterException( "reverse_ip_tag.port", reverse_ip_tag.port, "The port number for the reverse IP tag conflicts with" - " the SpiNNaker system ports ({} and {})".format( - SCP_SCAMP_PORT, UDP_BOOT_CONNECTION_DEFAULT_PORT)) + f" the SpiNNaker system ports ({SCP_SCAMP_PORT} and " + f"{UDP_BOOT_CONNECTION_DEFAULT_PORT})") # Get the connections - if the tag specifies a connection, use that, # otherwise apply the tag to all connections @@ -2355,24 +1614,23 @@ def set_reverse_ip_tag(self, reverse_ip_tag): reverse_ip_tag.sdp_port)) def clear_ip_tag(self, tag, board_address=None): - """ Clear the setting of an IP tag - - :param tag: The tag ID - :type tag: int - :param board_address: Board address where the tag should be cleared.\ - If not specified, all SCPSender connections will send the message\ - to clear the tag - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: \ + """ + Clear the setting of an IP tag. + + :param int tag: The tag ID + :param str board_address: + Board address where the tag should be cleared. + If not specified, all AbstractSCPConnection connections will send + the message to clear the tag + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If the tag is not a valid tag * If the connection cannot send SDP messages * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ for conn in self.__get_connection_list(board_address=board_address): @@ -2380,24 +1638,23 @@ def clear_ip_tag(self, tag, board_address=None): process.execute(IPTagClear(conn.chip_x, conn.chip_y, tag)) def get_tags(self, connection=None): - """ Get the current set of tags that have been set on the board + """ + Get the current set of tags that have been set on the board. - :param connection: Connection from which the tags should be received.\ - If not specified, all SCPSender connections will be queried and\ - the response will be combined. - :type connection:\ - :py:class:`spinnman.connections.abstract_classes.SCPSender` + :param AbstractSCPConnection connection: + Connection from which the tags should be received. + If not specified, all AbstractSCPConnection connections will be + queried and the response will be combined. :return: An iterable of tags - :rtype: iterable of\ - :py:class:`spinn_machine.tags.AbstractTag` - :raise spinnman.exceptions.SpinnmanIOException: \ + :rtype: iterable(~spinn_machine.tags.AbstractTag) + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If the connection cannot send SDP messages * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ all_tags = list() @@ -2407,234 +1664,227 @@ def get_tags(self, connection=None): return all_tags def malloc_sdram(self, x, y, size, app_id, tag=None): - """ Allocates a chunk of SDRAM on a chip on the machine - - :param x: The x-coordinate of the chip onto which to ask for memory - :type x: int - :param y: The y-coordinate of the chip onto which to ask for memory - :type y: int - :param size: the amount of memory to allocate in bytes - :type size: int - :param app_id: The ID of the application with which to associate the\ - routes. If not specified, defaults to 0. - :type app_id: int - :param tag: the tag for the SDRAM, a 8-bit (chip-wide) tag that can be\ - looked up by a SpiNNaker application to discover the address of\ + """ + Allocates a chunk of SDRAM on a chip on the machine. + + :param int x: The x-coordinate of the chip onto which to ask for memory + :param int y: The y-coordinate of the chip onto which to ask for memory + :param int size: the amount of memory to allocate in bytes + :param int app_id: The ID of the application with which to associate + the routes. If not specified, defaults to 0. + :param int tag: the tag for the SDRAM, a 8-bit (chip-wide) tag that can + be looked up by a SpiNNaker application to discover the address of the allocated block. If `0` then no tag is applied. - :type tag: int :return: the base address of the allocated memory :rtype: int """ - process = MallocSDRAMProcess(self._scamp_connection_selector) - process.malloc_sdram(x, y, size, app_id, tag) - return process.base_address - - def free_sdram(self, x, y, base_address, app_id): - """ Free allocated SDRAM - - :param x: The x-coordinate of the chip onto which to ask for memory - :type x: int - :param y: The y-coordinate of the chip onto which to ask for memory - :type y: int - :param base_address: The base address of the allocated memory - :type base_address: int - :param app_id: The app ID of the allocated memory - :type app_id: int - """ - process = DeAllocSDRAMProcess(self._scamp_connection_selector) - process.de_alloc_sdram(x, y, app_id, base_address) - - def free_sdram_by_app_id(self, x, y, app_id): - """ Free all SDRAM allocated to a given app ID - - :param x: The x-coordinate of the chip onto which to ask for memory - :type x: int - :param y: The y-coordinate of the chip onto which to ask for memory - :type y: int - :param app_id: The app ID of the allocated memory - :type app_id: int - :return: The number of blocks freed - :rtype: int - """ - process = DeAllocSDRAMProcess(self._scamp_connection_selector) - process.de_alloc_sdram(x, y, app_id) - return process.no_blocks_freed + try: + process = MallocSDRAMProcess(self._scamp_connection_selector) + process.malloc_sdram(x, y, size, app_id, tag) + return process.base_address + except Exception: + logger.info(self._where_is_xy(x, y)) + raise def load_multicast_routes(self, x, y, routes, app_id): - """ Load a set of multicast routes on to a chip - - :param x: The x-coordinate of the chip onto which to load the routes - :type x: int - :param y: The y-coordinate of the chip onto which to load the routes - :type y: int - :param routes: An iterable of multicast routes to load - :type routes: iterable of\ - :py:class:`spinn_machine.MulticastRoutingEntry` - :param app_id: The ID of the application with which to associate the\ - routes. If not specified, defaults to 0. - :type app_id: int - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: \ + """ + Load a set of multicast routes on to a chip. + + :param int x: + The x-coordinate of the chip onto which to load the routes + :param int y: + The y-coordinate of the chip onto which to load the routes + :param iterable(~spinn_machine.MulticastRoutingEntry) routes: + An iterable of multicast routes to load + :param int app_id: The ID of the application with which to associate + the routes. If not specified, defaults to 0. + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If any of the routes are invalid * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - - process = LoadMultiCastRoutesProcess(self._scamp_connection_selector) - process.load_routes(x, y, routes, app_id) + try: + process = LoadMultiCastRoutesProcess( + self._scamp_connection_selector) + process.load_routes(x, y, routes, app_id) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise def load_fixed_route(self, x, y, fixed_route, app_id): - """ Loads a fixed route routing table entry onto a chip's router. - - :param x: The x-coordinate of the chip onto which to load the routes - :type x: int - :param y: The y-coordinate of the chip onto which to load the routes - :type y: int - :param fixed_route: the route for the fixed route entry on this chip - :type fixed_route: :py:class:`spinn_machine.fixed_route_routing_entry` - :param app_id: The ID of the application with which to associate the\ - routes. If not specified, defaults to 0. - :type app_id: int - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: \ + """ + Loads a fixed route routing table entry onto a chip's router. + + :param int x: + The x-coordinate of the chip onto which to load the routes + :param int y: + The y-coordinate of the chip onto which to load the routes + :param ~spinn_machine.FixedRouteEntry fixed_route: + the route for the fixed route entry on this chip + :param int app_id: The ID of the application with which to associate + the routes. If not specified, defaults to 0. + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If any of the routes are invalid * If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - process = LoadFixedRouteRoutingEntryProcess( - self._scamp_connection_selector) - process.load_fixed_route(x, y, fixed_route, app_id) + try: + process = LoadFixedRouteRoutingEntryProcess( + self._scamp_connection_selector) + process.load_fixed_route(x, y, fixed_route, app_id) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise def read_fixed_route(self, x, y, app_id): - """ Reads a fixed route routing table entry from a chip's router. + """ + Reads a fixed route routing table entry from a chip's router. - :param x: The x-coordinate of the chip onto which to load the routes - :type x: int - :param y: The y-coordinate of the chip onto which to load the routes - :type y: int - :param app_id: The ID of the application with which to associate the\ + :param int x: + The x-coordinate of the chip onto which to load the routes + :param int y: + The y-coordinate of the chip onto which to load the routes + :param int app_id: + The ID of the application with which to associate the routes. If not specified, defaults to 0. - :type app_id: int :return: the route as a fixed route entry """ - process = ReadFixedRouteRoutingEntryProcess( - self._scamp_connection_selector) - return process.read_fixed_route(x, y, app_id) + try: + process = ReadFixedRouteRoutingEntryProcess( + self._scamp_connection_selector) + return process.read_fixed_route(x, y, app_id) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise def get_multicast_routes(self, x, y, app_id=None): - """ Get the current multicast routes set up on a chip + """ + Get the current multicast routes set up on a chip. - :param x: The x-coordinate of the chip from which to get the routes - :type x: int - :param y: The y-coordinate of the chip from which to get the routes - :type y: int - :param app_id: The ID of the application to filter the routes for. If\ + :param int x: + The x-coordinate of the chip from which to get the routes + :param int y: + The y-coordinate of the chip from which to get the routes + :param int app_id: + The ID of the application to filter the routes for. If not specified, will return all routes - :type app_id: int :return: An iterable of multicast routes - :rtype: iterable of\ - :py:class:`spinnman.model.multicast_routing_entry.MulticastRoute` - :raise spinnman.exceptions.SpinnmanIOException: \ + :rtype: list(~spinn_machine.MulticastRoutingEntry) + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + :raise SpinnmanInvalidParameterException: If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - base_address = self._get_sv_data( - x, y, SystemVariableDefinition.router_table_copy_address) - process = GetMultiCastRoutesProcess( - self._scamp_connection_selector, app_id) - return process.get_routes(x, y, base_address) + try: + base_address = self._get_sv_data( + x, y, SystemVariableDefinition.router_table_copy_address) + process = GetMultiCastRoutesProcess( + self._scamp_connection_selector, app_id) + return process.get_routes(x, y, base_address) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise def clear_multicast_routes(self, x, y): - """ Remove all the multicast routes on a chip - - :param x: The x-coordinate of the chip on which to clear the routes - :type x: int - :param y: The y-coordinate of the chip on which to clear the routes - :type y: int - :return: Nothing is returned - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: \ + """ + Remove all the multicast routes on a chip. + + :param int x: The x-coordinate of the chip on which to clear the routes + :param int y: The y-coordinate of the chip on which to clear the routes + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + :raise SpinnmanInvalidParameterException: If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - process = SendSingleCommandProcess(self._scamp_connection_selector) - process.execute(RouterClear(x, y)) + try: + process = SendSingleCommandProcess(self._scamp_connection_selector) + process.execute(RouterClear(x, y)) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise def get_router_diagnostics(self, x, y): - """ Get router diagnostic information from a chip + """ + Get router diagnostic information from a chip. - :param x: \ + :param int x: The x-coordinate of the chip from which to get the information - :type x: int - :param y: \ + :param int y: The y-coordinate of the chip from which to get the information - :type y: int :return: The router diagnostic information - :rtype: :py:class:`spinnman.model.router_diagnostics.RouterDiagnostics` - :raise spinnman.exceptions.SpinnmanIOException: \ + :rtype: RouterDiagnostics + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ + :raise SpinnmanInvalidParameterException: If a packet is received that has invalid parameters - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - process = ReadRouterDiagnosticsProcess(self._scamp_connection_selector) - return process.get_router_diagnostics(x, y) + try: + process = ReadRouterDiagnosticsProcess( + self._scamp_connection_selector) + return process.get_router_diagnostics(x, y) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise def set_router_diagnostic_filter(self, x, y, position, diagnostic_filter): - """ Sets a router diagnostic filter in a router - - :param x: \ - the X address of the router in which this filter is being set - :type x: int - :param y: \ - the Y address of the router in which this filter is being set - :type y: int - :param position: the position in the list of filters where this filter\ - is to be added - :type position: int - :param diagnostic_filter: the diagnostic filter being set in the\ - placed, between 0 and 15 (note that positions 0 to 11 are used by\ - the default filters, and setting these positions will result in a\ - warning). - :type diagnostic_filter:\ - :py:class:`spinnman.model.diagnostic_filter.DiagnosticFilter` - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: + """ + Sets a router diagnostic filter in a router. + + :param int x: + The X address of the router in which this filter is being set. + :param int y: + The Y address of the router in which this filter is being set. + :param int position: + The position in the list of filters where this filter is to be + added. + :param ~spinnman.model.DiagnosticFilter diagnostic_filter: + The diagnostic filter being set in the placed, between 0 and 15. + + .. note:: + Positions 0 to 11 are used by the default filters, + and setting these positions will result in a warning. + :raise SpinnmanIOException: * If there is an error communicating with the board * If there is an error reading the data - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: + :raise SpinnmanInvalidParameterException: * If x, y does not lead to a valid chip * If position is less than 0 or more than 15 - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ + try: + self.__set_router_diagnostic_filter( + x, y, position, diagnostic_filter) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise + + def __set_router_diagnostic_filter( + self, x, y, position, diagnostic_filter): data_to_send = diagnostic_filter.filter_word if position > NO_ROUTER_DIAGNOSTIC_FILTERS: raise SpinnmanInvalidParameterException( @@ -2656,290 +1906,75 @@ def set_router_diagnostic_filter(self, x, y, position, diagnostic_filter): process.execute(WriteMemory( x, y, memory_position, _ONE_WORD.pack(data_to_send))) - def get_router_diagnostic_filter(self, x, y, position): - """ Gets a router diagnostic filter from a router - - :param x: the X address of the router from which this filter is being\ - retrieved - :type x: int - :param y: the Y address of the router from which this filter is being\ - retrieved - :type y: int - :param position: the position in the list of filters where this filter\ - is to be added - :type position: int - :return: The diagnostic filter read - :rtype: :py:class:`spinnman.model.diagnostic_filter.DiagnosticFilter` - :raise spinnman.exceptions.SpinnmanIOException: - * If there is an error communicating with the board - * If there is an error reading the data - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ - If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: - * If x, y does not lead to a valid chip - * If a packet is received that has invalid parameters - * If position is less than 0 or more than 15 - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ - If a response indicates an error during the exchange + def clear_router_diagnostic_counters(self, x, y): """ - memory_position = ( - ROUTER_REGISTER_BASE_ADDRESS + ROUTER_FILTER_CONTROLS_OFFSET + - position * ROUTER_DIAGNOSTIC_FILTER_SIZE) + Clear router diagnostic information on a chip. - process = SendSingleCommandProcess(self._scamp_connection_selector) - response = process.execute(ReadMemory(x, y, memory_position, 4)) - return DiagnosticFilter.read_from_int(_ONE_WORD.unpack_from( - response.data, response.offset)[0]) - - def clear_router_diagnostic_counters(self, x, y, enable=True, - counter_ids=None): - """ Clear router diagnostic information on a chip - - :param x: The x-coordinate of the chip - :type x: int - :param y: The y-coordinate of the chip - :type y: int - :param enable: True (default) if the counters should be enabled - :type enable: bool - :param counter_ids: The IDs of the counters to reset (all by default)\ - and enable if enable is True; each must be between 0 and 15 - :type counter_ids: array-like of int - :rtype: None - :raise spinnman.exceptions.SpinnmanIOException: \ + :param int x: The x-coordinate of the chip + :param int y: The y-coordinate of the chip + :raise SpinnmanIOException: If there is an error communicating with the board - :raise spinnman.exceptions.SpinnmanInvalidPacketException: \ + :raise SpinnmanInvalidPacketException: If a packet is received that is not in the valid format - :raise spinnman.exceptions.SpinnmanInvalidParameterException: \ - If a packet is received that has invalid parameters or a counter\ + :raise SpinnmanInvalidParameterException: + If a packet is received that has invalid parameters or a counter ID is out of range - :raise spinnman.exceptions.SpinnmanUnexpectedResponseCodeException: \ + :raise SpinnmanUnexpectedResponseCodeException: If a response indicates an error during the exchange """ - if counter_ids is None: - counter_ids = range(0, 16) - clear_data = 0 - for counter_id in counter_ids: - if counter_id < 0 or counter_id > 15: - raise SpinnmanInvalidParameterException( - "counter_id", counter_id, - "Diagnostic counter IDs must be between 0 and 15") - clear_data |= 1 << counter_id - if enable: - for counter_id in counter_ids: - clear_data |= 1 << counter_id + 16 - process = SendSingleCommandProcess(self._scamp_connection_selector) - process.execute(WriteMemory( - x, y, 0xf100002c, _ONE_WORD.pack(clear_data))) + try: + process = SendSingleCommandProcess(self._scamp_connection_selector) + # Clear all + process.execute(WriteMemory( + x, y, 0xf100002c, _ONE_WORD.pack(0xFFFFFFFF))) + except Exception: + logger.info(self._where_is_xy(x, y)) + raise - @property - def number_of_boards_located(self): - """ Get the number of boards currently configured + def close(self): """ - boards = 0 - for bmp_connection in self._bmp_connections: - boards += len(bmp_connection.boards) - - # if no BMPs are available, then there's still at least one board - return max((1, boards)) + Close the transceiver and any threads that are running. + """ + if self._bmp_connection is not None: + if get_config_bool("Machine", "turn_off_machine"): + self.power_off_machine() - def close(self, close_original_connections=True, power_off_machine=False): - """ Close the transceiver and any threads that are running + for connection in self._all_connections: + connection.close() - :param close_original_connections: If True, the original connections\ - passed to the transceiver in the constructor are also closed.\ - If False, only newly discovered connections are closed. - :param power_off_machine: if true, the machine is sent a power down\ - command via its BMP (if it has one) - :type power_off_machine: bool - :return: Nothing is returned - :rtype: None - :raise None: No known exceptions are raised + def control_sync(self, do_sync): """ + Control the synchronisation of the chips. - if power_off_machine and self._bmp_connections: - self.power_off_machine() + :param bool do_sync: Whether to synchronise or not + """ + process = SendSingleCommandProcess(self._scamp_connection_selector) + process.execute(DoSync(do_sync)) - for connections in self._udp_receive_connections_by_port.values(): - for (_, listener) in connections.values(): - if listener is not None: - listener.close() + def update_provenance_and_exit(self, x, y, p): + """ + Sends a command to update prevenance and exit - for connection in self._all_connections: - if (close_original_connections or - connection not in self._original_connections): - connection.close() - - def register_udp_listener(self, callback, connection_class, - local_port=None, local_host=None): - """ Register a callback for a certain type of traffic to be received\ - via UDP. Note that the connection class must extend\ - :py:class:`spinnman.connections.abstract_classes.Listenable` - to avoid clashing with the SCAMP and BMP functionality - - :param callback: Function to be called when a packet is received - :type callback: function(packet) - :param connection_class: The class of connection to receive using - :type connection_class: subclass of\ - :py:class:`spinnman.connections.abstract_classes.Listenable` - :param local_port: The optional port number to listen on; if not\ - specified, an existing connection will be used if possible,\ - otherwise a random free port number will be used - :type local_port: int - :param local_host: The optional hostname or IP address to listen on;\ - if not specified, all interfaces will be used for listening - :type local_host: str - :return: The connection to be used - :rtype:\ - :py:class:`spinnman.connection.udp_packet_connections.UDPConnection` - """ - - # If the connection class is not an Listenable, this is an - # error - if not issubclass(connection_class, Listenable): - raise SpinnmanInvalidParameterException( - "connection_class", connection_class, - "The connection class must be Listenable") - - connections_of_class = self._udp_listenable_connections_by_class[ - connection_class] - connection = None - listener = None - - # If the local port was specified - if local_port is not None: - receiving_connections = self._udp_receive_connections_by_port[ - local_port] - - # If something is already listening on this port - if receiving_connections: - - if local_host is None or local_host == "0.0.0.0": - # If we are to listen on all interfaces and the listener - # is not on all interfaces, this is an error - if "0.0.0.0" not in receiving_connections: - raise SpinnmanInvalidParameterException( - "local_port", str(local_port), - "Another connection is already listening on this" - " port") - - # Normalise the local host - local_host = "0.0.0.0" - else: - # If we are to listen to a specific interface, and the - # listener is on all interfaces, this is an error - if "0.0.0.0" in receiving_connections: - raise SpinnmanInvalidPacketException( - "local_port and local_host", - "{} and {}".format(local_port, local_host)) - - # If the type of an existing connection is wrong, this is an - # error - if local_host in receiving_connections: - connection, listener = receiving_connections[local_host] - if not isinstance(connection, connection_class): - raise SpinnmanInvalidParameterException( - "connection_class", connection_class, - "A connection of class {} is already listening on" - "this port on all interfaces".format( - connection.__class__)) - - # If we are here, nothing is listening on this port, so create - # a connection if there isn't already one, and a listener - if connection is None: - connection = connection_class(local_port=local_port, - local_host=local_host) - self._all_connections.add(connection) - - if listener is None: - listener = ConnectionListener(connection) - listener.start() - receiving_connections[local_host] = (connection, listener) - - listener.add_callback(callback) - connections_of_class.append((connection, listener)) - return connection - - # If we are here, the local port wasn't specified to try to use an - # existing connection of the correct class - if connections_of_class: - - # If local_host is not specified, normalise it - if local_host is None: - local_host = "0.0.0.0" - - # Find a connection that matches the local host - for a_connection, a_listener in connections_of_class: - if a_connection.local_ip_address == local_host: - connection, listener = (a_connection, a_listener) - break - - # Create a connection if there isn't already one, and a listener - if connection is None: - connection = connection_class(local_host=local_host) - self._all_connections.add(connection) - - if listener is None: - listener = ConnectionListener(connection) - listener.start() - self._udp_receive_connections_by_port[connection.local_port][ - local_host] = (connection, listener) - - listener.add_callback(callback) - connections_of_class.append((connection, listener)) - return connection - - @property - def scamp_connection_selector(self): - return self._scamp_connection_selector - - @property - def bmp_connection(self): - return self._bmp_connection_selectors - - def get_heap(self, x, y, heap=SystemVariableDefinition.sdram_heap_address): - """ Get the contents of the given heap on a given chip - - :param x: The x-coordinate of the chip - :type x: int - :param y: The y-coordinate of the chip - :type y: int - :param heap: The SystemVariableDefinition which is the heap to read - :type heap: \ - :py:class:`spinnman.messages.spinnaker_boot.SystemVariableDefinition` - """ - process = GetHeapProcess(self._scamp_connection_selector) - return process.get_heap((x, y), heap) - - def fill_memory( - self, x, y, base_address, repeat_value, bytes_to_fill, - data_type=FillDataType.WORD): - """ Fill some memory with repeated data - - :param x: The x-coordinate of the chip - :type x: int - :param y: The y-coordinate of the chip - :type y: int - :param base_address: The address at which to start the fill - :type base_address: int - :param repeat_value: The data to repeat - :type repeat_value: int - :param bytes_to_fill:\ - The number of bytes to fill. Must be compatible with the data\ - type i.e. if the data type is WORD, the number of bytes must\ - be divisible by 4 - :type bytes_to_fill: int - :param data_type: - :type data_type:\ - :py:class:`spinnman.processes.fill_process.FillDataType` - """ - process = FillProcess(self._scamp_connection_selector) - return process.fill_memory( - x, y, base_address, repeat_value, bytes_to_fill, data_type) + :param int x: + The x-coordinate of the core + :param int y: + The y-coordinate of the core + :param int p: + The processor on the core + """ + # Send these signals to make sure the application isn't stuck + self.send_sdp_message(SDPMessage( + sdp_header=SDPHeader( + flags=SDPFlag.REPLY_NOT_EXPECTED, + destination_port=SDP_PORTS.RUNNING_COMMAND_SDP_PORT.value, + destination_chip_x=x, destination_chip_y=y, destination_cpu=p), + data=_ONE_WORD.pack(SDP_RUNNING_MESSAGE_CODES + .SDP_UPDATE_PROVENCE_REGION_AND_EXIT.value))) def __str__(self): - return "transceiver object connected to {} with {} connections"\ - .format(self._scamp_connections[0].remote_ip_address, - len(self._all_connections)) + addr = self._scamp_connections[0].remote_ip_address + n = len(self._all_connections) + return f"transceiver object connected to {addr} with {n} connections" def __repr__(self): return self.__str__() diff --git a/spinnman/utilities/__init__.py b/spinnman/utilities/__init__.py index d358f58a8..f3a422e41 100644 --- a/spinnman/utilities/__init__.py +++ b/spinnman/utilities/__init__.py @@ -1,14 +1,13 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/spinnman/utilities/appid_tracker.py b/spinnman/utilities/appid_tracker.py index 704606237..58c35e28e 100644 --- a/spinnman/utilities/appid_tracker.py +++ b/spinnman/utilities/appid_tracker.py @@ -1,24 +1,24 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. _MIN_APP_ID = 17 _MAX_APP_ID = 254 class AppIdTracker(object): - """ A tracker of application IDs to make it easier to allocate new IDs. + """ + A tracker of application IDs to make it easier to allocate new IDs. """ __slots__ = [ "_free_ids", @@ -33,11 +33,9 @@ def __init__( max_app_id=_MAX_APP_ID): """ :param app_ids_in_use: The IDs that are already in use - :type app_ids_in_use: list[int] or None - :param min_app_id: The smallest application ID to use - :type min_app_id: int - :param max_app_id: The largest application ID to use - :type max_app_id: int + :type app_ids_in_use: list(int) or None + :param int min_app_id: The smallest application ID to use + :param int max_app_id: The largest application ID to use """ self._free_ids = set(range(min_app_id, max_app_id)) if app_ids_in_use is not None: @@ -46,27 +44,31 @@ def __init__( self._max_app_id = max_app_id def get_new_id(self): - """ Get a new unallocated ID + """ + Get a new unallocated ID :rtype: int """ return self._free_ids.pop() def allocate_id(self, allocated_id): - """ Allocate a given ID. + """ + Allocate a given ID. - :param allocated_id: The ID to allocate + :param int allocated_id: The ID to allocate :raises KeyError: If the ID is not present """ self._free_ids.remove(allocated_id) def free_id(self, id_to_free): - """ Free a given ID. + """ + Free a given ID. - :param id_to_free: The ID to free + :param int id_to_free: The ID to free :raises KeyError: If the ID is out of range """ if id_to_free < self._min_app_id or id_to_free > self._max_app_id: - raise KeyError("ID {} out of allowed range of {} to {}".format( - id_to_free, self._min_app_id, self._max_app_id)) + raise KeyError( + f"ID {id_to_free} out of allowed range of {self._min_app_id} " + f"to {self._max_app_id}") self._free_ids.add(id_to_free) diff --git a/spinnman/utilities/io/__init__.py b/spinnman/utilities/io/__init__.py deleted file mode 100644 index 3445a7fb1..000000000 --- a/spinnman/utilities/io/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -from .abstract_io import AbstractIO -from .file_io import FileIO -from .memory_io import MemoryIO - -__all__ = [ - "AbstractIO", "FileIO", "MemoryIO" -] diff --git a/spinnman/utilities/io/abstract_io.py b/spinnman/utilities/io/abstract_io.py deleted file mode 100644 index 8ff51f1a3..000000000 --- a/spinnman/utilities/io/abstract_io.py +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import os -from abc import (ABCMeta, abstractmethod, abstractproperty) -from six import add_metaclass -from spinnman.processes.fill_process import FillDataType - - -@add_metaclass(ABCMeta) -class AbstractIO(object): - __slots__ = [] - - @abstractmethod - def __len__(self): - """ The size of the entire region of memory - """ - - @abstractmethod - def __getitem__(self, new_slice): - """ Get a sub-region of this memory object. The index or slice must\ - be in range of the current region to be valid. - - :param new_slice:\ - A single index for a single byte of memory, or a contiguous slice - :rtype: :py:class:`~MemoryIO` - :raise ValueError:\ - If the index or slice is outside of the current region - """ - - @abstractmethod - def __enter__(self): - """ Enter a new block which will call :py:meth:`~.close` when exited. - """ - - @abstractmethod - def __exit__(self, exception_type, exception_value, traceback): - """ Exit a block and call :py:meth:`~.close`. - """ - - @abstractmethod - def close(self): - """ Close the IO object - """ - - @abstractproperty - def closed(self): - """ Indicates if the object has been closed - """ - - @abstractmethod - def flush(self): - """ Flush any outstanding written data - """ - - @abstractmethod - def seek(self, n_bytes, from_what=os.SEEK_SET): - """ Seek to a position within the region - """ - - @abstractmethod - def tell(self): - """ Return the current position within the region relative to the start - """ - - @abstractproperty - def address(self): - """ Return the current absolute address within the region - """ - - @abstractmethod - def read(self, n_bytes=None): - """ Read a number of bytes, or the rest of the data if n_bytes is None\ - or negative - - :param n_bytes: The number of bytes to read - :rtype: bytes - :raise EOFError: If the read will be beyond the end of the region - """ - - @abstractmethod - def write(self, data): - """ Write some data to the region - - :param data: The data to write - :type data: bytes - :return: The number of bytes written - :rtype: int - :raise EOFError: If the write will go over the end of the region - """ - - @abstractmethod - def fill( - self, repeat_value, bytes_to_fill=None, - data_type=FillDataType.WORD): - """ Fill the next part of the region with repeated data - - :param repeat_value: The value to repeat - :type repeat_value: int - :param bytes_to_fill:\ - Optional number of bytes to fill from current position, or None\ - to fill to the end - :type bytes_to_fill: int - :param data_type: The type of the repeat value - :type data_type: :py:class:`spinnman.process.fill_process.FillDataType` - :raise EOFError: If the amount of data to fill is more than the region - """ diff --git a/spinnman/utilities/io/file_io.py b/spinnman/utilities/io/file_io.py deleted file mode 100644 index 6fc41cd41..000000000 --- a/spinnman/utilities/io/file_io.py +++ /dev/null @@ -1,192 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import os -import struct -from spinn_utilities.overrides import overrides -from spinnman.utilities.io.abstract_io import AbstractIO -from spinnman.processes.fill_process import FillDataType - - -class FileIO(AbstractIO): - """ A file input/output interface to match the MemoryIO interface - """ - - __slots__ = [ - - # The file to write to - "_file", - - # The current offset in the file - "_current_offset", - - # The start offset in the file - "_start_offset", - - # The end offset in the file - "_end_offset" - ] - - def __init__(self, file_obj, start_offset, end_offset): - """ - :param file_obj: The file handle or file name to write to - :type file_obj: str or file - :param start_offset: The start offset into the file - :type start_offset: int - :param end_offset: The end offset from the start of the file - :type end_offset: int or None - """ - self._file = file_obj - if isinstance(file_obj, str): - self._file = open(file_obj, "wb+") - - self._current_offset = start_offset - self._start_offset = start_offset - self._end_offset = end_offset - - @overrides(AbstractIO.__len__) - def __len__(self): - return self._end_offset - self._start_offset - - @overrides(AbstractIO.__getitem__) - def __getitem__(self, new_slice): - if isinstance(new_slice, int): - if new_slice >= len(self): - raise ValueError("Index {} out of range".format) - return FileIO( - self._file, self._start_offset + new_slice, - self._start_offset + new_slice + 1) - elif isinstance(new_slice, slice): - if new_slice.step is not None and new_slice.step != 1: - raise ValueError("Slice must be contiguous") - start_offset = self._start_offset - end_offset = self._end_offset - if new_slice.start is not None: - if new_slice.start < 0: - start_offset = self._end_offset + new_slice.start - else: - start_offset = self._start_offset + new_slice.start - if new_slice.stop is not None: - if new_slice.stop > 0: - end_offset = self._start_offset + new_slice.stop - else: - end_offset = self._end_offset + new_slice.stop - - if (start_offset < self._start_offset or - end_offset < self._start_offset or - end_offset > self._end_offset or - start_offset > self._end_offset): - raise ValueError("Slice {} outside of this region".format( - new_slice)) - if start_offset == end_offset: - raise ValueError("Zero sized regions are not supported") - - return FileIO(self._file, start_offset, end_offset) - - @overrides(AbstractIO.__enter__) - def __enter__(self): - return self - - @overrides(AbstractIO.__exit__) - def __exit__(self, exception_type, exception_value, traceback): - self.close() - - @overrides(AbstractIO.close) - def close(self): - self._file.close() - - @property - @overrides(AbstractIO.closed) - def closed(self): - return self._file.closed - - @overrides(AbstractIO.flush) - def flush(self): - self._file.flush() - - @overrides(AbstractIO.seek) - def seek(self, n_bytes, from_what=os.SEEK_SET): - position = 0 - if from_what == os.SEEK_SET: - position = self._start_offset + n_bytes - elif from_what == os.SEEK_CUR: - position = self._current_offset + n_bytes - elif from_what == os.SEEK_END: - position = self._end_offset + n_bytes - else: - raise ValueError( - "Value of from_what must be one of os.SEEK_SET, os.SEEK_CUR" - " or os.SEEK_END") - - if position < self._start_offset or position > self._end_offset: - raise ValueError( - "Attempt to seek to a position of {} which is outside of the" - " region".format(position)) - - self._current_offset = position - - @overrides(AbstractIO.tell) - def tell(self): - return self._current_offset - self._start_offset - - @property - @overrides(AbstractIO.address) - def address(self): - return self._current_offset - - @overrides(AbstractIO.read) - def read(self, n_bytes=None): - if n_bytes == 0: - return b"" - if n_bytes is None or n_bytes < 0: - n_bytes = self._end_offset - self._current_offset - if self._current_offset + n_bytes > self._end_offset: - raise EOFError - - self._file.seek(self._current_offset) - data = bytes(self._file.read(n_bytes)) - self._current_offset += n_bytes - return data - - @overrides(AbstractIO.write) - def write(self, data): - n_bytes = len(data) - - if self._current_offset + n_bytes > self._end_offset: - raise EOFError - - self._file.seek(self._current_offset) - self._file.write(data) - self._current_offset += n_bytes - return n_bytes - - @overrides(AbstractIO.fill) - def fill(self, repeat_value, bytes_to_fill=None, - data_type=FillDataType.WORD): - if bytes_to_fill is None: - bytes_to_fill = self._end_offset - self._current_offset - if self._current_offset + bytes_to_fill > self._end_offset: - raise EOFError - if bytes_to_fill % data_type.value != 0: - raise ValueError( - "The size of {} bytes to fill is not divisible by the size of" - " the data of {} bytes".format(bytes_to_fill, data_type.value)) - data_to_fill = struct.pack( - str(data_type.struct_type[-1]), repeat_value - ) - self._file.seek(self._current_offset) - for _ in range(bytes_to_fill // data_type.value): - self._file.write(data_to_fill) - self._current_offset += bytes_to_fill diff --git a/spinnman/utilities/io/memory_io.py b/spinnman/utilities/io/memory_io.py deleted file mode 100644 index a8318be4c..000000000 --- a/spinnman/utilities/io/memory_io.py +++ /dev/null @@ -1,383 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import os -import struct -from spinn_utilities.overrides import overrides -from spinnman.utilities.io.abstract_io import AbstractIO -from spinnman.processes.fill_process import FillDataType - -# A set of ChipMemoryIO objects that have been created, -# indexed by transceiver, x and y (thus two transceivers might not see the -# same buffered memory) -_chip_memory_io_objects = dict() - -# Start of SDRAM, using *unbuffered* memory access protocol. -UNBUFFERED_SDRAM_START = 0x60000000 - - -def _get_chip_memory_io(transceiver, x, y): - if (transceiver, x, y) not in _chip_memory_io_objects: - _chip_memory_io_objects[transceiver, x, y] = _ChipMemoryIO( - transceiver, x, y) - return _chip_memory_io_objects[transceiver, x, y] - - -class _ChipMemoryIO(object): - """ A file-like object for the memory of a chip - """ - - __slots__ = [ - - # The transceiver for speaking to the machine - "_transceiver", - - # The x coordinate of the chip to communicate with - "_x", - - # The y coordinate of the chip to communicate with - "_y", - - # The current pointer where read and writes are taking place - "_current_address", - - # The current pointer where the next buffered write will occur - "_write_address", - - # The write buffer size - "_buffer_size", - - # The write buffer bytearray - "_write_buffer", - - # The write buffer memory view - "_write_memory_view", - - # The write buffer pointer - "_write_buffer_offset" - ] - - def __init__( - self, transceiver, x, y, base_address=UNBUFFERED_SDRAM_START, - buffer_size=256): - """ - :param transceiver: The transceiver to read and write with - :param x: The x-coordinate of the chip to write to - :param y: The y-coordinate of the chip to write to - :param base_address: The lowest address that can be written - :param buffer_size: The size of the write buffer to improve efficiency - """ - # pylint: disable=too-many-arguments - self._transceiver = transceiver - self._x = x - self._y = y - self._current_address = base_address - self._write_address = base_address - self._buffer_size = buffer_size - self._write_buffer = bytearray(self._buffer_size) - self._write_memory_view = memoryview(self._write_buffer) - self._write_buffer_offset = 0 - - @property - def transceiver(self): - return self._transceiver - - @property - def x(self): - return self._x - - @property - def y(self): - return self._y - - def flush_write_buffer(self): - """ Force the writing of the current write buffer - """ - if self._write_buffer_offset > 0: - self._transceiver.write_memory( - self._x, self._y, self._write_address, self._write_buffer, - n_bytes=self._write_buffer_offset) - self._write_address += self._write_buffer_offset - self._write_buffer_offset = 0 - - @property - def current_address(self): - """ Return the current absolute address within the region - """ - return self._current_address - - @current_address.setter - def current_address(self, address): - """ Seek to a position within the region - """ - self.flush_write_buffer() - self._current_address = address - self._write_address = address - - def read(self, n_bytes): - """ Read a number of bytes - - :param n_bytes: The number of bytes to read - :rtype: bytes - """ - if n_bytes == 0: - return b"" - - self.flush_write_buffer() - data = self._transceiver.read_memory( - self._x, self._y, self._current_address, n_bytes) - self._current_address += n_bytes - self._write_address = self._current_address - - return data - - def write(self, data): - """ Write some data - - :param data: The data to write - :type data: bytes - """ - n_bytes = len(data) - - if n_bytes >= self._buffer_size: - self.flush_write_buffer() - self._transceiver.write_memory( - self._x, self._y, self._current_address, data) - self._current_address += n_bytes - self._write_address = self._current_address - - else: - n_bytes_to_copy = min( - n_bytes, self._buffer_size - self._write_buffer_offset) - self._write_memory_view[ - self._write_buffer_offset: - self._write_buffer_offset + n_bytes_to_copy - ] = data[:n_bytes_to_copy] - self._write_buffer_offset += n_bytes_to_copy - self._current_address += n_bytes_to_copy - n_bytes -= n_bytes_to_copy - if self._write_buffer_offset == self._buffer_size: - self.flush_write_buffer() - if n_bytes > 0: - self._write_memory_view[:n_bytes] = data[n_bytes_to_copy:] - self._write_buffer_offset += n_bytes - self._current_address += n_bytes - - def fill(self, repeat_value, bytes_to_fill, data_type=FillDataType.WORD): - """ Fill the memory with repeated data - - :param repeat_value: The value to repeat - :type repeat_value: int - :param bytes_to_fill: Number of bytes to fill from current position - :type bytes_to_fill: int - :param data_type: The type of the repeat value - :type data_type: \ - :py:class:`spinnman.processes.fill_process.FillDataType` - """ - self.flush_write_buffer() - self._transceiver.fill_memory( - self._x, self._y, self._current_address, repeat_value, - bytes_to_fill, data_type) - self._current_address += bytes_to_fill - - -class MemoryIO(AbstractIO): - """ A file-like object for reading and writing memory - """ - - __slots__ = [ - - # The transceiver for speaking to the machine - "_chip_memory_io", - - # The start address of the region to write to - "_start_address", - - # The current pointer where read and writes are taking place - "_current_address", - - # The end of the region to write to - "_end_address" - ] - - # Cache of all writes that are currently buffered in any instance, to - # ensure they are written before a read occurs - __write_cache__ = dict() - - def __init__(self, transceiver, x, y, start_address, end_address): - """ - :param transceiver: The transceiver to read and write with - :param x: The x-coordinate of the chip to write to - :param y: The y-coordinate of the chip to write to - :param start_address: The start address of the region to write to - :param end_address:\ - The end address of the region to write to. This is the first\ - address just outside the region - """ - # pylint: disable=too-many-arguments - if start_address >= end_address: - raise ValueError("Start address must be less than end address") - - self._chip_memory_io = _get_chip_memory_io(transceiver, x, y) - self._start_address = start_address - self._current_address = start_address - self._end_address = end_address - - @overrides(AbstractIO.__len__) - def __len__(self): - return self._end_address - self._start_address - - @overrides(AbstractIO.__getitem__) - def __getitem__(self, new_slice): - if isinstance(new_slice, int): - if new_slice >= len(self): - raise ValueError("Index {} out of range".format) - return MemoryIO( - self._chip_memory_io.transceiver, self._chip_memory_io.x, - self._chip_memory_io.y, - self._start_address + new_slice, - self._start_address + new_slice + 1) - elif isinstance(new_slice, slice): - if new_slice.step is not None and new_slice.step != 1: - raise ValueError("Slice must be contiguous") - start_address = self._start_address - end_address = self._end_address - if new_slice.start is not None: - if new_slice.start < 0: - start_address = self._end_address + new_slice.start - else: - start_address = self._start_address + new_slice.start - if new_slice.stop is not None: - if new_slice.stop > 0: - end_address = self._start_address + new_slice.stop - else: - end_address = self._end_address + new_slice.stop - - if (start_address < self._start_address or - end_address < self._start_address or - end_address > self._end_address or - start_address > self._end_address): - raise ValueError("Slice {} outside of this region".format( - new_slice)) - if start_address == end_address: - raise ValueError("Zero sized regions are not supported") - - return MemoryIO( - self._chip_memory_io.transceiver, self._chip_memory_io.x, - self._chip_memory_io.y, - start_address, end_address) - - @overrides(AbstractIO.__enter__) - def __enter__(self): - return self - - @overrides(AbstractIO.__exit__) - def __exit__(self, exception_type, exception_value, traceback): - self.close() - return False - - @overrides(AbstractIO.close) - def close(self): - self._chip_memory_io.flush_write_buffer() - - @property - @overrides(AbstractIO.closed) - def closed(self): - return False - - @overrides(AbstractIO.flush) - def flush(self): - self._chip_memory_io.flush_write_buffer() - - @overrides(AbstractIO.seek) - def seek(self, n_bytes, from_what=os.SEEK_SET): - """ Seek to a position within the region - """ - position = 0 - if from_what == os.SEEK_SET: - position = self._start_address + n_bytes - elif from_what == os.SEEK_CUR: - position = self._current_address + n_bytes - elif from_what == os.SEEK_END: - position = self._end_address + n_bytes - else: - raise ValueError( - "Value of from_what must be one of os.SEEK_SET, os.SEEK_CUR" - " or os.SEEK_END") - - if position < self._start_address or position > self._end_address: - raise ValueError( - "Attempt to seek to a position of {} which is outside of the" - " region".format(position)) - - self._current_address = position - - @overrides(AbstractIO.tell) - def tell(self): - return self._current_address - self._start_address - - @property - def address(self): - """ Return the current absolute address within the region - """ - return self._current_address - - @overrides(AbstractIO.read) - def read(self, n_bytes=None): - bytes_to_read = n_bytes - if n_bytes is None or n_bytes < 0: - bytes_to_read = self._end_address - self._current_address - - if self._current_address + bytes_to_read > self._end_address: - raise EOFError - - self._chip_memory_io.current_address = self._current_address - data = bytes(self._chip_memory_io.read(bytes_to_read)) - self._current_address += bytes_to_read - - return data - - @overrides(AbstractIO.write) - def write(self, data): - n_bytes = len(data) - - if self._current_address + n_bytes > self._end_address: - raise EOFError - - self._chip_memory_io.current_address = self._current_address - self._chip_memory_io.write(data) - self._current_address += n_bytes - - return n_bytes - - @overrides(AbstractIO.fill) - def fill(self, repeat_value, bytes_to_fill=None, - data_type=FillDataType.WORD): - if bytes_to_fill is None: - bytes_to_fill = self._end_address - self._current_address - if self._current_address + bytes_to_fill > self._end_address: - raise EOFError - - if bytes_to_fill % data_type.value != 0: - raise ValueError( - "The size of {} bytes to fill is not divisible by the size of" - " the data of {} bytes".format(bytes_to_fill, data_type.value)) - data_to_fill = struct.pack( - "{}".format(data_type.struct_type[-1]), - repeat_value) - self._chip_memory_io.current_address = self._current_address - for _ in range(bytes_to_fill // data_type.value): - self._chip_memory_io.write(data_to_fill) - self._current_address += bytes_to_fill diff --git a/spinnman/utilities/locate_connected_machine_ip_address.py b/spinnman/utilities/locate_connected_machine_ip_address.py index 7a4527efa..0318849fb 100644 --- a/spinnman/utilities/locate_connected_machine_ip_address.py +++ b/spinnman/utilities/locate_connected_machine_ip_address.py @@ -1,19 +1,17 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2015 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -from __future__ import print_function import time import sys import signal @@ -22,13 +20,14 @@ def locate_connected_machine(handler): - """ Locates any SpiNNaker machines IP addresses from the auto-transmitted\ - packets from non-booted SpiNNaker machines. + """ + Locates any SpiNNaker machines IP addresses from the auto-transmitted + packets from non-booted SpiNNaker machines. - :param handler: A callback that decides whether to stop searching. The\ - callback is given two arguments: the IP address found and the current\ - time. - :type handler: (ipaddr, float) --> bool + :param ~collections.abc.Callable handler: + A callback that decides whether to stop searching. The callback is + given two arguments: the IP address found and the current time. It + should return True if the search should cease. """ connection = IPAddressesConnection() @@ -43,9 +42,9 @@ def locate_connected_machine(handler): if __name__ == "__main__": - def ctrlc_handler(signal, frame): # @UnusedVariable + def _ctrlc_handler(sig, frame): # @UnusedVariable """ - :param signal: + :param sig: :param frame: :return: Never returns as it causes a sys.exit() """ @@ -53,12 +52,15 @@ def ctrlc_handler(signal, frame): # @UnusedVariable print("Exiting") sys.exit() - def print_connected(ip_address, timestamp): - print(ip_address, "({})".format( - socket.gethostbyaddr(ip_address)[0]), "at", timestamp) + def _print_connected(ip_address, timestamp): + try: + hostname = f" ({socket.gethostbyaddr(ip_address)[0]})" + except Exception: # pylint: disable=broad-except + hostname = "" + print(f"{ip_address}{hostname} at {timestamp}") return False print("The following addresses might be SpiNNaker boards " "(press Ctrl-C to quit):") - signal.signal(signal.SIGINT, ctrlc_handler) - locate_connected_machine(handler=print_connected) + signal.signal(signal.SIGINT, _ctrlc_handler) + locate_connected_machine(handler=_print_connected) diff --git a/spinnman/utilities/reports.py b/spinnman/utilities/reports.py index 02fbd3d2f..fede37f53 100644 --- a/spinnman/utilities/reports.py +++ b/spinnman/utilities/reports.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import os.path import time @@ -19,26 +18,23 @@ from spinn_utilities.log import FormatAdapter logger = FormatAdapter(logging.getLogger(__name__)) +_REPORT_NAME = "machine_structure.rpt" def generate_machine_report(report_directory, machine, connections): - """ Generate report on the physical structure of the target SpiNNaker \ - machine. + """ + Generate report on the physical structure of the target SpiNNaker machine. - :param report_directory: the directory to which reports are stored - :param machine: the machine python object - :param connections: the list of connections to the machine - :type report_directory: str - :type machine: :py:class:`spinn_machine.Machine` - :type connections: \ - iterable(:py:class:`spinnman.connections.abstract_classes.AbstractConnection`) - :rtype: None + :param str report_directory: the directory to which reports are stored + :param ~spinn_machine.Machine machine: the machine python object + :param list(Connection) connections: + the list of connections to the machine :raise IOError: when a file cannot be opened for some reason """ - file_name = os.path.join(report_directory, "machine_structure.rpt") + file_name = os.path.join(report_directory, _REPORT_NAME) time_date_string = time.strftime("%c") try: - with open(file_name, "w") as f: + with open(file_name, "w", encoding="utf-8") as f: _write_header(f, time_date_string, machine, connections) # TODO: Add further details on the target machine. for chip in machine.chips: @@ -51,21 +47,28 @@ def generate_machine_report(report_directory, machine, connections): def _write_header(f, timestamp, machine, connections): + """ + :param str timestamp: + :param ~spinn_machine.Machine machine: + :param list(Connection) connections: + """ f.write("\t\tTarget SpiNNaker Machine Structure\n") f.write("\t\t==================================\n") - f.write("\nGenerated: {} for target machine '{}'\n\n".format( - timestamp, connections)) - f.write("Machine dimensions (in chips) x : {} y : {}\n\n".format( - machine.width, machine.height)) + f.write(f"\nGenerated: {timestamp} for target machine '{connections}'\n\n") + f.write(f"Machine dimensions (in chips) x : {machine.width} " + f"y : {machine.height}\n\n") f.write("\t\tMachine router information\n") f.write("\t\t==========================\n") def _write_chip_router_report(f, chip): - f.write("\nInformation for chip {}:{}\n".format(chip.x, chip.y)) - f.write("Neighbouring chips \n{}\n".format( - chip.router.get_neighbouring_chips_coords())) + """ + :param ~spinn_machine.Chip chip: + """ + f.write(f"\nInformation for chip {chip.x}:{chip.y}\n") + f.write("Neighbouring chips\n" + f"{chip.router.get_neighbouring_chips_coords()}\n") f.write("Router list of links for this chip are: \n") for link in chip.router.links: - f.write("\t{}\n".format(link)) + f.write(f"\t{link}\n") f.write("\t\t==========================\n") diff --git a/spinnman/utilities/socket_utils.py b/spinnman/utilities/socket_utils.py new file mode 100644 index 000000000..5f0370217 --- /dev/null +++ b/spinnman/utilities/socket_utils.py @@ -0,0 +1,159 @@ +# Copyright (c) 2017 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Wrappers around socket-related system calls to do exception remapping and +apply some consistency to things. +""" + +import logging +import socket +from spinn_utilities.log import FormatAdapter +from spinnman.exceptions import SpinnmanIOException, SpinnmanTimeoutException + +logger = FormatAdapter(logging.getLogger(__name__)) + + +def get_udp_socket(): + """ + Wrapper round socket() system call to produce UDP/IPv4 sockets. + """ + try: + # Create a UDP Socket + return socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + except Exception as e: # pylint: disable=broad-except + raise SpinnmanIOException( + f"Error setting up socket: {e}") from e + + +def get_tcp_socket(): + """ + Wrapper round socket() system call to produce TCP/IPv4 sockets. + + .. note:: + TCP sockets cannot be used to talk to a SpiNNaker board. + """ + try: + # Create a UDP Socket + return socket.socket(socket.AF_INET, socket.SOCK_STREAM) + except Exception as e: # pylint: disable=broad-except + raise SpinnmanIOException( + f"Error setting up socket: {e}") from e + + +def set_receive_buffer_size(sock, size): + """ + Wrapper round setsockopt() system call. + """ + try: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, size) + except Exception: # pylint: disable=broad-except + # The OS said no, but we might still be able to work right with + # the defaults. Just warn and hope... + logger.warning("failed to configure UDP socket to have a large " + "receive buffer", exc_info=True) + + +def bind_socket(sock, host, port): + """ + Wrapper round bind() system call. + """ + try: + # Bind the socket + sock.bind((str(host), int(port))) + except Exception as e: # pylint: disable=broad-except + raise SpinnmanIOException( + f"Error binding socket to {host}:{port}: {e}") from e + + +def resolve_host(host): + """ + Wrapper round gethostbyname() system call. + """ + try: + return socket.gethostbyname(host) + except Exception as e: # pylint: disable=broad-except + raise SpinnmanIOException( + f"Error getting IP address for {host}: {e}") from e + + +def connect_socket(sock, remote_address, remote_port): + """ + Wrapper round connect() system call. + """ + try: + sock.connect((str(remote_address), int(remote_port))) + except Exception as e: # pylint: disable=broad-except + raise SpinnmanIOException( + f"Error connecting to {remote_address}:{remote_port}: {e}") from e + + +def get_socket_address(sock): + """ + Wrapper round getsockname() system call. + """ + try: + addr, port = sock.getsockname() + # Ensure that a standard address is used for the INADDR_ANY + # hostname + if addr is None or addr == "": + addr = "0.0.0.0" + return addr, port + except Exception as e: # pylint: disable=broad-except + raise SpinnmanIOException(f"Error querying socket: {e}") from e + + +def receive_message(sock, timeout, size): + """ + Wrapper round recv() system call. + """ + try: + sock.settimeout(timeout) + return sock.recv(size) + except socket.timeout as e: + raise SpinnmanTimeoutException("receive", timeout) from e + except Exception as e: # pylint: disable=broad-except + raise SpinnmanIOException(f"Error receiving: {e}") from e + + +def receive_message_and_address(sock, timeout, size): + """ + Wrapper round recvfrom() system call. + """ + try: + sock.settimeout(timeout) + return sock.recvfrom(size) + except socket.timeout as e: + raise SpinnmanTimeoutException("receive", timeout) from e + except Exception as e: # pylint: disable=broad-except + raise SpinnmanIOException(f"Error receiving: {e}") from e + + +def send_message(sock, data): + """ + Wrapper round send() system call. + """ + try: + return sock.send(data) + except Exception as e: # pylint: disable=broad-except + raise SpinnmanIOException(f"Error sending: {e}") from e + + +def send_message_to_address(sock, data, address): + """ + Wrapper round sendto() system call. + """ + try: + return sock.sendto(data, address) + except Exception as e: # pylint: disable=broad-except + raise SpinnmanIOException(f"Error sending: {e}") from e diff --git a/spinnman/utilities/utility_functions.py b/spinnman/utilities/utility_functions.py index 90836b3ee..014b670c5 100644 --- a/spinnman/utilities/utility_functions.py +++ b/spinnman/utilities/utility_functions.py @@ -1,39 +1,40 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import socket -import sys -from six import reraise from spinnman.model import BMPConnectionData +from spinnman.messages.scp.impl import IPTagSet from spinnman.messages.sdp import SDPMessage, SDPHeader, SDPFlag from spinnman.constants import SCP_SCAMP_PORT, CPU_INFO_BYTES, CPU_INFO_OFFSET +from spinnman.connections.udp_packet_connections import ( + SCAMPConnection, UDPConnection) from spinnman.connections.udp_packet_connections.utils import ( update_sdp_header_for_udp_send) -from spinnman.messages.scp.impl import IPTagSet from spinnman.exceptions import SpinnmanTimeoutException def work_out_bmp_from_machine_details(hostname, number_of_boards): - """ Work out the BMP connection IP address given the machine details.\ - This is assumed to be the IP address of the machine, with 1 subtracted\ - from the final part e.g. if the machine IP address is 192.168.0.5, the\ - BMP IP address is assumed to be 192.168.0.4 + """ + Work out the BMP connection IP address given the machine details. + This is assumed to be the IP address of the machine, with 1 subtracted + from the final part e.g. if the machine IP address is 192.168.0.5, the + BMP IP address is assumed to be 192.168.0.4 - :param hostname: the SpiNNaker machine main hostname or IP address - :param number_of_boards: the number of boards in the machine + :param str hostname: the SpiNNaker machine main hostname or IP address + :param int number_of_boards: the number of boards in the machine :return: The BMP connection data + :rtype: BMPConnectionData """ # take the IP address, split by dots, and subtract 1 off last bit ip_bits = socket.gethostbyname(hostname).split(".") @@ -49,27 +50,30 @@ def work_out_bmp_from_machine_details(hostname, number_of_boards): board_range = range(number_of_boards) # Assume a single board with no cabinet or frame specified - return BMPConnectionData(cabinet=0, frame=0, ip_address=bmp_ip_address, + return BMPConnectionData(ip_address=bmp_ip_address, boards=board_range, port_num=SCP_SCAMP_PORT) def get_vcpu_address(p): - """ Get the address of the vcpu_t structure for the given core + """ + Get the address of the vcpu_t structure for the given core. - :param p: The core - :type p: int + :param int p: The core + :rtype: int """ return CPU_INFO_OFFSET + (CPU_INFO_BYTES * p) def send_port_trigger_message(connection, board_address): - """ Sends a port trigger message using a connection to (hopefully) open a\ - port in a NAT and/or firewall to allow incoming packets to be received. + """ + Sends a port trigger message using a connection to (hopefully) open a + port in a NAT and/or firewall to allow incoming packets to be received. - :param connection: \ + :param UDPConnection connection: The UDP connection down which the trigger message should be sent - :param board_address: \ - The address of the SpiNNaker board to which the message should be sent + :param str board_address: + The IP address of the SpiNNaker board to which the message should be + sent """ # Set up the message so that no reply is expected and it is sent to an @@ -84,25 +88,74 @@ def send_port_trigger_message(connection, board_address): trigger_message.bytestring, (board_address, SCP_SCAMP_PORT)) -def reprogram_tag(connection, tag, strip=True): - """ Reprogram an IP Tag to send responses to a given SCAMPConnection - - :param connection: The connection to target the tag at - :param tag: The id of the tag to set - :param strip: True if +def reprogram_tag(connection: SCAMPConnection, tag: int, strip: bool = True): + """ + Reprogram an IP Tag to send responses to a given SCAMPConnection. + + :param SCAMPConnection connection: The connection to target the tag at + :param int tag: The id of the tag to set + :param bool strip: + True if the tag should strip SDP headers from outgoing messages + :raises SpinnmanTimeoutException: + If things time out several times """ request = IPTagSet( connection.chip_x, connection.chip_y, [0, 0, 0, 0], 0, tag, strip=strip, use_sender=True) data = connection.get_scp_data(request) - einfo = None + exn = None for _ in range(3): try: connection.send(data) - _, _, response, offset = \ - connection.receive_scp_response() + _, _, response, offset = connection.receive_scp_response() request.get_scp_response().read_bytestring(response, offset) return - except SpinnmanTimeoutException: - einfo = sys.exc_info() - reraise(*einfo) + except SpinnmanTimeoutException as e: + exn = e + # Should be impossible to get here with exn=None + raise exn or Exception + + +def reprogram_tag_to_listener( + connection: UDPConnection, x: int, y: int, ip_address: str, tag: int, + strip: bool = True, read_response: bool = True): + """ + Reprogram an IP Tag to send responses to a given connection that is + not connected to a specific board. Such connections are normally + receive-only connections. + + :param UDPConnection connection: The connection to target the tag at + :param int x: + The X coordinate of the Ethernet-enabled chip that should send to the + connection + :param int y: + The Y coordinate of the Ethernet-enabled chip that should send to the + connection + :param str ip_address: + The IP address of the Ethernet-enabled chip that should be given the + message + :param int tag: The id of the tag to set + :param bool strip: + True if the tag should strip SDP headers from outgoing messages + :param bool read_response: + True if the response to the reprogramming should be read + :raises SpinnmanTimeoutException: + If things time out several times + """ + request = IPTagSet( + x, y, [0, 0, 0, 0], 0, tag, + strip=strip, use_sender=True) + update_sdp_header_for_udp_send(request.sdp_header, x, y) + send_data = b'\0\0' + request.bytestring + exn = None + for _ in range(3): + try: + connection.send_to(send_data, (ip_address, SCP_SCAMP_PORT)) + if read_response: + request.get_scp_response().read_bytestring( + connection.receive(), 2) + return + except SpinnmanTimeoutException as e: + exn = e + # Should be impossible to get here with exn=None + raise exn or Exception diff --git a/test.cfg b/test.cfg deleted file mode 100644 index 6848309a7..000000000 --- a/test.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[Machine] -machineName = spinn-4.cs.man.ac.uk -bmp_names = spinn-4c.cs.man.ac.uk -version = 5 - -#machineName = 192.168.240.253 -#version = 2 -#bmp_names = None -auto_detect_bmp = False diff --git a/unittests/.gitignore b/unittests/.gitignore index 0569abce9..0be603289 100644 --- a/unittests/.gitignore +++ b/unittests/.gitignore @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. *.pyc __pycache__ \ No newline at end of file diff --git a/unittests/__init__.py b/unittests/__init__.py index e00b96137..1d5b5492f 100644 --- a/unittests/__init__.py +++ b/unittests/__init__.py @@ -1,16 +1,15 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. __author__ = 'Petrut' diff --git a/unittests/connection_tests/__init__.py b/unittests/connection_tests/__init__.py index d358f58a8..05bfa339d 100644 --- a/unittests/connection_tests/__init__.py +++ b/unittests/connection_tests/__init__.py @@ -1,14 +1,13 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/unittests/connection_tests/test_udp_boot_connection.py b/unittests/connection_tests/test_udp_boot_connection.py index b07633678..2796c1491 100644 --- a/unittests/connection_tests/test_udp_boot_connection.py +++ b/unittests/connection_tests/test_udp_boot_connection.py @@ -1,23 +1,27 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest from spinnman.connections.udp_packet_connections import BootConnection +from spinnman.config_setup import unittest_setup class MyTestCase(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_something(self): udp_connect = BootConnection() self.assertIsNotNone(udp_connect) diff --git a/unittests/connection_tests/test_udp_connection.py b/unittests/connection_tests/test_udp_connection.py index a6c788ec2..3aedd39b4 100644 --- a/unittests/connection_tests/test_udp_connection.py +++ b/unittests/connection_tests/test_udp_connection.py @@ -1,35 +1,37 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest from spinnman.connections.udp_packet_connections import SCAMPConnection +from spinnman.config_setup import unittest_setup from spinnman.exceptions import SpinnmanTimeoutException from spinnman.messages.scp.impl import GetVersion, ReadLink, ReadMemory from spinnman.messages.scp.enums import SCPResult from spinnman.messages.scp.impl.get_version_response import GetVersionResponse -from board_test_configuration import BoardTestConfiguration - -board_config = BoardTestConfiguration() +from spinnman.board_test_configuration import BoardTestConfiguration class TestUDPConnection(unittest.TestCase): + def setUp(self): + unittest_setup() + self.board_config = BoardTestConfiguration() + def test_scp_version_request_and_response_board(self): - board_config.set_up_remote_board() + self.board_config.set_up_remote_board() connection = SCAMPConnection( - remote_host=board_config.remotehost) + remote_host=self.board_config.remotehost) scp_req = GetVersion(0, 0, 0) scp_response = GetVersionResponse() connection.send_scp_request(scp_req) @@ -40,18 +42,18 @@ def test_scp_version_request_and_response_board(self): scp_response._scp_response_header._result, SCPResult.RC_OK) def test_scp_read_link_request_and_response_board(self): - board_config.set_up_remote_board() + self.board_config.set_up_remote_board() connection = SCAMPConnection( - remote_host=board_config.remotehost) + remote_host=self.board_config.remotehost) scp_link = ReadLink(0, 0, 0, 0x70000000, 250) connection.send_scp_request(scp_link) result, _, _, _ = connection.receive_scp_response() self.assertEqual(result, SCPResult.RC_OK) def test_scp_read_memory_request_and_response_board(self): - board_config.set_up_remote_board() + self.board_config.set_up_remote_board() connection = SCAMPConnection( - remote_host=board_config.remotehost) + remote_host=self.board_config.remotehost) scp_link = ReadMemory(0, 0, 0x70000000, 256) connection.send_scp_request(scp_link) result, _, _, _ = connection.receive_scp_response() @@ -59,9 +61,9 @@ def test_scp_read_memory_request_and_response_board(self): def test_send_scp_request_to_nonexistent_host(self): with self.assertRaises(SpinnmanTimeoutException): - board_config.set_up_nonexistent_board() + self.board_config.set_up_nonexistent_board() connection = SCAMPConnection( - remote_host=board_config.remotehost) + remote_host=self.board_config.remotehost) scp = ReadMemory(0, 0, 0, 256) connection.send_scp_request(scp) _, _, _, _ = connection.receive_scp_response(2) diff --git a/unittests/data/__init__.py b/unittests/data/__init__.py new file mode 100644 index 000000000..860375669 --- /dev/null +++ b/unittests/data/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2021 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/unittests/data/test_data.py b/unittests/data/test_data.py new file mode 100644 index 000000000..ab4fa0652 --- /dev/null +++ b/unittests/data/test_data.py @@ -0,0 +1,60 @@ +# Copyright (c) 2021 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from spinn_utilities.config_holder import set_config +from spinn_utilities.exceptions import (DataNotYetAvialable) +from spinnman.config_setup import unittest_setup +from spinnman.data import SpiNNManDataView +from spinnman.data.spinnman_data_writer import SpiNNManDataWriter +from spinnman.transceiver import Transceiver + + +class MockTranceiver(Transceiver): + + def __init__(self): + """ + Hide normal init + """ + + +class TestData(unittest.TestCase): + + def setUp(cls): + unittest_setup() + + def test_setup(self): + # What happens before setup depends on the previous test + # Use manual_check to verify this without dependency + SpiNNManDataWriter.setup() + with self.assertRaises(DataNotYetAvialable): + SpiNNManDataView.get_transceiver() + + def test_mock(self): + SpiNNManDataWriter.mock() + set_config("Machine", "version", 5) + # check there is a + # value not what it is + SpiNNManDataView.get_machine() + + def test_transceiver(self): + writer = SpiNNManDataWriter.setup() + with self.assertRaises(DataNotYetAvialable): + SpiNNManDataView.get_transceiver() + self.assertFalse(SpiNNManDataView.has_transceiver()) + writer.set_transceiver(MockTranceiver()) + SpiNNManDataView.get_transceiver() + self.assertTrue(SpiNNManDataView.has_transceiver()) + with self.assertRaises(TypeError): + writer.set_transceiver("bacon") diff --git a/unittests/data_tests/__init__.py b/unittests/data_tests/__init__.py deleted file mode 100644 index d358f58a8..000000000 --- a/unittests/data_tests/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . diff --git a/unittests/data_tests/file_data/txt_5_bytes b/unittests/data_tests/file_data/txt_5_bytes deleted file mode 100644 index bd41cba78..000000000 --- a/unittests/data_tests/file_data/txt_5_bytes +++ /dev/null @@ -1 +0,0 @@ -12345 \ No newline at end of file diff --git a/unittests/data_tests/file_data/txt_empty b/unittests/data_tests/file_data/txt_empty deleted file mode 100644 index e69de29bb..000000000 diff --git a/unittests/data_tests/file_data/txt_one_byte b/unittests/data_tests/file_data/txt_one_byte deleted file mode 100644 index 56a6051ca..000000000 --- a/unittests/data_tests/file_data/txt_one_byte +++ /dev/null @@ -1 +0,0 @@ -1 \ No newline at end of file diff --git a/unittests/data_tests/file_data/txt_one_byte_from_multiple_bytes b/unittests/data_tests/file_data/txt_one_byte_from_multiple_bytes deleted file mode 100644 index 9bd3ba708..000000000 --- a/unittests/data_tests/file_data/txt_one_byte_from_multiple_bytes +++ /dev/null @@ -1 +0,0 @@ -𤭢 \ No newline at end of file diff --git a/unittests/data_tests/test_file_data_reader.py b/unittests/data_tests/test_file_data_reader.py deleted file mode 100644 index a89d78388..000000000 --- a/unittests/data_tests/test_file_data_reader.py +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest -import tempfile -import os -from spinn_storage_handlers import FileDataReader - - -class TestFileDataReader(unittest.TestCase): - - @classmethod - def setUpClass(cls): - (os_fd, cls._file_txt_empty) = tempfile.mkstemp(text=False) - os.close(os_fd) - (os_fd, cls._file_txt_one_byte) = tempfile.mkstemp(text=False) - os.write(os_fd, b"1") - os.close(os_fd) - (os_fd, cls._file_txt_five_bytes) = tempfile.mkstemp(text=False) - os.write(os_fd, b"1") - os.write(os_fd, b"2") - os.write(os_fd, b"3") - os.write(os_fd, b"4") - os.write(os_fd, b"5") - os.close(os_fd) - - @classmethod - def tearDownClass(cls): - try: - os.remove(cls._file_txt_empty) - except OSError: - pass - - try: - os.remove(cls._file_txt_one_byte) - except OSError: - pass - - try: - os.remove(cls._file_txt_five_bytes) - except OSError: - pass - - def setUp(self): - self.reader = None - - def tearDown(self): - if self.reader is not None: - self.reader.close() - - def test_read_one_byte(self): - self.reader = FileDataReader(self._file_txt_one_byte) - stream = self.reader.read(1) - assert stream == b'1' - - def test_readinto_one_byte(self): - self.reader = FileDataReader(self._file_txt_one_byte) - ba = bytearray(1) - stream = self.reader.readinto(ba) - assert stream is not None - assert len(ba) == 1 - assert ba[0] == ord('1') - - def test_read_five_byte(self): - self.reader = FileDataReader(self._file_txt_five_bytes) - stream = self.reader.read(5) - assert stream is not None - assert len(stream) == 5 - assert stream == b'12345' - - def test_read_from_empty_file(self): - self.reader = FileDataReader(self._file_txt_empty) - stream = self.reader.read(1) - assert stream is not None - assert len(stream) == 0 - - def test_read_truncate(self): - self.reader = FileDataReader(self._file_txt_five_bytes) - stream = self.reader.read(2) - assert stream is not None - assert len(stream) == 2 - assert stream == b'12' - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/model_tests/__init__.py b/unittests/model_tests/__init__.py index d358f58a8..05bfa339d 100644 --- a/unittests/model_tests/__init__.py +++ b/unittests/model_tests/__init__.py @@ -1,14 +1,13 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/unittests/model_tests/test_abstract_process.py b/unittests/model_tests/test_abstract_process.py new file mode 100644 index 000000000..2aea5ed39 --- /dev/null +++ b/unittests/model_tests/test_abstract_process.py @@ -0,0 +1,50 @@ +# Copyright (c) 2020 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from spinn_utilities.config_holder import set_config +from spinnman.processes.abstract_multi_connection_process import ( + AbstractMultiConnectionProcess) +from spinnman.messages.scp.impl import ReadMemory +from spinnman.config_setup import unittest_setup +from spinnman.connections.udp_packet_connections import SCAMPConnection +from spinnman.exceptions import ( + SpinnmanTimeoutException, SpinnmanGenericProcessException) +from spinnman.processes import RoundRobinConnectionSelector +import pytest + + +class MockProcess(AbstractMultiConnectionProcess): + + def test(self): + self._send_request(ReadMemory(0, 0, 0, 4)) + self._finish() + self.check_for_error(print_exception=True) + + +class MockConnection(SCAMPConnection): + + def send(self, data): + pass + + def receive_scp_response(self, timeout=1.0): + raise SpinnmanTimeoutException("Test", timeout) + + +def test_error_print(): + unittest_setup() + set_config("Machine", "version", 5) + connection = MockConnection(0, 0) + process = MockProcess(RoundRobinConnectionSelector([connection])) + with pytest.raises(SpinnmanGenericProcessException): + process.test() diff --git a/unittests/model_tests/test_chip_info.py b/unittests/model_tests/test_chip_info.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/model_tests/test_chip_info.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/model_tests/test_core_subset_model.py b/unittests/model_tests/test_core_subset_model.py index 08cf42613..7f6deb03b 100644 --- a/unittests/model_tests/test_core_subset_model.py +++ b/unittests/model_tests/test_core_subset_model.py @@ -1,23 +1,27 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest from spinn_machine import CoreSubset +from spinnman.config_setup import unittest_setup class TestCoreSubset(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_create_new_core_subset(self): proc_list = [0, 1, 2, 3, 5, 8, 13] cs = CoreSubset(0, 0, proc_list) diff --git a/unittests/model_tests/test_core_subsets_model.py b/unittests/model_tests/test_core_subsets_model.py index de0ed6823..f0fdf001d 100644 --- a/unittests/model_tests/test_core_subsets_model.py +++ b/unittests/model_tests/test_core_subsets_model.py @@ -1,23 +1,27 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest from spinn_machine import CoreSubset, CoreSubsets +from spinnman.config_setup import unittest_setup class TestCoreSubsets(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_create_new_default_core_subsets(self): css = CoreSubsets() self.assertIsNotNone(css, "must make instance of CoreSubsets") diff --git a/unittests/model_tests/test_cpu_info.py b/unittests/model_tests/test_cpu_info.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/model_tests/test_cpu_info.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/model_tests/test_cpu_infos.py b/unittests/model_tests/test_cpu_infos.py new file mode 100644 index 000000000..4d27ba050 --- /dev/null +++ b/unittests/model_tests/test_cpu_infos.py @@ -0,0 +1,65 @@ +# Copyright (c) 2014 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from spinnman.config_setup import unittest_setup +from spinnman.model import CPUInfo, CPUInfos +from spinnman.model.enums.cpu_state import CPUState + + +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) + + self.assertEqual( + "['0, 0, 1 (ph: 5)', '0, 0, 2 (ph: 6)', '1, 0, 1 (ph: 7)']", + str(infos)) + + finished = infos.infos_for_state(CPUState.FINISHED) + self.assertEqual( + "['0, 0, 2 (ph: 6)', '1, 0, 1 (ph: 7)']", str(finished)) + self.assertTrue(finished) + + idle = infos.infos_for_state(CPUState.IDLE) + self.assertFalse(idle) + + info = infos.get_cpu_info(0, 0, 2) + self.assertEqual( + "0:0:02 (06) FINISHED scamp-3 0", str(info)) + + +if __name__ == '__main__': + unittest.main() diff --git a/unittests/model_tests/test_io_buff_model.py b/unittests/model_tests/test_io_buff_model.py index 40a5ab8fb..fac8286fe 100644 --- a/unittests/model_tests/test_io_buff_model.py +++ b/unittests/model_tests/test_io_buff_model.py @@ -1,23 +1,27 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest from spinnman.model import IOBuffer +from spinnman.config_setup import unittest_setup class TestingIOBuf(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_new_buf(self): iobuf = IOBuffer(0, 1, 2, 'Everything failed on chip.') self.assertIsNotNone(iobuf) diff --git a/unittests/model_tests/test_iptag_model.py b/unittests/model_tests/test_iptag_model.py index a921b6bb1..e870b6424 100644 --- a/unittests/model_tests/test_iptag_model.py +++ b/unittests/model_tests/test_iptag_model.py @@ -1,28 +1,30 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -from __future__ import absolute_import import unittest from spinn_machine.tags import IPTag -from board_test_configuration import BoardTestConfiguration - -board_config = BoardTestConfiguration() +from spinnman.board_test_configuration import BoardTestConfiguration +from spinnman.config_setup import unittest_setup class TestIptag(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_new_iptag(self): + board_config = BoardTestConfiguration() board_config.set_up_remote_board() ip = "8.8.8.8" port = 1337 diff --git a/unittests/model_tests/test_machine_dimensions_model.py b/unittests/model_tests/test_machine_dimensions_model.py index 9f223bf58..cb9b26735 100644 --- a/unittests/model_tests/test_machine_dimensions_model.py +++ b/unittests/model_tests/test_machine_dimensions_model.py @@ -1,24 +1,27 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest from spinnman.model import MachineDimensions +from spinnman.config_setup import unittest_setup class TestMachineDimensionsModel(unittest.TestCase): + def setUp(self): + unittest_setup() + def test_new_iptag(self): x_max = 253 y_max = 220 diff --git a/unittests/model_tests/test_retrieving_values_from_enum_models.py b/unittests/model_tests/test_retrieving_values_from_enum_models.py index 6270e7779..2c9c4d278 100644 --- a/unittests/model_tests/test_retrieving_values_from_enum_models.py +++ b/unittests/model_tests/test_retrieving_values_from_enum_models.py @@ -1,24 +1,28 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest from spinnman.model.enums import MailboxCommand, CPUState, RunTimeError from spinnman.constants import ROUTER_REGISTER_REGISTERS +from spinnman.config_setup import unittest_setup class TestingEnums(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_mailbox_command_enum(self): self.assertEqual(MailboxCommand.SHM_IDLE.value, 0) self.assertEqual(MailboxCommand.SHM_MSG.value, 1) diff --git a/unittests/model_tests/test_version_info_model.py b/unittests/model_tests/test_version_info_model.py index f4115c0c6..028806b2b 100644 --- a/unittests/model_tests/test_version_info_model.py +++ b/unittests/model_tests/test_version_info_model.py @@ -1,25 +1,29 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest import struct from spinnman.model import VersionInfo +from spinnman.config_setup import unittest_setup from spinnman.exceptions import SpinnmanInvalidParameterException class TestVersionInfo(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_retrieving_bits_from_version_data(self): p2p_adr = 0xf0a1 phys_cpu = 0xff diff --git a/unittests/scp_tests/__init__.py b/unittests/scp_tests/__init__.py index d358f58a8..05bfa339d 100644 --- a/unittests/scp_tests/__init__.py +++ b/unittests/scp_tests/__init__.py @@ -1,14 +1,13 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/unittests/scp_tests/test_count_state_request.py b/unittests/scp_tests/test_count_state_request.py deleted file mode 100644 index f09986102..000000000 --- a/unittests/scp_tests/test_count_state_request.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest -from spinnman.messages.scp.impl import CountState -from spinnman.model.enums import CPUState - - -class TestCPUStateRequest(unittest.TestCase): - def test_new_state_request(self): - request = CountState(32, CPUState.READY) - self.assertIsNotNone(request, "must make a CountState") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_count_state_response.py b/unittests/scp_tests/test_count_state_response.py index abbaf873d..ff41786d8 100644 --- a/unittests/scp_tests/test_count_state_response.py +++ b/unittests/scp_tests/test_count_state_response.py @@ -1,20 +1,20 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest from struct import pack +from spinnman.config_setup import unittest_setup from spinnman.exceptions import SpinnmanUnexpectedResponseCodeException from spinnman.messages.scp.impl.count_state_response import ( CountStateResponse) @@ -23,6 +23,10 @@ class TestCPUStateResponse(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_new_count_state_response(self): response = CountStateResponse() # SCP Stuff diff --git a/unittests/scp_tests/test_iptag_clear_request.py b/unittests/scp_tests/test_iptag_clear_request.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_iptag_clear_request.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_iptag_set_request.py b/unittests/scp_tests/test_iptag_set_request.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_iptag_set_request.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_application_run_request.py b/unittests/scp_tests/test_scp_application_run_request.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_scp_application_run_request.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_check_ok_response.py b/unittests/scp_tests/test_scp_check_ok_response.py index c7176e068..4766caf18 100644 --- a/unittests/scp_tests/test_scp_check_ok_response.py +++ b/unittests/scp_tests/test_scp_check_ok_response.py @@ -1,20 +1,20 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import struct import unittest +from spinnman.config_setup import unittest_setup from spinnman.exceptions import SpinnmanUnexpectedResponseCodeException from spinnman.messages.scp.impl import CheckOKResponse from spinnman.messages.scp.enums import SCPResult @@ -22,6 +22,10 @@ class TestOkResponse(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_new_scp_check_ok_response(self): CheckOKResponse("Testing operation", "Testing command") diff --git a/unittests/scp_tests/test_scp_enums.py b/unittests/scp_tests/test_scp_enums.py index bc4db08d4..7f7eca5de 100644 --- a/unittests/scp_tests/test_scp_enums.py +++ b/unittests/scp_tests/test_scp_enums.py @@ -1,19 +1,19 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest +from spinnman.config_setup import unittest_setup from spinnman.messages.scp.enums import IPTagCommand from spinnman.messages.scp.enums import SCPCommand from spinnman.messages.scp.enums import SCPResult @@ -22,6 +22,10 @@ class TestSCPEnums(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_iptag(self): self.assertEqual(IPTagCommand.NEW.value, 0) self.assertEqual(IPTagCommand.SET.value, 1) @@ -43,7 +47,7 @@ def test_command(self): self.assertEqual(SCPCommand.CMD_AR.value, 19) self.assertEqual(SCPCommand.CMD_NNP.value, 20) - self.assertEqual(SCPCommand.CMD_P2PC.value, 21) + self.assertEqual(SCPCommand.CMD_APP_COPY_RUN.value, 21) self.assertEqual(SCPCommand.CMD_SIG.value, 22) self.assertEqual(SCPCommand.CMD_FFD.value, 23) self.assertEqual(SCPCommand.CMD_AS.value, 24) @@ -54,6 +58,7 @@ def test_command(self): self.assertEqual(SCPCommand.CMD_ALLOC.value, 28) self.assertEqual(SCPCommand.CMD_RTR.value, 29) + self.assertEqual(SCPCommand.CMD_SYNC.value, 32) self.assertEqual(SCPCommand.CMD_FLASH_COPY.value, 49) self.assertEqual(SCPCommand.CMD_FLASH_ERASE.value, 50) self.assertEqual(SCPCommand.CMD_FLASH_WRITE.value, 51) @@ -107,9 +112,9 @@ def test_signal(self): SignalType.NEAREST_NEIGHBOUR) self.assertEqual(Signal.STOP.signal_type, SignalType.NEAREST_NEIGHBOUR) - self.assertEqual(Signal.START.signal_type, - SignalType.NEAREST_NEIGHBOUR) + self.assertEqual(Signal.START.signal_type, + SignalType.MULTICAST) self.assertEqual(Signal.SYNC0.signal_type, SignalType.MULTICAST) self.assertEqual(Signal.SYNC1.signal_type, diff --git a/unittests/scp_tests/test_scp_flood_fill_data_request.py b/unittests/scp_tests/test_scp_flood_fill_data_request.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_scp_flood_fill_data_request.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_flood_fill_end_request.py b/unittests/scp_tests/test_scp_flood_fill_end_request.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_scp_flood_fill_end_request.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_flood_fill_start_request.py b/unittests/scp_tests/test_scp_flood_fill_start_request.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_scp_flood_fill_start_request.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_iptag_get_request.py b/unittests/scp_tests/test_scp_iptag_get_request.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_scp_iptag_get_request.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_iptag_get_response.py b/unittests/scp_tests/test_scp_iptag_get_response.py deleted file mode 100644 index 4e34ca61a..000000000 --- a/unittests/scp_tests/test_scp_iptag_get_response.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest -# from struct import pack - - -class MyTestCase(unittest.TestCase): - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_iptag_info_request.py b/unittests/scp_tests/test_scp_iptag_info_request.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_scp_iptag_info_request.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_iptag_info_response.py b/unittests/scp_tests/test_scp_iptag_info_response.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_scp_iptag_info_response.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_link_request.py b/unittests/scp_tests/test_scp_link_request.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_scp_link_request.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_link_response.py b/unittests/scp_tests/test_scp_link_response.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_scp_link_response.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_message_assembly.py b/unittests/scp_tests/test_scp_message_assembly.py index 47f79321a..57da5f853 100644 --- a/unittests/scp_tests/test_scp_message_assembly.py +++ b/unittests/scp_tests/test_scp_message_assembly.py @@ -1,25 +1,29 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest +from spinnman.config_setup import unittest_setup from spinnman.messages.scp import SCPRequestHeader from spinnman.messages.scp.enums import SCPCommand from spinnman.messages.scp.impl import GetVersion, ReadLink, ReadMemory class TestSCPMessageAssembly(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_create_new_scp_header(self): header = SCPRequestHeader(SCPCommand.CMD_VER) diff --git a/unittests/scp_tests/test_scp_read_memory_request.py b/unittests/scp_tests/test_scp_read_memory_request.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_scp_read_memory_request.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_read_memory_response.py b/unittests/scp_tests/test_scp_read_memory_response.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_scp_read_memory_response.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_router_alloc_request.py b/unittests/scp_tests/test_scp_router_alloc_request.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_scp_router_alloc_request.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_router_alloc_response.py b/unittests/scp_tests/test_scp_router_alloc_response.py deleted file mode 100644 index d632dfede..000000000 --- a/unittests/scp_tests/test_scp_router_alloc_response.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/scp_tests/test_scp_version_request.py b/unittests/scp_tests/test_scp_version_request.py index 6d1ec02f0..5dcc07ff0 100644 --- a/unittests/scp_tests/test_scp_version_request.py +++ b/unittests/scp_tests/test_scp_version_request.py @@ -1,24 +1,28 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest +from spinnman.config_setup import unittest_setup from spinnman.messages.scp.impl import GetVersion from spinnman.messages.scp.enums import SCPCommand class TestSCPVersionRequest(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_new_version_request(self): ver_request = GetVersion(0, 1, 2) self.assertEqual(ver_request.scp_request_header.command, diff --git a/unittests/scp_tests/test_scp_version_response.py b/unittests/scp_tests/test_scp_version_response.py index f218e92f0..4585132e1 100644 --- a/unittests/scp_tests/test_scp_version_response.py +++ b/unittests/scp_tests/test_scp_version_response.py @@ -1,26 +1,30 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest from struct import pack +from spinnman.config_setup import unittest_setup from spinnman.messages.scp.impl.get_version_response import GetVersionResponse from spinnman.messages.scp.enums import SCPResult from spinnman.messages.sdp import SDPFlag class TestSCPVersionResponse(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_new_scp_version_response(self): GetVersionResponse() diff --git a/unittests/sdp_tests/__init__.py b/unittests/sdp_tests/__init__.py index d358f58a8..05bfa339d 100644 --- a/unittests/sdp_tests/__init__.py +++ b/unittests/sdp_tests/__init__.py @@ -1,14 +1,13 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/unittests/sdp_tests/test_sdp_enums.py b/unittests/sdp_tests/test_sdp_enums.py index 82f3231eb..8341f1f7d 100644 --- a/unittests/sdp_tests/test_sdp_enums.py +++ b/unittests/sdp_tests/test_sdp_enums.py @@ -1,23 +1,27 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest +from spinnman.config_setup import unittest_setup from spinnman.messages.sdp.sdp_flag import SDPFlag class TestSDPEnums(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_sdp_flag(self): self.assertEqual(SDPFlag.REPLY_NOT_EXPECTED.value, 0x7) self.assertEqual(SDPFlag.REPLY_EXPECTED.value, 0x87) diff --git a/unittests/test_boot_message_assembly.py b/unittests/test_boot_message_assembly.py index 59822f80f..7997918c8 100644 --- a/unittests/test_boot_message_assembly.py +++ b/unittests/test_boot_message_assembly.py @@ -1,24 +1,28 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest import spinnman.messages.spinnaker_boot.spinnaker_boot_message as boot_msg +from spinnman.config_setup import unittest_setup from spinnman.messages.spinnaker_boot import SpinnakerBootOpCode class TestSpiNNakerBootMessage(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_create_new_boot_message(self): msg = boot_msg.SpinnakerBootMessage(SpinnakerBootOpCode.HELLO, 0, 0, 0) self.assertEqual(msg.data, None) diff --git a/unittests/test_cfg_checker.py b/unittests/test_cfg_checker.py new file mode 100644 index 000000000..859e3b6a9 --- /dev/null +++ b/unittests/test_cfg_checker.py @@ -0,0 +1,30 @@ +# Copyright (c) 2017 The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import unittest +from spinn_utilities.config_holder import run_config_checks +import spinnman +from spinnman.config_setup import unittest_setup + + +class TestCfgChecker(unittest.TestCase): + + def setUp(self): + unittest_setup() + + def test_config_checks(self): + unittests = os.path.dirname(__file__) + spinnman_dir = spinnman.__path__[0] + run_config_checks(directories=[spinnman_dir, unittests]) diff --git a/unittests/test_import_all_test.py b/unittests/test_import_all_test.py index 779daa36a..45574e3df 100644 --- a/unittests/test_import_all_test.py +++ b/unittests/test_import_all_test.py @@ -1,17 +1,16 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import os import unittest @@ -20,6 +19,8 @@ class ImportAllModule(unittest.TestCase): + # no unittest_setup to check all imports work without it + def test_import_all(self): if os.environ.get('CONTINUOUS_INTEGRATION', 'false').lower() == 'true': package_loader.load_module("spinnman", remove_pyc_files=False) diff --git a/unittests/test_multicast_message_assembly.py b/unittests/test_multicast_message_assembly.py index 883f901a3..335f0b053 100644 --- a/unittests/test_multicast_message_assembly.py +++ b/unittests/test_multicast_message_assembly.py @@ -1,23 +1,27 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest +from spinnman.config_setup import unittest_setup import spinnman.messages.multicast_message as multicast_msg class TestMulticastMessage(unittest.TestCase): + + def setUp(self): + unittest_setup() + def test_create_new_multicast_message_without_payload(self): msg = multicast_msg.MulticastMessage(1) self.assertEqual(msg.key, 1) diff --git a/unittests/test_transceiver.py b/unittests/test_transceiver.py index 5c16196fb..6cee5bde8 100644 --- a/unittests/test_transceiver.py +++ b/unittests/test_transceiver.py @@ -1,50 +1,49 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -from __future__ import print_function import unittest import struct +from spinn_utilities.config_holder import set_config from spinn_machine import virtual_machine +from spinnman.config_setup import unittest_setup +from spinnman.data import SpiNNManDataView +from spinnman.data.spinnman_data_writer import SpiNNManDataWriter +from spinnman.extended.extended_transceiver import ExtendedTransceiver from spinnman.transceiver import Transceiver from spinnman import constants from spinnman.messages.spinnaker_boot.system_variable_boot_values import ( SystemVariableDefinition) from spinnman.connections.udp_packet_connections import ( - BootConnection, EIEIOConnection, SCAMPConnection) + BootConnection, SCAMPConnection) import spinnman.transceiver as transceiver -from board_test_configuration import BoardTestConfiguration +import spinnman.extended.extended_transceiver as extended +from spinnman.board_test_configuration import BoardTestConfiguration -board_config = BoardTestConfiguration() ver = 5 # Guess? class MockWriteTransceiver(Transceiver): def __init__( - self, version, connections=None, ignore_chips=None, - ignore_cores=None, ignore_links=None, max_core_id=None, - scamp_connections=None, max_sdram_size=None): - super(MockWriteTransceiver, self).__init__( - version, connections=connections, ignore_chips=ignore_chips, - ignore_cores=ignore_cores, ignore_links=ignore_links, - max_core_id=max_core_id, scamp_connections=scamp_connections, - max_sdram_size=max_sdram_size) + self, version, connections=None): + super().__init__(version, connections=connections) self.written_memory = list() def get_machine_details(self): - return virtual_machine(2, 2) + version = SpiNNManDataView.get_machine_version() + width, height = version.board_shape + return virtual_machine(width, height) def _update_machine(self): self._machine = self.get_machine_details() @@ -56,113 +55,86 @@ def write_memory( self.written_memory.append( (x, y, base_address, data, n_bytes, offset, cpu, is_filename)) + def close(self): + pass + + +class MockExtendedTransceiver(MockWriteTransceiver, ExtendedTransceiver): + pass + class TestTransceiver(unittest.TestCase): + def setUp(self): + unittest_setup() + self.board_config = BoardTestConfiguration() + def test_create_new_transceiver_to_board(self): - board_config.set_up_remote_board() + self.board_config.set_up_remote_board() connections = list() connections.append(SCAMPConnection( - remote_host=board_config.remotehost)) + remote_host=self.board_config.remotehost)) trans = transceiver.Transceiver(ver, connections=connections) trans.close() def test_create_new_transceiver_one_connection(self): - board_config.set_up_remote_board() + self.board_config.set_up_remote_board() connections = set() connections.add(SCAMPConnection( - remote_host=board_config.remotehost)) - trans = transceiver.Transceiver(ver, connections=connections) - - assert trans.get_connections() == connections - trans.close() + remote_host=self.board_config.remotehost)) + with extended.ExtendedTransceiver( + ver, connections=connections) as trans: + assert trans._all_connections == connections def test_create_new_transceiver_from_list_connections(self): - board_config.set_up_remote_board() + self.board_config.set_up_remote_board() connections = list() connections.append(SCAMPConnection( - remote_host=board_config.remotehost)) - board_config.set_up_local_virtual_board() - connections.append(BootConnection( - remote_host=board_config.remotehost)) - trans = transceiver.Transceiver(ver, connections=connections) - instantiated_connections = trans.get_connections() + remote_host=self.board_config.remotehost)) + connections.append(BootConnection(remote_host="127.0.0.1")) + with transceiver.Transceiver(ver, connections=connections) as trans: + instantiated_connections = trans._all_connections - for connection in connections: - assert connection in instantiated_connections - # assert trans.get_connections() == connections - trans.close() + for connection in connections: + assert connection in instantiated_connections + # assert trans.get_connections() == connections def test_retrieving_machine_details(self): - board_config.set_up_remote_board() + self.board_config.set_up_remote_board() connections = list() connections.append(SCAMPConnection( - remote_host=board_config.remotehost)) - board_config.set_up_local_virtual_board() - connections.append(BootConnection( - remote_host=board_config.remotehost)) - trans = transceiver.Transceiver(ver, connections=connections) - - if board_config.board_version == 3 or board_config.board_version == 2: - assert trans.get_machine_dimensions().width == 2 - assert trans.get_machine_dimensions().height == 2 - elif (board_config.board_version == 5 or - board_config.board_version == 4): - assert trans.get_machine_dimensions().width == 8 - assert trans.get_machine_dimensions().height == 8 - else: - size = trans.get_machine_dimensions() - print("Unknown board with x size {0:d} and y size {1:d}".format( - size.width, size.height)) - assert trans.is_connected() - print(trans.get_scamp_version()) - print(trans.get_cpu_information()) - trans.close() + remote_host=self.board_config.remotehost)) + connections.append(BootConnection(remote_host="127.0.0.1")) + with transceiver.Transceiver(ver, connections=connections) as trans: + SpiNNManDataWriter.mock().set_machine(trans.get_machine_details()) + if self.board_config.board_version in (2, 3): + assert trans._get_machine_dimensions().width == 2 + assert trans._get_machine_dimensions().height == 2 + elif self.board_config.board_version in (4, 5): + assert trans._get_machine_dimensions().width == 8 + assert trans._get_machine_dimensions().height == 8 + else: + size = trans._get_machine_dimensions() + print(f"Unknown board with size {size.width} x {size.height}") + + assert any(c.is_connected() for c in trans._scamp_connections) + print(trans._get_scamp_version()) + print(trans.get_cpu_infos()) def test_boot_board(self): - board_config.set_up_remote_board() - trans = transceiver.create_transceiver_from_hostname( - board_config.remotehost, board_config.board_version) - # self.assertFalse(trans.is_connected()) - trans.boot_board() - trans.close() - - def test_listener_creation(self): - # Tests the creation of listening sockets - - # Create board connections - connections = [] - connections.append(SCAMPConnection( - remote_host=None)) - orig_connection = EIEIOConnection() - connections.append(orig_connection) - - # Create transceiver - trnx = Transceiver(version=5, connections=connections) - - # Register a UDP listeners - connection_1 = trnx.register_udp_listener( - callback=None, connection_class=EIEIOConnection) - connection_2 = trnx.register_udp_listener( - callback=None, connection_class=EIEIOConnection) - connection_3 = trnx.register_udp_listener( - callback=None, connection_class=EIEIOConnection, - local_port=orig_connection.local_port) - connection_4 = trnx.register_udp_listener( - callback=None, connection_class=EIEIOConnection, - local_port=orig_connection.local_port + 1) - - assert connection_1 == orig_connection - assert connection_2 == orig_connection - assert connection_3 == orig_connection - assert connection_4 != orig_connection + self.board_config.set_up_remote_board() + with transceiver.create_transceiver_from_hostname( + self.board_config.remotehost, + self.board_config.board_version) as trans: + # self.assertFalse(trans.is_connected( unittest_setup())) + trans._boot_board() def test_set_watch_dog(self): + set_config("Machine", "version", 5) connections = [] - connections.append(SCAMPConnection( - remote_host=None)) - tx = MockWriteTransceiver(version=5, connections=connections) - + connections.append(SCAMPConnection(remote_host=None)) + tx = MockExtendedTransceiver(version=5, connections=connections) + SpiNNManDataWriter.mock().set_machine(tx.get_machine_details()) # All chips tx.set_watch_dog(True) tx.set_watch_dog(False) diff --git a/unittests/test_version.py b/unittests/test_version.py index 81f84c73f..c8f42fc87 100644 --- a/unittests/test_version.py +++ b/unittests/test_version.py @@ -1,63 +1,63 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2017 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import unittest import spinn_utilities import spinn_machine -import spinn_storage_handlers from spinnman.transceiver import Transceiver, _SCAMP_VERSION import spinnman +from spinnman.config_setup import unittest_setup class Test(unittest.TestCase): """ Tests for the SCAMP version comparison """ + def setUp(self): + unittest_setup() + def test_version_same(self): - self.assertTrue(Transceiver.is_scamp_version_compabible(( + self.assertTrue(Transceiver._is_scamp_version_compabible(( _SCAMP_VERSION[0], _SCAMP_VERSION[1], _SCAMP_VERSION[2]))) def test_major_version_too_big(self): - self.assertFalse(Transceiver.is_scamp_version_compabible(( + self.assertFalse(Transceiver._is_scamp_version_compabible(( _SCAMP_VERSION[0] + 1, 0, 0))) def test_major_version_too_small(self): - self.assertFalse(Transceiver.is_scamp_version_compabible(( + self.assertFalse(Transceiver._is_scamp_version_compabible(( _SCAMP_VERSION[0] - 1, 0, 0))) def test_minor_version_bigger(self): - self.assertTrue(Transceiver.is_scamp_version_compabible(( + self.assertTrue(Transceiver._is_scamp_version_compabible(( _SCAMP_VERSION[0], _SCAMP_VERSION[1] + 1, _SCAMP_VERSION[2]))) def test_minor_version_smaller(self): - self.assertFalse(Transceiver.is_scamp_version_compabible(( + self.assertFalse(Transceiver._is_scamp_version_compabible(( _SCAMP_VERSION[0], _SCAMP_VERSION[1] - 1, _SCAMP_VERSION[2]))) def test_patch_version_bigger(self): - self.assertTrue(Transceiver.is_scamp_version_compabible(( + self.assertTrue(Transceiver._is_scamp_version_compabible(( _SCAMP_VERSION[0], _SCAMP_VERSION[1], _SCAMP_VERSION[2] + 1))) def test_patch_version_smaller(self): - self.assertFalse(Transceiver.is_scamp_version_compabible(( + self.assertFalse(Transceiver._is_scamp_version_compabible(( _SCAMP_VERSION[0], _SCAMP_VERSION[1], _SCAMP_VERSION[2] - 1))) def test_compare_versions(self): spinn_utilities_parts = spinn_utilities.__version__.split('.') spinn_machine_parts = spinn_machine.__version__.split('.') - spinn_storage_handlers_parts = spinn_storage_handlers.__version__.\ - split('.') spinnman_parts = spinnman.__version__.split('.') self.assertEqual(spinn_utilities_parts[0], spinnman_parts[0]) @@ -65,7 +65,3 @@ def test_compare_versions(self): self.assertEqual(spinn_machine_parts[0], spinnman_parts[0]) self.assertLessEqual(spinn_machine_parts[1], spinnman_parts[1]) - - self.assertEqual(spinn_storage_handlers_parts[0], spinnman_parts[0]) - self.assertLessEqual(spinn_storage_handlers_parts[1], - spinnman_parts[1]) diff --git a/unittests/threads_tests/__init__.py b/unittests/threads_tests/__init__.py index d358f58a8..05bfa339d 100644 --- a/unittests/threads_tests/__init__.py +++ b/unittests/threads_tests/__init__.py @@ -1,14 +1,13 @@ -# Copyright (c) 2017-2019 The University of Manchester +# Copyright (c) 2014 The University of Manchester # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/unittests/threads_tests/scp_message_thread.py b/unittests/threads_tests/scp_message_thread.py deleted file mode 100644 index 9ba35201d..000000000 --- a/unittests/threads_tests/scp_message_thread.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - @unittest.skip("testing skipping") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/threads_tests/test_get_iptags_thread.py b/unittests/threads_tests/test_get_iptags_thread.py deleted file mode 100644 index 63dece38f..000000000 --- a/unittests/threads_tests/test_get_iptags_thread.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main() diff --git a/unittests/threads_tests/test_iobuf_thread.py b/unittests/threads_tests/test_iobuf_thread.py deleted file mode 100644 index 63dece38f..000000000 --- a/unittests/threads_tests/test_iobuf_thread.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2017-2019 The University of Manchester -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import unittest - - -class MyTestCase(unittest.TestCase): - @unittest.skip("Test not implemented yet") - def test_something(self): - self.assertEqual(True, False, "Test not implemented yet") - - -if __name__ == '__main__': - unittest.main()