[Wait4#9][N91/NPUdrvApi_c] Implementation for NPU API interface
authorParichay Kapoor <pk.kapoor@samsung.com>
Fri, 28 Jun 2019 05:55:05 +0000 (14:55 +0900)
committer함명주/On-Device Lab(SR)/Principal Engineer/삼성전자 <myungjoo.ham@samsung.com>
Tue, 9 Jul 2019 08:05:19 +0000 (17:05 +0900)
Added implementation for NPU API interface
Updated meson build
TODO: implement NYI items

v1:
Added locking along with minor fixes

v2:
Updated as per blocking I/O

Signed-off-by: Parichay Kapoor <pk.kapoor@samsung.com>
core/libnpu-core/meson.build
core/libnpu-core/src/NPUdrvAPI.c [new file with mode: 0644]

index 6607342..d1b205d 100644 (file)
@@ -1 +1,12 @@
-# DO NOTHING, YET
+inc = [
+  'include',
+]
+
+lib_sources = [
+  'src/NPUdrvAPI.c',
+]
+
+libnpu_build = static_library('npu-core',
+    lib_sources,
+    include_directories : inc,
+    install : false)
diff --git a/core/libnpu-core/src/NPUdrvAPI.c b/core/libnpu-core/src/NPUdrvAPI.c
new file mode 100644 (file)
index 0000000..d52cd81
--- /dev/null
@@ -0,0 +1,393 @@
+/**
+ * Proprietary
+ * Copyright (C) 2019 Samsung Electronics
+ * Copyright (C) 2019 Parichay Kapoor <pk.kapoor@samsung.com>
+ * Copyright (C) 2019 MyungJoo Ham <myungjoo.ham@samsung.com>
+ */
+/**
+ * @file NPUdrvAPI.c
+ * @date 26 June 2019
+ * @brief Implementation of API to access NPU
+ * @see http://suprem.sec.samsung.net/confluence/display/ODLC/Software+Stack
+ * @author Parichay Kapoor <pk.kapoor@samsung.com>
+ * @bug No known bugs except for NYI items
+ *
+ * To packagers: this is used by NPU Engine (NE).
+ */
+
+#include <stdio.h>
+#include <signal.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+
+#include <NPUdrvAPI.h>
+
+#define DEV_BASE_PATH "/dev"
+#define DEV_NAME "srnpu"
+#define MAX_NUM_DEVICE 1
+
+
+/** private object corresponding to each npu opened */
+typedef struct _npu_dev_priv
+{
+  int internal_fd;        /** the actual fd for the opened device */
+  npu_input_opmode mode;  /** operating for the npu */
+
+  /** npu copnfigration variables */
+  uint64_t program_offset_addr;
+  uint64_t program_size;
+  uint64_t weight_offset_addr;
+  uint64_t activation_offset_addr0;
+  uint64_t activation_offset_addr1;
+
+  /** Base physical address for the memory */
+  int dmabuf_fd;
+  int offset0;
+  int offset1;
+} npu_dev_priv;
+
+
+/** npu api private struct */
+typedef struct
+{
+  npu_dev_priv * npu_fd_table[MAX_NUM_DEVICE];  /** struct of opened devices */
+  pthread_mutex_t mutex;    /** mutex for multiple devices */
+} npu_api_priv;
+
+
+/**
+ * global values maintain all the opened npus
+ * currently only 1 NPU is supported
+ * extened npu_fd_table to a table to support multiple devices
+ */
+static npu_api_priv napi_priv;
+
+/** constructor/destructor for npu api */
+void init_npu_api(void) __attribute__ ((constructor));
+void fini_npu_api(void) __attribute__ ((destructor));
+
+/**
+ * @brief find the object for the given fd
+ * @param[in] fd file descriptor for the device
+ * @return npu_dev_priv object on success, NULL on error
+ */
+static npu_dev_priv* fd_to_obj (int fd)
+{
+  return napi_priv.npu_fd_table[fd];
+}
+
+
+/**
+ * @brief add an entry to the fd table
+ * @param[in] fd abstracted file descriptor for the device
+ * @param[in] internal_fd actual non-abstracted file descriptor for the device
+ *
+ * @note actual file descriptor is referred as internal_fd, and abstracted
+ *       fd is referred directly as fd
+ */
+static int fd_table_add_new_entry (int fd, int internal_fd)
+{
+  if (fd >= MAX_NUM_DEVICE) {
+    return -ENOMEM;
+  }
+
+  pthread_mutex_lock(&napi_priv.mutex);
+  /** cannot open the same device twice */
+  if (napi_priv.npu_fd_table[fd] != NULL) {
+    pthread_mutex_unlock(&napi_priv.mutex);
+    return -EBUSY;
+  }
+
+  napi_priv.npu_fd_table[fd] = (npu_dev_priv *) malloc (sizeof (npu_dev_priv));
+  napi_priv.npu_fd_table[fd]->internal_fd = internal_fd;
+
+  pthread_mutex_unlock(&napi_priv.mutex);
+  return 0;
+}
+
+/**
+ * @brief delete an entry from the fd table
+ * @param[in] fd file descriptor for the device
+ * return 0 on success, errno on failure
+ */
+static int fd_table_delete_entry (int fd)
+{
+  if (fd >= MAX_NUM_DEVICE) {
+    return -EMFILE;
+  }
+
+  pthread_mutex_lock(&napi_priv.mutex);
+  if (napi_priv.npu_fd_table[fd] == NULL) {
+    pthread_mutex_unlock(&napi_priv.mutex);
+    return -EBADF;
+  }
+
+  free (napi_priv.npu_fd_table[fd]);
+  napi_priv.npu_fd_table[fd] = NULL;
+
+  pthread_mutex_unlock(&napi_priv.mutex);
+
+  return 0;
+}
+
+/**
+ * @brief verify that addresses are 32bits
+ * @param[in] value address/offset value provided
+ * @return 0 on success, errno on failure
+ */
+static int verify_size32 (uint64_t value)
+{
+  if (value >> 32 == 0x0) {
+    return 0;
+  } else {
+    return -ERANGE;
+  }
+}
+
+/**
+ * @brief open the npu device
+ * @param[in] dev_id index of the device to be opened
+ * @return file descriptor is not error. otherwise errno
+ */
+int npu_open (unsigned int dev_id)
+{
+  int fd;
+  char name[64];
+  int status;
+  errno = 0;
+
+  snprintf(name, sizeof(name), "%s/%s%u", DEV_BASE_PATH, DEV_NAME, dev_id);
+
+  fd = open(name, O_RDWR);
+  if (fd < 0) {
+    return -errno;
+  }
+
+  status = fd_table_add_new_entry (dev_id, fd);
+  if (status < 0) {
+    close (fd);
+    return status;
+  }
+
+  return dev_id;
+}
+
+
+/**
+ * @brief close the npu device
+ * @param[in] fd file descriptor of the device
+ * @returns 0 on success, errno on error
+ */
+int npu_close (int fd)
+{
+  npu_dev_priv * npu_dev_priv_t;
+  int status;
+
+  npu_dev_priv_t = fd_to_obj (fd);
+  if (npu_dev_priv_t == NULL)
+    return -ENXIO;
+
+  close (npu_dev_priv_t->internal_fd);
+  status = fd_table_delete_entry (fd);
+  if (status < 0) {
+    return status;
+  }
+
+  return 0;
+}
+
+
+/**
+ * @brief set mode for the NPU to operate in
+ * @param[in] fd file descriptor of the device
+ * @param[in] mode mode of operation for the NPU
+ */
+void npu_set_mode (int fd, npu_input_opmode mode)
+{
+  npu_dev_priv * npu_dev_priv_t;
+
+  npu_dev_priv_t = fd_to_obj (fd);
+  npu_dev_priv_t->mode = mode;
+}
+
+
+/**
+ * @brief run the npu based on the set configuration
+ * @param[in] fd file descriptor of the device
+ * @return 0 on success, errno on error
+ * @note this function will block the calling thread till output is complete
+ */
+int npu_setup_device (int fd)
+{
+  return 0;
+}
+
+
+/**
+ * @brief get the status of the device
+ * @param[in] fd file descriptor of the device
+ * @return NULL to error, else register values filled in struct
+ */
+struct srnpu_status_arg * npu_get_status (int fd)
+{
+  return NULL;
+}
+
+
+/**
+ * @brief set the program instruction offset dram address and size
+ * @param[in] fd file descriptor of the device
+ * @param[in] program_offset_addr offset address for the instructions (NPU_PROG_BASE)
+ * @param[in] program_size size of the program instructions (NPU_PROG_SIZE)
+ * @return 0 on success, errno on error
+ */
+int npu_set_program (int fd, uint64_t program_offset_addr,
+    uint64_t program_size)
+{
+  npu_dev_priv * npu_dev_priv_t;
+
+  npu_dev_priv_t = fd_to_obj (fd);
+  if (npu_dev_priv_t == NULL)
+    return -ENXIO;
+
+  if (verify_size32(program_offset_addr) != 0 ||
+      verify_size32(program_size) != 0) {
+    return -ERANGE;
+  }
+
+  npu_dev_priv_t->program_offset_addr = program_offset_addr;
+  npu_dev_priv_t->program_size = program_size;
+  return 0;
+}
+
+
+/**
+ * @brief set the offset dram address for the model parameters
+ * @param[in] fd file descriptor of the device
+ * @param[in] weight_offset_addr offset address for storing weights (NPU_WGT_BASE)
+ * @return 0 on success, errno on error
+ */
+int npu_set_weight (int fd, uint64_t weight_offset_addr)
+{
+  npu_dev_priv * npu_dev_priv_t;
+
+  npu_dev_priv_t = fd_to_obj (fd);
+  if (npu_dev_priv_t == NULL)
+    return -ENXIO;
+
+  if (verify_size32(weight_offset_addr) != 0) {
+    return -ERANGE;
+  }
+
+  npu_dev_priv_t->weight_offset_addr = weight_offset_addr;
+  return 0;
+}
+
+
+/**
+ * @brief set the offset dram address for the activation/input
+ * @param[in] fd file descriptor of the device
+ * @param[in] activation_offset_addr0 offset address for storing weights (NPU_ACT_BASE0)
+ * @param[in] activation_offset_addr1 offset address for storing weights (NPU_ACT_BASE1)
+ * @return 0 on success, -1 on error
+ *
+ * @note The addr value act as offset from the base physical address
+ */
+int npu_set_activation (int fd, uint64_t activation_offset_addr0,
+               uint64_t activation_offset_addr1)
+{
+  npu_dev_priv * npu_dev_priv_t;
+
+  npu_dev_priv_t = fd_to_obj (fd);
+  if (npu_dev_priv_t == NULL)
+    return -ENXIO;
+
+  if (verify_size32(activation_offset_addr0) != 0 ||
+      verify_size32(activation_offset_addr1) != 0) {
+    return -ERANGE;
+  }
+
+  npu_dev_priv_t->activation_offset_addr0 = activation_offset_addr0;
+  npu_dev_priv_t->activation_offset_addr1 = activation_offset_addr1;
+  return 0;
+}
+
+
+/**
+ * @brief set the base dram address
+ * @param[in] fd file descriptor of the device
+ * @param[in] dmabuf_fd to extract the base physical address
+ * @param[in] offset1 to get start of first memory region
+ * @param[in] offset2 to get start of second memory region
+ * @return 0 on success, errno on error
+ */
+int npu_set_buffer (int fd, int dmabuf_fd, uint64_t offset0, uint64_t offset1)
+{
+  npu_dev_priv * npu_dev_priv_t;
+  struct srnpu_buffer_arg npu_buffer_t;
+  int ret;
+
+  npu_dev_priv_t = fd_to_obj (fd);
+  if (npu_dev_priv_t == NULL)
+    return -ENXIO;
+
+  if (verify_size32(offset0) != 0 || verify_size32(offset1) != 0) {
+    return -ERANGE;
+  }
+
+  /** for now, these two will be same */
+  npu_dev_priv_t->dmabuf_fd = dmabuf_fd;
+  npu_dev_priv_t->offset0 = offset0;
+  npu_dev_priv_t->offset1 = offset1;
+
+  /** TODO: send the dmabuf fd */
+
+  return 0;
+}
+
+
+/**
+ * @brief check if the device to get ready for the next computation
+ * @param[in] fd file descriptor of the device
+ * @return 0 if ready, errno if not ready or error
+ */
+int npu_check_compute_ready (int fd)
+{
+  return 0;
+}
+
+
+/**
+ * @brief check if the device to get ready for the next computation
+ * @param[in] fd file descriptor of the device
+ * @return 0 if ready, errno on error
+ * @note this function will block till npu is ready
+ */
+int npu_wait_compute_ready (int fd)
+{
+  return 0;
+}
+
+void init_npu_api(void)
+{
+  int i;
+
+  pthread_mutex_init(&napi_priv.mutex, NULL);
+  for (i=0; i<MAX_NUM_DEVICE; i++) {
+    napi_priv.npu_fd_table[i] = NULL;
+  }
+
+}
+
+void fini_npu_api(void)
+{
+  pthread_mutex_destroy(&napi_priv.mutex);
+}