Skip to content

Commit

Permalink
Merge pull request #250 from SpiNNakerManchester/single_routing_entry
Browse files Browse the repository at this point in the history
Single routing entry
  • Loading branch information
rowleya authored Apr 16, 2024
2 parents 2c5bea7 + aacd526 commit 00dfae5
Show file tree
Hide file tree
Showing 9 changed files with 473 additions and 364 deletions.
7 changes: 4 additions & 3 deletions spinn_machine/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,12 @@
from .machine import Machine
from .multicast_routing_entry import MulticastRoutingEntry
from .router import Router
from .routing_entry import RoutingEntry
from .spinnaker_triad_geometry import SpiNNakerTriadGeometry
from .virtual_machine import virtual_machine
from .fixed_route_entry import FixedRouteEntry


__all__ = ["Chip", "CoreSubset", "CoreSubsets", "FixedRouteEntry",
__all__ = ["Chip", "CoreSubset", "CoreSubsets",
"FrozenCoreSubsets", "Link", "Machine", "MulticastRoutingEntry",
"Router", "SpiNNakerTriadGeometry", "virtual_machine"]
"Router", "RoutingEntry", "SpiNNakerTriadGeometry",
"virtual_machine"]
75 changes: 0 additions & 75 deletions spinn_machine/fixed_route_entry.py

This file was deleted.

184 changes: 31 additions & 153 deletions spinn_machine/multicast_routing_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,102 +12,47 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
from typing import Any, FrozenSet, Iterable, Optional, Tuple, overload
from spinn_machine.router import Router
from typing import Any, FrozenSet
from .exceptions import SpinnMachineInvalidParameterException
from .routing_entry import RoutingEntry


class MulticastRoutingEntry(object):
"""
Represents an entry in a SpiNNaker chip's multicast routing table.
There can be up to 1024 such entries per chip, but some may be reserved
for system purposes.
Represents an entry in a SpiNNaker chip's routing table,
including the key and mask
"""

__slots__ = (
"_routing_entry_key", "_mask", "_defaultable", "_processor_ids",
"_link_ids", "_spinnaker_route", "__repr")
"_key", "_mask", "_routing_entry")

@overload
def __init__(self, routing_entry_key: int, mask: int, *,
processor_ids: Iterable[int],
link_ids: Iterable[int],
defaultable: bool = False,
spinnaker_route: None = None):
...

@overload
def __init__(self, routing_entry_key: int, mask: int, *,
processor_ids: None = None,
link_ids: None = None,
defaultable: bool = False,
spinnaker_route: int):
...

# pylint: disable=too-many-arguments
def __init__(self, routing_entry_key: int, mask: int, *,
processor_ids: Optional[Iterable[int]] = None,
link_ids: Optional[Iterable[int]] = None,
defaultable: bool = False,
spinnaker_route: Optional[int] = None) -> None:
def __init__(self, key: int, mask: int,
routing_entry: RoutingEntry):
"""
.. note::
The processor_ids and link_ids parameters are only optional if a
spinnaker_route is provided. If a spinnaker_route is provided
the processor_ids and link_ids parameters are ignored.
:param int routing_entry_key: The routing key_combo
:param int key: The routing key_combo
:param int mask: The route key_combo mask
:param processor_ids: The destination processor IDs
:type processor_ids: iterable(int) or None
:param link_ids: The destination link IDs
:type link_ids: iterable(int) or None
:param bool defaultable:
If this entry is defaultable (it receives packets
from its directly opposite route position)
:param spinnaker_route:
The processor_ids and link_ids expressed as a single int.
:type spinnaker_route: int or None
:raise spinn_machine.exceptions.SpinnMachineAlreadyExistsException:
* If processor_ids contains the same ID more than once
* If link_ids contains the same ID more than once
:raise TypeError: if no spinnaker_route provided and either
processor_ids or link_ids is missing or `None`
:param RoutingEntry: routing_entry
"""
self._routing_entry_key: int = routing_entry_key
self._key: int = key
self._mask: int = mask
self._defaultable: bool = defaultable
self.__repr: Optional[str] = None
self._routing_entry = routing_entry

