MLBEDSW-3796 Ethos-U driver interface multiple NPUs

---ethosu_driver---
Modified: Declarations for the driver interfaces to support multiNPU (takes *drv)
Added: ethosu_register_driver(...) to allow for a specific NPU driver to be instantiated
Added: ethosu_deregister_Driver(...) to allow for a specific NPU driver to be de-registered
Added: ethosu_reserve_driver(...) to reserve & return the first NPU driver instance available
Added: ethosu_release_driver(...) to release a specific NPU driver instance and make it available again
Added: *registered_drivers - A  static linked list of drivers ready be used

---ethosu_pmu---
Modified: Declarations for pmu interfaces to support multiNPU (takes *drv)

---ethosu_device---
Modified: Resolved a circular include dependency (Remove include and add ETHOSU_PMU_NCOUNTERS macro)

Change-Id: Iede41cd41bb0d5d483bd9d929d1b6c9ca5d3c48e
diff --git a/include/ethosu_device.h b/include/ethosu_device.h
index eff9054..d9c233d 100644
--- a/include/ethosu_device.h
+++ b/include/ethosu_device.h
@@ -23,8 +23,6 @@
  * Includes
  ******************************************************************************/
 
-#include "pmu_ethosu.h"
-
 #include <stdbool.h>
 #include <stdint.h>
 
@@ -41,6 +39,10 @@
 #define ETHOSU_DRIVER_VERSION_PATCH 0  ///< Driver patch version
 #define ETHOSU_DRIVER_BASEP_INDEXES 8  ///< Number of base pointer indexes
 
+#ifndef ETHOSU_PMU_NCOUNTERS
+#define ETHOSU_PMU_NCOUNTERS 4
+#endif
+
 /******************************************************************************
  * Types
  ******************************************************************************/
diff --git a/include/ethosu_driver.h b/include/ethosu_driver.h
index d47b676..70e3110 100644
--- a/include/ethosu_driver.h
+++ b/include/ethosu_driver.h
@@ -45,6 +45,8 @@
     size_t fast_memory_size;
     bool status_error;
     bool dev_power_always_on;
+    struct ethosu_driver *next;
+    bool reserved;
 };
 
 struct ethosu_version_id
@@ -90,7 +92,8 @@
 /**
  * Initialize the Ethos-U driver.
  */
