docs: def read(self, n_bytes=-1):
          """Read a number of bytes from the memory.
  
          .. note::
              Reads beyond the specified memory range will be truncated.
  
          .. note::
              Produces a :py:exc:`.TruncationWarning` if fewer bytes are read
              than requested. These warnings can be converted into exceptions
              using :py:func:`warnings.simplefilter`::
  
                  >>> import warnings
                  >>> from rig.machine_control.machine_controller \\
                  ...     import TruncationWarning
                  >>> warnings.simplefilter('error', TruncationWarning)
  
          Parameters
          ----------
          n_bytes : int
              A number of bytes to read.  If the number of bytes is negative or
              omitted then read all data until the end of memory region.
  
          Returns
          -------
          :py:class:`bytes`
              Data read from SpiNNaker as a bytestring.
          """
          # If n_bytes is negative then calculate it as the number of bytes left
          if n_bytes < 0:
              n_bytes = self._end_address - self.address
  
          # Determine how far to read, then read nothing beyond that point.
          if self.address + n_bytes > self._end_address:
              new_n_bytes = self._end_address - self.address
              warnings.warn("read truncated from {} to {} bytes".format(
                  n_bytes, new_n_bytes), TruncationWarning, stacklevel=3)
              n_bytes = new_n_bytes
  
          if n_bytes <= 0:
              return b''
  
          # Perform the read and increment the offset
          data = self._parent._perform_read(self.address, n_bytes)
          self._offset += n_bytes
          return data