code: def memory_read(self, addr, num_units, zone=None, nbits=None):
          
          buf_size = num_units
          buf = None
          access = 0
  
          if nbits is None:
              buf = (ctypes.c_uint8 * buf_size)()
              access = 0
          elif nbits == 8:
              buf = (ctypes.c_uint8 * buf_size)()
              access = 1
          elif nbits == 16:
              buf = (ctypes.c_uint16 * buf_size)()
              access = 2
              buf_size = buf_size * access
          elif nbits == 32:
              buf = (ctypes.c_uint32 * buf_size)()
              access = 4
              buf_size = buf_size * access
          else:
              raise ValueError('Given bit size is invalid: %s' % nbits)
  
          args = [addr, buf_size, buf, access]
  
          method = self._dll.JLINKARM_ReadMemEx
          if zone is not None:
              method = self._dll.JLINKARM_ReadMemZonedEx
              args.append(zone.encode())
  
          units_read = method(*args)
          if units_read < 0:
              raise errors.JLinkReadException(units_read)
  
          return buf[:units_read]
docs: Reads memory from a target system or specific memory zone.
  
          The optional ``zone`` specifies a memory zone to access to read from,
          e.g. ``IDATA``, ``DDATA``, or ``CODE``.
  
          The given number of bits, if provided, must be either ``8``, ``16``, or
          ``32``.  If not provided, always reads ``num_units`` bytes.
  
          Args:
            self (JLink): the ``JLink`` instance
            addr (int): start address to read from
            num_units (int): number of units to read
            zone (str): optional memory zone name to access
            nbits (int): number of bits to use for each unit
  
          Returns:
            List of units read from the target system.
  
          Raises:
            JLinkException: if memory could not be read.
            ValueError: if ``nbits`` is not ``None``, and not in ``8``, ``16``,
              or ``32``.