-int ethosu_init_v3(const void *base_address,
+int ethosu_init_v4(struct ethosu_driver *drv,
+                   const void *base_address,
                    const void *fast_memory,
                    const size_t fast_memory_size,
                    uint32_t secure_enable,
@@ -99,16 +102,21 @@
 #define ethosu_init(base_address) ethosu_init_v3(base_address, NULL, 0, 0, 0)
 #define ethosu_init_v2(base_address, fast_memory, fast_memory_size)                                                    \
     ethosu_init_v3(base_address, fast_memory, fast_memory_size, 0, 0)
+#define ethosu_init_v3(base_address, fast_memory, fast_memory_size, secure_enable, privilege_enable)                   \
+    ethosu_init_v4(&ethosu_drv, base_address, fast_memory, fast_memory_size, secure_enable, privilege_enable)
 
 /**
  * Get Ethos-U version.
  */
-int ethosu_get_version(struct ethosu_version *version);
+int ethosu_get_version_v2(struct ethosu_driver *drv, struct ethosu_version *version);
+
+#define ethosu_get_version(version) ethosu_get_version_v2(&ethosu_drv, version)
 
 /**
  * Invoke Vela command stream.
  */
-int ethosu_invoke_v2(const void *custom_data_ptr,
+int ethosu_invoke_v3(struct ethosu_driver *drv,
+                     const void *custom_data_ptr,
                      const int custom_data_size,
                      const uint64_t *base_addr,
                      const size_t *base_addr_size,
@@ -116,21 +124,49 @@
 
 #define ethosu_invoke(custom_data_ptr, custom_data_size, base_addr, num_base_addr)                                     \
     ethosu_invoke_v2(custom_data_ptr, custom_data_size, base_addr, NULL, num_base_addr)
+#define ethosu_invoke_v2(custom_data_ptr, custom_data_size, base_addr, base_addr_size, num_base_addr)                  \
+    ethosu_invoke_v3(&ethosu_drv, custom_data_ptr, custom_data_size, base_addr, base_addr_size, num_base_addr)
 
 /**
  * Abort Ethos-U inference.
  */
-void ethosu_abort(void);
+void ethosu_abort_v2(struct ethosu_driver *drv);
+
+#define ethosu_abort(void) ethosu_abort_v2(&ethosu_drv)
 
 /**
  * Interrupt handler do be called on IRQ from Ethos-U
  */
-void ethosu_irq_handler(void);
+void ethosu_irq_handler_v2(struct ethosu_driver *drv);
+
+#define ethosu_irq_handler(void) ethosu_irq_handler_v2(&ethosu_drv)
 
 /**
  * Set Ethos-U power mode.
  */
-void ethosu_set_power_mode(bool);
+void ethosu_set_power_mode_v2(struct ethosu_driver *drv, bool always_on);
+
+#define ethosu_set_power_mode(always_on) ethosu_set_power_mode_v2(&ethosu_drv, always_on)
+
+/**
+ *  Register a driver for multiNPU usage
+ */
+int ethosu_register_driver(struct ethosu_driver *drv);
+
+/**
+ * Deregister a driver from multiNPU usage
+ */
+int ethosu_deregister_driver(struct ethosu_driver *drv);
+
+/**
+ * Find, reserve, and return the first available driver
+ */
+struct ethosu_driver *ethosu_reserve_driver(void);
+
+/**
+ * Change driver status to available
+ */
+void ethosu_release_driver(struct ethosu_driver *drv);
 
 #ifdef __cplusplus
 }
diff --git a/include/pmu_ethosu.h b/include/pmu_ethosu.h
index 78d46ee..74a2989 100644
--- a/include/pmu_ethosu.h
+++ b/include/pmu_ethosu.h
@@ -25,6 +25,8 @@
 
 #include <stdint.h>
 
+#include "ethosu_driver.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -137,36 +139,48 @@
 /**
  * \brief   Enable the PMU
  */
-void ETHOSU_PMU_Enable(void);
+void ETHOSU_PMU_Enable_v2(struct ethosu_driver *drv);
+
+#define ETHOSU_PMU_Enable(void) ETHOSU_PMU_Enable_v2(&ethosu_drv)
 
 /**
  * \brief   Disable the PMU
  */
-void ETHOSU_PMU_Disable(void);
+void ETHOSU_PMU_Disable_v2(struct ethosu_driver *drv);
+
+#define ETHOSU_PMU_Disable(void) ETHOSU_PMU_Disable_v2(&ethosu_drv)
 
 /**
  * \brief   Set event to count for PMU eventer counter
  * \param [in]    num     Event counter (0-ETHOSU_PMU_NCOUNTERS) to configure
  * \param [in]    type    Event to count
  */
-void ETHOSU_PMU_Set_EVTYPER(uint32_t num, enum ethosu_pmu_event_type type);
+void ETHOSU_PMU_Set_EVTYPER_v2(struct ethosu_driver *drv, uint32_t num, enum ethosu_pmu_event_type type);
+
+#define ETHOSU_PMU_Set_EVTYPER(num, type) ETHOSU_PMU_Set_EVTYPER_v2(&ethosu_drv, num, type)
 
 /**
  * \brief   Get event to count for PMU eventer counter
  * \param [in]    num     Event counter (0-ETHOSU_PMU_NCOUNTERS) to configure
  * \return        type    Event to count
  */
-enum ethosu_pmu_event_type ETHOSU_PMU_Get_EVTYPER(uint32_t num);
+enum ethosu_pmu_event_type ETHOSU_PMU_Get_EVTYPER_v2(struct ethosu_driver *drv, uint32_t num);
+
+#define ETHOSU_PMU_Get_EVTYPER(num) ETHOSU_PMU_Get_EVTYPER_v2(&ethosu_drv, num)
 
 /**
  * \brief  Reset cycle counter
  */
-void ETHOSU_PMU_CYCCNT_Reset(void);
+void ETHOSU_PMU_CYCCNT_Reset_v2(struct ethosu_driver *drv);
+
+#define ETHOSU_PMU_CYCCNT_Reset(void) ETHOSU_PMU_CYCCNT_Reset_v2(&ethosu_drv)
 
 /**
  * \brief  Reset all event counters
  */
-void ETHOSU_PMU_EVCNTR_ALL_Reset(void);
+void ETHOSU_PMU_EVCNTR_ALL_Reset_v2(struct ethosu_driver *drv);
+
+#define ETHOSU_PMU_EVCNTR_ALL_Reset(void) ETHOSU_PMU_EVCNTR_ALL_Reset_v2(&ethosu_drv)
 
 /**
  * \brief  Enable counters
@@ -175,7 +189,9 @@
  *         - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
  *         - cycle counter  (bit 31)
  */
-void ETHOSU_PMU_CNTR_Enable(uint32_t mask);
+void ETHOSU_PMU_CNTR_Enable_v2(struct ethosu_driver *drv, uint32_t mask);
+
+#define ETHOSU_PMU_CNTR_Enable(mask) ETHOSU_PMU_CNTR_Enable_v2(&ethosu_drv, mask)
 
 /**
  * \brief  Disable counters
@@ -184,7 +200,9 @@
  *         - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
  *         - cycle counter  (bit 31)
  */
-void ETHOSU_PMU_CNTR_Disable(uint32_t mask);
+void ETHOSU_PMU_CNTR_Disable_v2(struct ethosu_driver *drv, uint32_t mask);
+
+#define ETHOSU_PMU_CNTR_Disable(mask) ETHOSU_PMU_CNTR_Disable_v2(&ethosu_drv, mask)
 
 /**
  * \brief  Determine counters activation
@@ -196,7 +214,9 @@
  *         - cycle counter  activate  (bit 31)
  * \note   ETHOSU specific. Usage breaks CMSIS complience
  */
-uint32_t ETHOSU_PMU_CNTR_Status(void);
+uint32_t ETHOSU_PMU_CNTR_Status_v2(struct ethosu_driver *drv);
+
+#define ETHOSU_PMU_CNTR_Status(void) ETHOSU_PMU_CNTR_Status_v2(&ethosu_drv)
 
 /**
  * \brief  Read cycle counter (64 bit)
@@ -207,7 +227,9 @@
  *         is not greater than the former, it means overflow of LSW without
  *         incrementing MSW has occurred, in which case the former value is used.
  */
-uint64_t ETHOSU_PMU_Get_CCNTR(void);
+uint64_t ETHOSU_PMU_Get_CCNTR_v2(struct ethosu_driver *drv);
+
+#define ETHOSU_PMU_Get_CCNTR(void) ETHOSU_PMU_Get_CCNTR_v2(&ethosu_drv)
 
 /**
  * \brief  Set cycle counter (64 bit)
@@ -216,14 +238,18 @@
  *         To work-around raciness, counter is temporary disabled if enabled.
  * \note   ETHOSU specific. Usage breaks CMSIS complience
  */
-void ETHOSU_PMU_Set_CCNTR(uint64_t val);
+void ETHOSU_PMU_Set_CCNTR_v2(struct ethosu_driver *drv, uint64_t val);
+
+#define ETHOSU_PMU_Set_CCNTR(val) ETHOSU_PMU_Set_CCNTR_v2(&ethosu_drv, val)
 
 /**
  * \brief   Read event counter
  * \param [in]    num     Event counter (0-ETHOSU_PMU_NCOUNTERS)
  * \return                Event count
  */
-uint32_t ETHOSU_PMU_Get_EVCNTR(uint32_t num);
+uint32_t ETHOSU_PMU_Get_EVCNTR_v2(struct ethosu_driver *drv, uint32_t num);
+
+#define ETHOSU_PMU_Get_EVCNTR(num) ETHOSU_PMU_Get_EVCNTR_v2(&ethosu_drv, num)
 
 /**
  * \brief   Set event counter value
@@ -231,7 +257,9 @@
  * \param [in]    val     Conter value
  * \note   ETHOSU specific. Usage breaks CMSIS complience
  */
-void ETHOSU_PMU_Set_EVCNTR(uint32_t num, uint32_t val);
+void ETHOSU_PMU_Set_EVCNTR_v2(struct ethosu_driver *drv, uint32_t num, uint32_t val);
+
+#define ETHOSU_PMU_Set_EVCNTR(num, val) ETHOSU_PMU_Set_EVCNTR_v2(&ethosu_drv, num, val)
 
 /**
  * \brief   Read counter overflow status
@@ -239,7 +267,9 @@
  *          - event counters (bit 0-ETHOSU_PMU_NCOUNTERS))
  *          - cycle counter  (bit 31)
  */
-uint32_t ETHOSU_PMU_Get_CNTR_OVS(void);
+uint32_t ETHOSU_PMU_Get_CNTR_OVS_v2(struct ethosu_driver *drv);
+
+#define ETHOSU_PMU_Get_CNTR_OVS(void) ETHOSU_PMU_Get_CNTR_OVS_v2(&ethosu_drv)
 
 /**
  * \brief   Clear counter overflow status
@@ -248,7 +278,9 @@
  *          - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
  *          - cycle counter  (bit 31)
  */
-void ETHOSU_PMU_Set_CNTR_OVS(uint32_t mask);
+void ETHOSU_PMU_Set_CNTR_OVS_v2(struct ethosu_driver *drv, uint32_t mask);
+
+#define ETHOSU_PMU_Set_CNTR_OVS(mask) ETHOSU_PMU_Set_CNTR_OVS_v2(&ethosu_drv, mask)
 
 /**
  * \brief   Enable counter overflow interrupt request
@@ -257,7 +289,9 @@
  *          - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
  *          - cycle counter  (bit 31)
  */
-void ETHOSU_PMU_Set_CNTR_IRQ_Enable(uint32_t mask);
+void ETHOSU_PMU_Set_CNTR_IRQ_Enable_v2(struct ethosu_driver *drv, uint32_t mask);
+
+#define ETHOSU_PMU_Set_CNTR_IRQ_Enable(mask) ETHOSU_PMU_Set_CNTR_IRQ_Enable_v2(&ethosu_drv, mask)
 
 /**
  * \brief   Disable counter overflow interrupt request
@@ -266,7 +300,9 @@
  *          - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
  *          - cycle counter  (bit 31)
  */
-void ETHOSU_PMU_Set_CNTR_IRQ_Disable(uint32_t mask);
+void ETHOSU_PMU_Set_CNTR_IRQ_Disable_v2(struct ethosu_driver *drv, uint32_t mask);
+
+#define ETHOSU_PMU_Set_CNTR_IRQ_Disable(mask) ETHOSU_PMU_Set_CNTR_IRQ_Disable_v2(&ethosu_drv, mask)
 
 /**
  * \brief   Get counters overflow interrupt request stiinings
@@ -276,7 +312,9 @@
  *          - cycle counter  (bit 31)
  * \note   ETHOSU specific. Usage breaks CMSIS compliance
  */
-uint32_t ETHOSU_PMU_Get_IRQ_Enable(void);
+uint32_t ETHOSU_PMU_Get_IRQ_Enable_v2(struct ethosu_driver *drv);
+
+#define ETHOSU_PMU_Get_IRQ_Enable(void) ETHOSU_PMU_Get_IRQ_Enable_v2(&ethosu_drv)
 
 /**
  * \brief   Software increment event counter
@@ -285,7 +323,9 @@
  *          - cycle counter  (bit 31)
  * \note    Software increment bits for one or more event counters.
  */
-void ETHOSU_PMU_CNTR_Increment(uint32_t mask);
+void ETHOSU_PMU_CNTR_Increment_v2(struct ethosu_driver *drv, uint32_t mask);
+
+#define ETHOSU_PMU_CNTR_Increment(mask) ETHOSU_PMU_CNTR_Increment_v2(&ethosu_drv, mask)
 
 /**
  * \brief   Set start event number for the cycle counter
@@ -294,7 +334,10 @@
  * \note   Sets the event number that starts the cycle counter.
  *           - Event number in the range 0..1023
  */
-void ETHOSU_PMU_PMCCNTR_CFG_Set_Start_Event(uint32_t start_event);
+void ETHOSU_PMU_PMCCNTR_CFG_Set_Start_Event_v2(struct ethosu_driver *drv, uint32_t start_event);
+
+#define ETHOSU_PMU_PMCCNTR_CFG_Set_Start_Event(start_event)                                                            \
+    ETHOSU_PMU_PMCCNTR_CFG_Set_Start_Event_v2(&ethosu_drv, start_event)
 
 /**
  * \brief   Set stop event number for the cycle counter
@@ -303,7 +346,10 @@
  * \note   Sets the event number that stops the cycle counter.
  *           - Event number in the range 0..1023
  */
-void ETHOSU_PMU_PMCCNTR_CFG_Set_Stop_Event(uint32_t stop_event);
+void ETHOSU_PMU_PMCCNTR_CFG_Set_Stop_Event_v2(struct ethosu_driver *drv, uint32_t stop_event);
+
+#define ETHOSU_PMU_PMCCNTR_CFG_Set_Stop_Event(stop_event)                                                              \
+    ETHOSU_PMU_PMCCNTR_CFG_Set_Stop_Event_v2(&ethosu_drv, stop_event)
 
 #ifdef __cplusplus
 }
diff --git a/src/ethosu_driver.c b/src/ethosu_driver.c
index 182b90c..2eedfe3 100644
--- a/src/ethosu_driver.c
+++ b/src/ethosu_driver.c
@@ -154,33 +154,37 @@
     .status_error        = false,
     .dev_power_always_on = false};
 
+// Registered drivers linked list HEAD
+static struct ethosu_driver *registered_drivers = NULL;
+
 // IRQ
 static volatile bool irq_triggered = false;
 static int ethosu_soft_reset_and_restore(struct ethosu_driver *drv);
-void ethosu_irq_handler(void)
+
+void ethosu_irq_handler_v2(struct ethosu_driver *drv)
 {
     uint8_t irq_raised = 0;
 
     LOG_DEBUG("Interrupt. status=0x%08x, qread=%d\n",
-              ethosu_read_reg(&ethosu_drv.dev, NPU_REG_STATUS),
-              ethosu_read_reg(&ethosu_drv.dev, NPU_REG_QREAD));
+              ethosu_read_reg(&drv->dev, NPU_REG_STATUS),
+              ethosu_read_reg(&drv->dev, NPU_REG_QREAD));
 
     // Verify that interrupt has been raised
-    (void)ethosu_is_irq_raised(&ethosu_drv.dev, &irq_raised);
+    (void)ethosu_is_irq_raised(&drv->dev, &irq_raised);
     ASSERT(irq_raised == 1);
     irq_triggered = true;
 
     // Clear interrupt
-    (void)ethosu_clear_irq_status(&ethosu_drv.dev);
+    (void)ethosu_clear_irq_status(&drv->dev);
 
     // Verify that interrupt has been successfully cleared
-    (void)ethosu_is_irq_raised(&ethosu_drv.dev, &irq_raised);
+    (void)ethosu_is_irq_raised(&drv->dev, &irq_raised);
     ASSERT(irq_raised == 0);
 
-    if (ethosu_status_has_error(&ethosu_drv.dev))
+    if (ethosu_status_has_error(&drv->dev))
     {
-        ethosu_soft_reset_and_restore(&ethosu_drv);
-        ethosu_drv.status_error = true;
+        ethosu_soft_reset_and_restore(drv);
+        drv->status_error = true;
     }
 }
 
