docs: def sdram_alloc(self, size, tag=0, x=Required, y=Required,
                      app_id=Required, clear=False):
          """Allocate a region of SDRAM for an application.
  
          Requests SARK to allocate a block of SDRAM for an application and
          raises a :py:exc:`.SpiNNakerMemoryError` on failure. This allocation
          will be freed when the application is stopped.
  
          Parameters
          ----------
          size : int
              Number of bytes to attempt to allocate in SDRAM.
          tag : int
              8-bit tag that can be looked up by a SpiNNaker application to
              discover the address of the allocated block. The tag must be unique
              for this ``app_id`` on this chip. Attempting to allocate two blocks
              on the same chip and for the same ``app_id`` will fail. If ``0``
              (the default) then no tag is applied.
  
              For example, if some SDRAM is allocated with ``tag=12``, a
              SpiNNaker application can later discover the address using::
  
                  void *allocated_data = sark_tag_ptr(12, 0);
  
              A common convention is to allocate one block of SDRAM per
              application core and give each allocation the associated core
              number as its tag. This way the underlying SpiNNaker applications
              can simply call::
  
                  void *allocated_data = sark_tag_ptr(sark_core_id(), 0);
  
          clear : bool
              If True the requested memory will be filled with zeros before the
              pointer is returned.  If False (the default) the memory will be
              left as-is.
  
          Returns
          -------
          int
              Address of the start of the region.
  
              The allocated SDRAM remains valid until either the 'stop' signal is
              sent to the application ID associated with the allocation or
              :py:meth:`.sdram_free` is called on the address returned.
  
          Raises
          ------
          rig.machine_control.machine_controller.SpiNNakerMemoryError
              If the memory cannot be allocated, the tag is already taken or it
              is invalid.
          """
          assert 0 <= tag < 256
  
          # Construct arg1 (app_id << 8) | op code
          arg1 = app_id << 8 | consts.AllocOperations.alloc_sdram
  
          # Send the packet and retrieve the address
          rv = self._send_scp(x, y, 0, SCPCommands.alloc_free, arg1, size, tag)
          if rv.arg1 == 0:  # Allocation failed
              tag_in_use = False
              if tag != 0:
                  # If a tag was specified then read the allocation table to see
                  # if the tag was already in use or whether we ran out of
                  # memory.
                  alloc_tags = self.read_struct_field("sv", "alloc_tag", x, y)
                  index = (app_id << 8) + tag
                  entry = self.read(alloc_tags + index, 4, x, y)
                  tag_in_use = (entry != 0)
  
              raise SpiNNakerMemoryError(size, x, y, tag, tag_in_use)
  
          # Get the address
          address = rv.arg1
  
          if clear:
              # Clear the memory if so desired
              self.fill(address, 0, size, x, y, 0)
  
          return address