if (routing_entry_key & mask) != routing_entry_key:
if (key & mask) != key:
raise SpinnMachineInvalidParameterException(
"key_mask_combo and mask",
f"{routing_entry_key} and {mask}",
f"{key} and {mask}",
"The key combo is changed when masked with the mask. This"
" is determined to be an error in the tool chain. Please "
"correct this and try again.")

# Add processor IDs, ignore duplicates
if spinnaker_route is None:
assert processor_ids is not None
assert link_ids is not None
self._processor_ids: Optional[FrozenSet[int]] = \
frozenset(processor_ids)
self._link_ids: Optional[FrozenSet[int]] = frozenset(link_ids)
self._spinnaker_route: int = self._calc_spinnaker_route()
else:
self._spinnaker_route = spinnaker_route
self._processor_ids = None
self._link_ids = None

@property
def routing_entry_key(self) -> int:
def key(self) -> int:
"""
The routing key.
:rtype: int
"""
return self._routing_entry_key
return self._key

@property
def mask(self) -> int:
Expand All @@ -125,9 +70,7 @@ def processor_ids(self) -> FrozenSet[int]:
:rtype: frozenset(int)
"""
if self._processor_ids is None:
self._processor_ids, self._link_ids = self._calc_routing_ids()
return self._processor_ids
return self._routing_entry.processor_ids

@property
def link_ids(self) -> FrozenSet[int]:
Expand All @@ -136,9 +79,7 @@ def link_ids(self) -> FrozenSet[int]:
:rtype: frozenset(int)
"""
if self._link_ids is None:
self._processor_ids, self._link_ids = self._calc_routing_ids()
return self._link_ids
return self._routing_entry.link_ids

@property
def defaultable(self) -> bool:
Expand All @@ -151,7 +92,7 @@ def defaultable(self) -> bool:
:rtype: bool
"""
return self._defaultable
return self._routing_entry.defaultable

@property
def spinnaker_route(self) -> int:
Expand All @@ -160,7 +101,7 @@ def spinnaker_route(self) -> int:
:rtype: int
"""
return self._spinnaker_route
return self._routing_entry.spinnaker_route

def merge(self, other: MulticastRoutingEntry) -> MulticastRoutingEntry:
"""
Expand All @@ -178,102 +119,39 @@ def merge(self, other: MulticastRoutingEntry) -> MulticastRoutingEntry:
:raise spinn_machine.exceptions.SpinnMachineInvalidParameterException:
If the key and mask of the other entry do not match
"""
if other.routing_entry_key != self.routing_entry_key:
if other.key != self.key:
raise SpinnMachineInvalidParameterException(
"other.key", hex(other.routing_entry_key),
f"The key does not match 0x{self.routing_entry_key:x}")
"other.key", hex(other.key),
f"The key does not match 0x{self.key:x}")
if other.mask != self.mask:
raise SpinnMachineInvalidParameterException(
"other.mask", hex(other.mask),
f"The mask does not match 0x{self.mask:x}")

# pylint: disable=protected-access
routing_entry = self._routing_entry.merge(other._routing_entry)
return MulticastRoutingEntry(
self.routing_entry_key, self.mask,
processor_ids=(self.processor_ids | other.processor_ids),
link_ids=(self.link_ids | other.link_ids),
defaultable=(self._defaultable and other.defaultable))

def __add__(self, other: MulticastRoutingEntry) -> MulticastRoutingEntry:
"""
Allows overloading of `+` to merge two entries together.
See :py:meth:`merge`
"""
return self.merge(other)

def __or__(self, other: MulticastRoutingEntry) -> MulticastRoutingEntry:
"""
Allows overloading of `|` to merge two entries together.
See :py:meth:`merge`
"""
return self.merge(other)
self.key, self.mask, routing_entry)