@@ -214,7 +218,8 @@
 static void dump_command_stream(const uint32_t *cmd_stream, const int cms_length, int qread);
 static void npu_axi_init(struct ethosu_driver *drv);
 
-int ethosu_init_v3(const void *base_address,
+int ethosu_init_v4(struct ethosu_driver *drv,
+                   const void *base_address,
                    const void *fast_memory,
                    const size_t fast_memory_size,
                    uint32_t secure_enable,
@@ -230,38 +235,40 @@
              secure_enable,
              privilege_enable);
 
-    ethosu_drv.fast_memory      = (uint32_t)fast_memory;
-    ethosu_drv.fast_memory_size = fast_memory_size;
+    ethosu_register_driver(drv);
 
-    if (ETHOSU_SUCCESS != ethosu_dev_init(&ethosu_drv.dev, base_address, secure_enable, privilege_enable))
+    drv->fast_memory      = (uint32_t)fast_memory;
+    drv->fast_memory_size = fast_memory_size;
+
+    if (ETHOSU_SUCCESS != ethosu_dev_init(&drv->dev, base_address, secure_enable, privilege_enable))
     {
         LOG_ERR("Failed in ethosu_dev_init");
         return -1;
     }
 
-    if (ETHOSU_SUCCESS != ethosu_set_clock_and_power(&ethosu_drv.dev, ETHOSU_CLOCK_Q_DISABLE, ETHOSU_POWER_Q_DISABLE))
+    if (ETHOSU_SUCCESS != ethosu_set_clock_and_power(&drv->dev, ETHOSU_CLOCK_Q_DISABLE, ETHOSU_POWER_Q_DISABLE))
     {
         LOG_ERR("Failed to disable clock-q & power-q for Ethos-U\n");
         return -1;
     }
 
-    if (ETHOSU_SUCCESS != ethosu_soft_reset(&ethosu_drv.dev))
+    if (ETHOSU_SUCCESS != ethosu_soft_reset(&drv->dev))
     {
         return -1;
     }
 
-    if (ETHOSU_SUCCESS != ethosu_wait_for_reset(&ethosu_drv.dev))
+    if (ETHOSU_SUCCESS != ethosu_wait_for_reset(&drv->dev))
     {
         LOG_ERR("Failed reset of Ethos-U\n");
         return -1;
     }
 
-    ethosu_drv.status_error = false;
+    drv->status_error = false;
 
     return return_code;
 }
 
-int ethosu_get_version(struct ethosu_version *version)
+int ethosu_get_version_v2(struct ethosu_driver *drv, struct ethosu_version *version)
 {
     int return_code = 0;
 
@@ -269,8 +276,8 @@
     {
         struct ethosu_id id;
         struct ethosu_config cfg;
-        (void)ethosu_get_id(&ethosu_drv.dev, &id);
-        (void)ethosu_get_config(&ethosu_drv.dev, &cfg);
+        (void)ethosu_get_id(&drv->dev, &id);
+        (void)ethosu_get_config(&drv->dev, &cfg);
 
         version->id.version_status      = id.version_status;
         version->id.version_minor       = id.version_minor;
@@ -294,7 +301,8 @@
     return return_code;
 }
 
-int ethosu_invoke_v2(const void *custom_data_ptr,
+int ethosu_invoke_v3(struct ethosu_driver *drv,
+                     const void *custom_data_ptr,
                      const int custom_data_size,
                      const uint64_t *base_addr,
                      const size_t *base_addr_size,
@@ -323,36 +331,39 @@
     ++data_ptr;
 
     // Adjust base address to fast memory area
-    if (ethosu_drv.fast_memory != 0 && num_base_addr >= FAST_MEMORY_BASE_ADDR_INDEX)
+    if (drv->fast_memory != 0 && num_base_addr >= FAST_MEMORY_BASE_ADDR_INDEX)
     {
         uint64_t *fast_memory = (uint64_t *)&base_addr[FAST_MEMORY_BASE_ADDR_INDEX];
 
-        if (base_addr_size != NULL && base_addr_size[FAST_MEMORY_BASE_ADDR_INDEX] > ethosu_drv.fast_memory_size)
+        if (base_addr_size != NULL && base_addr_size[FAST_MEMORY_BASE_ADDR_INDEX] > drv->fast_memory_size)
         {
             LOG_ERR("Fast memory area too small. fast_memory_size=%u, base_addr_size=%u\n",
-                    ethosu_drv.fast_memory_size,
+                    drv->fast_memory_size,
                     base_addr_size[FAST_MEMORY_BASE_ADDR_INDEX]);
             return -1;
         }
 
-        *fast_memory = ethosu_drv.fast_memory;
+        *fast_memory = drv->fast_memory;
     }
 
-    if (!ethosu_drv.dev_power_always_on)
+    if (!drv->dev_power_always_on)
     {
-        if (ethosu_drv.dev.proto != ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PROT))
+        // Only soft reset if securty state or privilege level needs changing
+        if (drv->dev.proto != ethosu_read_reg(&drv->dev, NPU_REG_PROT))
         {
-            if (ETHOSU_SUCCESS != ethosu_soft_reset(&ethosu_drv.dev))
+            if (ETHOSU_SUCCESS != ethosu_soft_reset(&drv->dev))
             {
                 return -1;
             }
         }
-        ethosu_set_clock_and_power(&ethosu_drv.dev, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_DISABLE);
-        ethosu_restore_pmu_config(&ethosu_drv.dev);
-        npu_axi_init(&ethosu_drv);
+
+        drv->status_error = false;
+        ethosu_set_clock_and_power(&drv->dev, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_DISABLE);
+        ethosu_restore_pmu_config(&drv->dev);
+        npu_axi_init(drv);
     }
 
-    ethosu_drv.status_error = false;
+    drv->status_error = false;
 
     while (data_ptr < data_end)
     {
@@ -363,7 +374,7 @@
             LOG_INFO("ethosu_invoke OPTIMIZER_CONFIG\n");
             struct opt_cfg_s *opt_cfg_p = (struct opt_cfg_s *)data_ptr;
 
-            ret = handle_optimizer_config(&ethosu_drv, opt_cfg_p);
+            ret = handle_optimizer_config(drv, opt_cfg_p);
             data_ptr += DRIVER_ACTION_LENGTH_32_BIT_WORD + OPTIMIZER_CONFIG_LENGTH_32_BIT_WORD;
             break;
         case COMMAND_STREAM:
@@ -371,34 +382,33 @@
             void *command_stream = (uint8_t *)(data_ptr) + sizeof(struct custom_data_s);
             int cms_length       = (data_ptr->reserved << 16) | data_ptr->length;
 
-            ethosu_drv.abort_inference = false;
+            drv->abort_inference = false;
             // It is safe to clear this flag without atomic, because npu is not running.
             irq_triggered = false;
 
-            ret = handle_command_stream(
-                &ethosu_drv, command_stream, cms_length, base_addr, base_addr_size, num_base_addr);
+            ret = handle_command_stream(drv, command_stream, cms_length, base_addr, base_addr_size, num_base_addr);
 
-            if (return_code == -1 && ethosu_drv.abort_inference)
+            if (return_code == -1 && drv->abort_inference)
             {
                 uint32_t qread = 0;
-                ethosu_get_qread(&ethosu_drv.dev, &qread);
+                ethosu_get_qread(&drv->dev, &qread);
                 LOG_ERR("NPU timeout\n");
                 dump_command_stream(command_stream, cms_length, qread);
-                dump_npu_register(&ethosu_drv, 0x200, 0x2BF);
-                dump_npu_register(&ethosu_drv, 0x800, 0xB3F);
-                dump_shram(&ethosu_drv);
+                dump_npu_register(drv, 0x200, 0x2BF);
+                dump_npu_register(drv, 0x800, 0xB3F);
+                dump_shram(drv);
             }
 
             data_ptr += DRIVER_ACTION_LENGTH_32_BIT_WORD + cms_length;
             break;
         case READ_APB_REG:
             LOG_INFO("ethosu_invoke READ_APB_REG\n");
-            ret = read_apb_reg(&ethosu_drv, data_ptr->driver_action_data);
+            ret = read_apb_reg(drv, data_ptr->driver_action_data);
             data_ptr += DRIVER_ACTION_LENGTH_32_BIT_WORD;
             break;
         case DUMP_SHRAM:
             LOG_INFO("ethosu_invoke DUMP_SHRAM\n");
-            ret = dump_shram(&ethosu_drv);
+            ret = dump_shram(drv);
             data_ptr += DRIVER_ACTION_LENGTH_32_BIT_WORD;
             break;
         case NOP:
@@ -417,27 +427,103 @@
         }
     }
 
-    if (!ethosu_drv.status_error && !ethosu_drv.dev_power_always_on)
+    if (!drv->status_error && !drv->dev_power_always_on)
     {
-        ethosu_save_pmu_counters(&ethosu_drv.dev);
-        ethosu_set_clock_and_power(&ethosu_drv.dev, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_ENABLE);
+        ethosu_save_pmu_counters(&drv->dev);
+        ethosu_set_clock_and_power(&drv->dev, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_ENABLE);
     }
 
     return return_code;
 }
 