def __eq__(self, other_entry: Any) -> bool:
if not isinstance(other_entry, MulticastRoutingEntry):
return False
if self.routing_entry_key != other_entry.routing_entry_key:
if self.key != other_entry.key:
return False
if self.mask != other_entry.mask:
return False
if self._spinnaker_route != other_entry._spinnaker_route:
return False
return (self._defaultable == other_entry.defaultable)
return (self._routing_entry == other_entry._routing_entry)

def __hash__(self) -> int:
return (self.routing_entry_key * 13 + self.mask * 19 +
self._spinnaker_route * 29 * int(self._defaultable) * 131)
return (self.key * 13 + self.mask * 19 +
hash(self._routing_entry))

def __ne__(self, other: Any) -> bool:
return not self.__eq__(other)

def __repr__(self) -> str:
if not self.__repr:
self.__repr = (
f"{self._routing_entry_key}:{self._mask}:{self._defaultable}"
f":{{{', '.join(map(str, sorted(self.processor_ids)))}}}:"
f"{{{', '.join(map(str, sorted(self.link_ids)))}}}")

return self.__repr
return (f"{self._key}:{self._mask}:"
f"{repr(self._routing_entry)}")

def __str__(self) -> str:
return self.__repr__()

def __getstate__(self):
return dict(
(slot, getattr(self, slot))
for slot in self.__slots__
if hasattr(self, slot)
)

def __setstate__(self, state):
for slot, value in state.items():
setattr(self, slot, value)

def _calc_spinnaker_route(self) -> int:
"""
Convert a routing table entry represented in software to a
binary routing table entry usable on the machine.
:rtype: int
"""
route_entry = 0
assert self._processor_ids is not None
for processor_id in self._processor_ids:
route_entry |= (1 << (Router.MAX_LINKS_PER_ROUTER + processor_id))
assert self._link_ids is not None
for link_id in self._link_ids:
route_entry |= (1 << link_id)
return route_entry

def _calc_routing_ids(self) -> Tuple[FrozenSet[int], FrozenSet[int]]:
"""
Convert a binary routing table entry usable on the machine to lists of
route IDs usable in a routing table entry represented in software.
:rtype: tuple(frozenset(int), frozenset(int))
"""
processor_ids = (pi for pi in range(0, Router.MAX_CORES_PER_ROUTER)
if self._spinnaker_route & 1 <<
(Router.MAX_LINKS_PER_ROUTER + pi))
link_ids = (li for li in range(0, Router.MAX_LINKS_PER_ROUTER)
if self._spinnaker_route & 1 << li)
return frozenset(processor_ids), frozenset(link_ids)
6 changes: 3 additions & 3 deletions spinn_machine/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
SpinnMachineAlreadyExistsException, SpinnMachineInvalidParameterException)
if TYPE_CHECKING:
from .link import Link
from .fixed_route_entry import FixedRouteEntry
from .multicast_routing_entry import MulticastRoutingEntry
from .routing_entry import RoutingEntry


class Router(object):
Expand Down Expand Up @@ -146,15 +146,15 @@ def n_available_multicast_entries(self) -> int:
@staticmethod
def convert_routing_table_entry_to_spinnaker_route(
routing_table_entry: Union[
MulticastRoutingEntry, FixedRouteEntry]) -> int:
MulticastRoutingEntry, RoutingEntry]) -> int:
"""
Convert a routing table entry represented in software to a
binary routing table entry usable on the machine.
:param routing_table_entry:
The entry to convert
:type routing_table_entry: ~spinn_machine.MulticastRoutingEntry or
~spinn_machine.FixedRouteEntry
~spinn_machine.RoutingEntry
:rtype: int
"""
route_entry = 0
Expand Down
Loading

0 comments on commit 00dfae5

Please sign in to comment.