-void ethosu_abort(void)
+void ethosu_abort_v2(struct ethosu_driver *drv)
 {
-    ethosu_drv.abort_inference = true;
+    drv->abort_inference = true;
 }
 
-void ethosu_set_power_mode(bool always_on)
+void ethosu_set_power_mode_v2(struct ethosu_driver *drv, bool always_on)
 {
-    ethosu_drv.dev_power_always_on = always_on;
+    drv->dev_power_always_on = always_on;
 
     if (always_on)
     {
-        npu_axi_init(&ethosu_drv);
+        npu_axi_init(drv);
+    }
+}
+
+int ethosu_register_driver(struct ethosu_driver *drv)
+{
+    // Safeguard check for if driver is already registered
+    struct ethosu_driver *cur = registered_drivers;
+    while (cur != NULL)
+    {
+        if (cur == drv)
+        {
+            LOG_ERR("%s: NPU driver at address %p is already registered.\n", __FUNCTION__, drv);
+            return -1;
+        }
+        cur = cur->next;
+    }
+
+    drv->next = registered_drivers;
+    // Designate new registered driver HEAD
+    registered_drivers = drv;
+
+    LOG_INFO("%s: New NPU driver at address %p is registered.\n", __FUNCTION__, drv);
+
+    return 0;
+}
+
+int ethosu_deregister_driver(struct ethosu_driver *drv)
+{
+    struct ethosu_driver *cur   = registered_drivers;
+    struct ethosu_driver **prev = &registered_drivers;
+
+    while (cur != NULL)
+    {
+        if (cur == drv)
+        {
+            *prev = cur->next;
+            LOG_INFO("%s: NPU driver at address %p is deregistered.\n", __FUNCTION__, drv);
+            return 0;
+        }
+
+        prev = &cur->next;
+        cur  = cur->next;
+    }
+
+    LOG_ERR("%s: NPU driver at address %p does not match a registered driver and therefore may not be deregistered.\n",
+            __FUNCTION__,
+            drv);
+    return -1;
+}
+
+struct ethosu_driver *ethosu_reserve_driver(void)
+{
+    struct ethosu_driver *drv = registered_drivers;
+
+    while (drv != NULL)
+    {
+        if (!drv->reserved)
+        {
+            drv->reserved = true;
+            LOG_INFO("%s - Driver %p reserved.\n", __FUNCTION__, drv);
+            return drv;
+        }
+        drv = drv->next;
+    }
+
+    LOG_INFO("%s: No available drivers.\n", __FUNCTION__, drv);
+
+    return NULL;
+}
+
+void ethosu_release_driver(struct ethosu_driver *drv)
+{
+    if (drv != NULL && drv->reserved)
+    {
+        drv->reserved = false;
+        LOG_INFO("%s - Driver %p released\n", __FUNCTION__, drv);
     }
 }
 
diff --git a/src/ethosu_pmu.c b/src/ethosu_pmu.c
index 57edacd..759a722 100644
--- a/src/ethosu_pmu.c
+++ b/src/ethosu_pmu.c
@@ -84,90 +84,90 @@
  * Functions
  *****************************************************************************/
 
-void ETHOSU_PMU_Enable(void)
+void ETHOSU_PMU_Enable_v2(struct ethosu_driver *drv)
 {
     LOG_DEBUG("%s:\n", __FUNCTION__);
     struct pmcr_r pmcr;
-    pmcr.word   = ethosu_drv.dev.pmcr;
+    pmcr.word   = drv->dev.pmcr;
     pmcr.cnt_en = 1;
-    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCR, pmcr.word, &ethosu_drv.dev.pmcr);
+    ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMCR, pmcr.word, &drv->dev.pmcr);
 }
 
-void ETHOSU_PMU_Disable(void)
+void ETHOSU_PMU_Disable_v2(struct ethosu_driver *drv)
 {
     LOG_DEBUG("%s:\n", __FUNCTION__);
     struct pmcr_r pmcr;
-    pmcr.word   = ethosu_drv.dev.pmcr;
+    pmcr.word   = drv->dev.pmcr;
     pmcr.cnt_en = 0;
-    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCR, pmcr.word, &ethosu_drv.dev.pmcr);
+    ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMCR, pmcr.word, &drv->dev.pmcr);
 }
 
-void ETHOSU_PMU_Set_EVTYPER(uint32_t num, enum ethosu_pmu_event_type type)
+void ETHOSU_PMU_Set_EVTYPER_v2(struct ethosu_driver *drv, uint32_t num, enum ethosu_pmu_event_type type)
 {
     ASSERT(num < ETHOSU_PMU_NCOUNTERS);
     uint32_t val = pmu_event_value(type);
     LOG_DEBUG("%s: num=%u, type=%d, val=%u\n", __FUNCTION__, num, type, val);
-    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMEVTYPER(num), val, &ethosu_drv.dev.pmu_evtypr[num]);
+    ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMEVTYPER(num), val, &drv->dev.pmu_evtypr[num]);
 }
 
-enum ethosu_pmu_event_type ETHOSU_PMU_Get_EVTYPER(uint32_t num)
+enum ethosu_pmu_event_type ETHOSU_PMU_Get_EVTYPER_v2(struct ethosu_driver *drv, uint32_t num)
 {
     ASSERT(num < ETHOSU_PMU_NCOUNTERS);
-    uint32_t val                    = ethosu_drv.dev.pmu_evtypr[num];
+    uint32_t val                    = drv->dev.pmu_evtypr[num];
     enum ethosu_pmu_event_type type = pmu_event_type(val);
     LOG_DEBUG("%s: num=%u, type=%d, val=%u\n", __FUNCTION__, num, type, val);
     return type;
 }
 
-void ETHOSU_PMU_CYCCNT_Reset(void)
+void ETHOSU_PMU_CYCCNT_Reset_v2(struct ethosu_driver *drv)
 {
     LOG_DEBUG("%s:\n", __FUNCTION__);
     struct pmcr_r pmcr;
-    pmcr.word          = ethosu_drv.dev.pmcr;
+    pmcr.word          = drv->dev.pmcr;
     pmcr.cycle_cnt_rst = 1;
-    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCR, pmcr.word, &ethosu_drv.dev.pmcr);
-    ethosu_drv.dev.pmccntr[0] = 0;
-    ethosu_drv.dev.pmccntr[1] = 0;
+    ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMCR, pmcr.word, &drv->dev.pmcr);
+    drv->dev.pmccntr[0] = 0;
+    drv->dev.pmccntr[1] = 0;
 }
 
-void ETHOSU_PMU_EVCNTR_ALL_Reset(void)
+void ETHOSU_PMU_EVCNTR_ALL_Reset_v2(struct ethosu_driver *drv)
 {
     LOG_DEBUG("%s:\n", __FUNCTION__);
     struct pmcr_r pmcr;
-    pmcr.word          = ethosu_drv.dev.pmcr;
+    pmcr.word          = drv->dev.pmcr;
     pmcr.event_cnt_rst = 1;
-    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCR, pmcr.word, &ethosu_drv.dev.pmcr);
+    ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMCR, pmcr.word, &drv->dev.pmcr);
 
     for (uint32_t i = 0; i < ETHOSU_PMU_NCOUNTERS; i++)
     {
-        ethosu_drv.dev.pmu_evcntr[i] = 0;
+        drv->dev.pmu_evcntr[i] = 0;
     }
 }
 
-void ETHOSU_PMU_CNTR_Enable(uint32_t mask)
+void ETHOSU_PMU_CNTR_Enable_v2(struct ethosu_driver *drv, uint32_t mask)
 {
     LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, mask);
-    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCNTENSET, mask, &ethosu_drv.dev.pmcnten);
+    ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMCNTENSET, mask, &drv->dev.pmcnten);
 }
 
-void ETHOSU_PMU_CNTR_Disable(uint32_t mask)
+void ETHOSU_PMU_CNTR_Disable_v2(struct ethosu_driver *drv, uint32_t mask)
 {
     LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, mask);
-    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCNTENCLR, mask, &ethosu_drv.dev.pmcnten);
+    ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMCNTENCLR, mask, &drv->dev.pmcnten);
 }
 
-uint32_t ETHOSU_PMU_CNTR_Status(void)
+uint32_t ETHOSU_PMU_CNTR_Status_v2(struct ethosu_driver *drv)
 {
-    LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, ethosu_drv.dev.pmcnten);
-    return ethosu_drv.dev.pmcnten;
+    LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, drv->dev.pmcnten);
+    return drv->dev.pmcnten;
 }
 
-uint64_t ETHOSU_PMU_Get_CCNTR(void)
+uint64_t ETHOSU_PMU_Get_CCNTR_v2(struct ethosu_driver *drv)
 {
-    uint32_t val_lo = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_LO);
-    uint32_t val_hi = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_HI);
+    uint32_t val_lo = ethosu_read_reg(&drv->dev, NPU_REG_PMCCNTR_LO);
+    uint32_t val_hi = ethosu_read_reg(&drv->dev, NPU_REG_PMCCNTR_HI);
     uint64_t val    = ((uint64_t)val_hi << 32) | val_lo;
-    uint64_t shadow = ((uint64_t)ethosu_drv.dev.pmccntr[1] << 32) | ethosu_drv.dev.pmccntr[0];
+    uint64_t shadow = ((uint64_t)drv->dev.pmccntr[1] << 32) | drv->dev.pmccntr[0];
 
     LOG_DEBUG("%s: val=%" PRIu64 ", shadow=%" PRIu64 "\n", __FUNCTION__, val, shadow);
 
@@ -178,132 +178,130 @@
     }
 
     // Update the shadow variable
-    ethosu_drv.dev.pmccntr[0] = val_lo;
-    ethosu_drv.dev.pmccntr[1] = val_hi;
+    drv->dev.pmccntr[0] = val_lo;
+    drv->dev.pmccntr[1] = val_hi;
 
     return val;
 }
 
-void ETHOSU_PMU_Set_CCNTR(uint64_t val)
+void ETHOSU_PMU_Set_CCNTR_v2(struct ethosu_driver *drv, uint64_t val)
 {
-    uint32_t active = ETHOSU_PMU_CNTR_Status() & ETHOSU_PMU_CCNT_Msk;
+    uint32_t active = ETHOSU_PMU_CNTR_Status_v2(drv) & ETHOSU_PMU_CCNT_Msk;
 
     LOG_DEBUG("%s: val=%llu\n", __FUNCTION__, val);
 
     if (active)
     {
-        ETHOSU_PMU_CNTR_Disable(ETHOSU_PMU_CCNT_Msk);
+        ETHOSU_PMU_CNTR_Disable_v2(drv, ETHOSU_PMU_CCNT_Msk);
     }
 
-    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCCNTR_LO, val & MASK_0_31_BITS, &ethosu_drv.dev.pmccntr[0]);
-    ethosu_write_reg_shadow(
-        &ethosu_drv.dev, NPU_REG_PMCCNTR_HI, (val & MASK_32_47_BITS) >> 32, &ethosu_drv.dev.pmccntr[1]);
+    ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMCCNTR_LO, val & MASK_0_31_BITS, &drv->dev.pmccntr[0]);
+    ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMCCNTR_HI, (val & MASK_32_47_BITS) >> 32, &drv->dev.pmccntr[1]);
 
     if (active)
     {
-        ETHOSU_PMU_CNTR_Enable(ETHOSU_PMU_CCNT_Msk);
+        ETHOSU_PMU_CNTR_Enable_v2(drv, ETHOSU_PMU_CCNT_Msk);
     }
 }
 
-uint32_t ETHOSU_PMU_Get_EVCNTR(uint32_t num)
+uint32_t ETHOSU_PMU_Get_EVCNTR_v2(struct ethosu_driver *drv, uint32_t num)
 {
     ASSERT(num < ETHOSU_PMU_NCOUNTERS);
-    uint32_t val = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMEVCNTR(num));
-    LOG_DEBUG("%s: num=%u, val=%u, shadow=%u\n", __FUNCTION__, num, val, ethosu_drv.dev.pmu_evcntr[num]);
+    uint32_t val = ethosu_read_reg(&drv->dev, NPU_REG_PMEVCNTR(num));
+    LOG_DEBUG("%s: num=%u, val=%u, shadow=%u\n", __FUNCTION__, num, val, drv->dev.pmu_evcntr[num]);
 
     // Return the shadow variable in case the NPU was powered off and lost the event count
-    if (ethosu_drv.dev.pmu_evcntr[num] > val)
+    if (drv->dev.pmu_evcntr[num] > val)
     {
-        return ethosu_drv.dev.pmu_evcntr[num];
+        return drv->dev.pmu_evcntr[num];
     }
 
     // Update the shadow variable
-    ethosu_drv.dev.pmu_evcntr[num] = val;
+    drv->dev.pmu_evcntr[num] = val;
 
     return val;
 }
 
-void ETHOSU_PMU_Set_EVCNTR(uint32_t num, uint32_t val)
+void ETHOSU_PMU_Set_EVCNTR_v2(struct ethosu_driver *drv, uint32_t num, uint32_t val)
 {
     ASSERT(num < ETHOSU_PMU_NCOUNTERS);
     LOG_DEBUG("%s: num=%u, val=%u\n", __FUNCTION__, num, val);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMEVCNTR(num), val);
+    ethosu_write_reg(&drv->dev, NPU_REG_PMEVCNTR(num), val);
 }
 
-uint32_t ETHOSU_PMU_Get_CNTR_OVS(void)
+uint32_t ETHOSU_PMU_Get_CNTR_OVS_v2(struct ethosu_driver *drv)
 {
     LOG_DEBUG("%s:\n", __FUNCTION__);
-    return ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMOVSSET);
+    return ethosu_read_reg(&drv->dev, NPU_REG_PMOVSSET);
 }
 
-void ETHOSU_PMU_Set_CNTR_OVS(uint32_t mask)
+void ETHOSU_PMU_Set_CNTR_OVS_v2(struct ethosu_driver *drv, uint32_t mask)
 {
     LOG_DEBUG("%s:\n", __FUNCTION__);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMOVSCLR, mask);
+    ethosu_write_reg(&drv->dev, NPU_REG_PMOVSCLR, mask);
 }
 
-void ETHOSU_PMU_Set_CNTR_IRQ_Enable(uint32_t mask)
+void ETHOSU_PMU_Set_CNTR_IRQ_Enable_v2(struct ethosu_driver *drv, uint32_t mask)
 {
     LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, mask);
-    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMINTSET, mask, &ethosu_drv.dev.pmint);
+    ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMINTSET, mask, &drv->dev.pmint);
 }
 
-void ETHOSU_PMU_Set_CNTR_IRQ_Disable(uint32_t mask)
+void ETHOSU_PMU_Set_CNTR_IRQ_Disable_v2(struct ethosu_driver *drv, uint32_t mask)
 {
     LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, mask);
-    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMINTCLR, mask, &ethosu_drv.dev.pmint);
+    ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMINTCLR, mask, &drv->dev.pmint);
 }
 
-uint32_t ETHOSU_PMU_Get_IRQ_Enable(void)
+uint32_t ETHOSU_PMU_Get_IRQ_Enable_v2(struct ethosu_driver *drv)
 {
-    LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, ethosu_drv.dev.pmint);
-    return ethosu_drv.dev.pmint;
+    LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, drv->dev.pmint);
+    return drv->dev.pmint;
 }
 
-void ETHOSU_PMU_CNTR_Increment(uint32_t mask)
+void ETHOSU_PMU_CNTR_Increment_v2(struct ethosu_driver *drv, uint32_t mask)
 {
     LOG_DEBUG("%s:\n", __FUNCTION__);
-    uint32_t cntrs_active = ETHOSU_PMU_CNTR_Status();
+    uint32_t cntrs_active = ETHOSU_PMU_CNTR_Status_v2(drv);
 
     // Disable counters
-    ETHOSU_PMU_CNTR_Disable(mask);
+    ETHOSU_PMU_CNTR_Disable_v2(drv, mask);
 
     // Increment cycle counter
     if (mask & ETHOSU_PMU_CCNT_Msk)
     {
-        uint64_t val = ETHOSU_PMU_Get_CCNTR() + 1;
-        ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCCNTR_LO, val & MASK_0_31_BITS, &ethosu_drv.dev.pmccntr[0]);
-        ethosu_write_reg_shadow(
-            &ethosu_drv.dev, NPU_REG_PMCCNTR_HI, (val & MASK_32_47_BITS) >> 32, &ethosu_drv.dev.pmccntr[1]);
+        uint64_t val = ETHOSU_PMU_Get_CCNTR_v2(drv) + 1;
+        ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMCCNTR_LO, val & MASK_0_31_BITS, &drv->dev.pmccntr[0]);
+        ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMCCNTR_HI, (val & MASK_32_47_BITS) >> 32, &drv->dev.pmccntr[1]);
     }
 
     for (int i = 0; i < ETHOSU_PMU_NCOUNTERS; i++)
     {
         if (mask & (1 << i))
         {
-            uint32_t val = ETHOSU_PMU_Get_EVCNTR(i);
-            ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMEVCNTR(i), val + 1, &ethosu_drv.dev.pmu_evcntr[i]);
+            uint32_t val = ETHOSU_PMU_Get_EVCNTR_v2(drv, i);
+            ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMEVCNTR(i), val + 1, &drv->dev.pmu_evcntr[i]);
         }
     }
 
     // Reenable the active counters
-    ETHOSU_PMU_CNTR_Enable(cntrs_active);
+    ETHOSU_PMU_CNTR_Enable_v2(drv, cntrs_active);
 }
 
-void ETHOSU_PMU_PMCCNTR_CFG_Set_Start_Event(uint32_t start_event)
+void ETHOSU_PMU_PMCCNTR_CFG_Set_Start_Event_v2(struct ethosu_driver *drv, uint32_t start_event)
 {
     LOG_DEBUG("%s: start_event=%u\n", __FUNCTION__, start_event);
     struct pmccntr_cfg_r cfg;
-    cfg.word                = ethosu_drv.dev.pmccntr_cfg;
+    cfg.word                = drv->dev.pmccntr_cfg;
     cfg.CYCLE_CNT_CFG_START = start_event;
-    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCCNTR_CFG, cfg.word, &ethosu_drv.dev.pmccntr_cfg);
+    ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMCCNTR_CFG, cfg.word, &drv->dev.pmccntr_cfg);
 }
 
-void ETHOSU_PMU_PMCCNTR_CFG_Set_Stop_Event(uint32_t stop_event)
+void ETHOSU_PMU_PMCCNTR_CFG_Set_Stop_Event_v2(struct ethosu_driver *drv, uint32_t stop_event)
 {
     LOG_DEBUG("%s: stop_event=%u\n", __FUNCTION__, stop_event);
     struct pmccntr_cfg_r cfg;
-    cfg.word               = ethosu_drv.dev.pmccntr_cfg;
+    cfg.word               = drv->dev.pmccntr_cfg;
     cfg.CYCLE_CNT_CFG_STOP = stop_event;
-    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCCNTR_CFG, cfg.word, &ethosu_drv.dev.pmccntr_cfg);
+    ethosu_write_reg_shadow(&drv->dev, NPU_REG_PMCCNTR_CFG, cfg.word, &drv->dev.pmccntr_cfg);
 }