PMU counter shadow

Storing PMU counters in shadow variables, in case the PMU was powered
off or soft reset.

Change-Id: I64ccf3fb6195f9be2d8315891ec612bb75404885
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ff099c6..a4589ef 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -62,11 +62,6 @@
     message(FATAL_ERROR "Unsupported log level ${ETHOSU_LOG_SEVERITY}")
 endif()
 
-# Enable PMU boot auto-initialization
-if(DRIVER_PMU_AUTOINIT)
-    add_compile_definitions(PMU_AUTOINIT)
-endif()
-
 # Make include directories available for current- and sub projects
 include_directories(include src)
 include_directories(${CMSIS_PATH}/CMSIS/Core/Include)
diff --git a/include/ethosu_device.h b/include/ethosu_device.h
index 5edba94..91aa877 100644
--- a/include/ethosu_device.h
+++ b/include/ethosu_device.h
@@ -54,15 +54,15 @@
 
 struct ethosu_device
 {
-    uintptr_t base_address;
+    volatile uint32_t *base_address;
     uint32_t reset;
     uint32_t pmcr;
-    uint64_t pmccntr;
+    uint32_t pmccntr[2];
     uint32_t pmcnten;
     uint32_t pmint;
     uint32_t pmccntr_cfg;
     uint32_t pmu_evcntr[ETHOSU_PMU_NCOUNTERS];
-    enum ethosu_pmu_event_type pmu_evtypr[ETHOSU_PMU_NCOUNTERS];
+    uint32_t pmu_evtypr[ETHOSU_PMU_NCOUNTERS];
 };
 
 struct ethosu_id
@@ -378,6 +378,13 @@
 void ethosu_write_reg(struct ethosu_device *dev, uint32_t address, uint32_t value);
 
 /**
+ * Write register with shadow variable.
+ * \param[in] address          Address to read.
+ * \param[in] value            Value to be written.
+ */
+void ethosu_write_reg_shadow(struct ethosu_device *dev, uint32_t address, uint32_t value, uint32_t *shadow);
+
+/**
  * Save the PMU configuration to ethosu_device struct.
  * \param[in] dev              Ethos-U device where the PMU configuration is
  *                             saved.
@@ -394,6 +401,14 @@
 enum ethosu_error_codes ethosu_restore_pmu_config(struct ethosu_device *dev);
 
 /**
+ * Save PMU counters to shadow variables in memory.
+ * \param[in] dev              Ethos-U device where the PMU configuration is
+ *                             stored.
+ * \return                     \ref ethosu_error_codes
+ */
+enum ethosu_error_codes ethosu_save_pmu_counters(struct ethosu_device *dev);
+
+/**
  * Check if the STATUS register has any error bits set or not.
  * \param[in] dev              Ethos-U device to check.
  * \return                     true if any error bits set,
diff --git a/include/ethosu_driver.h b/include/ethosu_driver.h
index 2594fbe..433ad06 100644
--- a/include/ethosu_driver.h
+++ b/include/ethosu_driver.h
@@ -77,6 +77,12 @@
 };
 
 /******************************************************************************
+ * Variables
+ ******************************************************************************/
+
+extern struct ethosu_driver ethosu_drv;
+
+/******************************************************************************
  * Prototypes
  ******************************************************************************/
 
diff --git a/include/pmu_ethosu.h b/include/pmu_ethosu.h
index 7eb5c09..78d46ee 100644
--- a/include/pmu_ethosu.h
+++ b/include/pmu_ethosu.h
@@ -19,14 +19,32 @@
 #ifndef PMU_ETHOSU_H
 #define PMU_ETHOSU_H
 
+/*****************************************************************************
+ * Includes
+ *****************************************************************************/
+
 #include <stdint.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/*****************************************************************************
+ * Defines
+ *****************************************************************************/
+
 #define ETHOSU_PMU_NCOUNTERS 4
 
+#define ETHOSU_PMU_CNT1_Msk (1UL << 0)
+#define ETHOSU_PMU_CNT2_Msk (1UL << 1)
+#define ETHOSU_PMU_CNT3_Msk (1UL << 2)
+#define ETHOSU_PMU_CNT4_Msk (1UL << 3)
+#define ETHOSU_PMU_CCNT_Msk (1UL << 31)
+
+/*****************************************************************************
+ * Types
+ *****************************************************************************/
+
 /** \brief HW Supported ETHOSU PMU Events
  *
  * Note: These values are symbolic. Actual HW-values may change. I.e. always use API
@@ -112,193 +130,179 @@
     ETHOSU_PMU_SENTINEL // End-marker (not event)
 };
 
-#define ETHOSU_PMU_CNT1_Msk (1UL << 0)
-#define ETHOSU_PMU_CNT2_Msk (1UL << 1)
-#define ETHOSU_PMU_CNT3_Msk (1UL << 2)
-#define ETHOSU_PMU_CNT4_Msk (1UL << 3)
-#define ETHOSU_PMU_CCNT_Msk (1UL << 31)
-
-/* Transpose functions between HW-event-type and event-id*/
-enum ethosu_pmu_event_type pmu_event_type(uint32_t);
-uint32_t pmu_event_value(enum ethosu_pmu_event_type);
-
-/* Initialize the PMU driver */
-void ethosu_pmu_driver_init(void);
-
-void ethosu_pmu_driver_exit(void);
-
-// CMSIS ref API
-/** \brief PMU Functions */
+/*****************************************************************************
+ * Functions
+ *****************************************************************************/
 
 /**
-  \brief   Enable the PMU
-*/
+ * \brief   Enable the PMU
+ */
 void ETHOSU_PMU_Enable(void);
 
 /**
-  \brief   Disable the PMU
-*/
+ * \brief   Disable the PMU
+ */
 void ETHOSU_PMU_Disable(void);
 
 /**
-  \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
-*/
+ * \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);
 
 /**
-  \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
-*/
+ * \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);
 
 /**
-  \brief  Reset cycle counter
-*/
+ * \brief  Reset cycle counter
+ */
 void ETHOSU_PMU_CYCCNT_Reset(void);
 
 /**
-  \brief  Reset all event counters
-*/
+ * \brief  Reset all event counters
+ */
 void ETHOSU_PMU_EVCNTR_ALL_Reset(void);
 
 /**
-  \brief  Enable counters
-  \param [in]     mask    Counters to enable
-  \note   Enables one or more of the following:
-          - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
-          - cycle counter  (bit 31)
-*/
+ * \brief  Enable counters
+ * \param [in]     mask    Counters to enable
+ * \note   Enables one or more of the following:
+ *         - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
+ *         - cycle counter  (bit 31)
+ */
 void ETHOSU_PMU_CNTR_Enable(uint32_t mask);
 
 /**
-  \brief  Disable counters
-  \param [in]     mask    Counters to disable
-  \note   Disables one or more of the following:
-          - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
-          - cycle counter  (bit 31)
-*/
+ * \brief  Disable counters
+ * \param [in]     mask    Counters to disable
+ * \note   Disables one or more of the following:
+ *         - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
+ *         - cycle counter  (bit 31)
+ */
 void ETHOSU_PMU_CNTR_Disable(uint32_t mask);
 
 /**
-  \brief  Determine counters activation
-
-  \return                Event count
-  \param [in]     mask    Counters to enable
-  \return  a bitmask where bit-set means:
-          - event counters activated (bit 0-ETHOSU_PMU_NCOUNTERS)
-          - cycle counter  activate  (bit 31)
-  \note   ETHOSU specific. Usage breaks CMSIS complience
-*/
+ * \brief  Determine counters activation
+ *
+ * \return                Event count
+ * \param [in]     mask    Counters to enable
+ * \return  a bitmask where bit-set means:
+ *         - event counters activated (bit 0-ETHOSU_PMU_NCOUNTERS)
+ *         - cycle counter  activate  (bit 31)
+ * \note   ETHOSU specific. Usage breaks CMSIS complience
+ */
 uint32_t ETHOSU_PMU_CNTR_Status(void);
 
 /**
-  \brief  Read cycle counter (64 bit)
-  \return                 Cycle count
-  \note   Two HW 32-bit registers that can increment independently in-between reads.
-          To work-around raciness yet still avoid turning
-          off the event both are read as one value twice. If the latter read
-          is not greater than the former, it means overflow of LSW without
-          incrementing MSW has occurred, in which case the former value is used.
-*/
+ * \brief  Read cycle counter (64 bit)
+ * \return                 Cycle count
+ * \note   Two HW 32-bit registers that can increment independently in-between reads.
+ *         To work-around raciness yet still avoid turning
+ *         off the event both are read as one value twice. If the latter read
+ *         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);
 
 /**
-  \brief  Set cycle counter (64 bit)
-  \param [in]    val     Conter value
-  \note   Two HW 32-bit registers that can increment independently in-between reads.
-          To work-around raciness, counter is temporary disabled if enabled.
-  \note   ETHOSU specific. Usage breaks CMSIS complience
-*/
+ * \brief  Set cycle counter (64 bit)
+ * \param [in]    val     Conter value
+ * \note   Two HW 32-bit registers that can increment independently in-between reads.
+ *         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);
 
 /**
-  \brief   Read event counter
-  \param [in]    num     Event counter (0-ETHOSU_PMU_NCOUNTERS)
-  \return                Event count
-*/
+ * \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);
 
 /**
-  \brief   Set event counter value
-  \param [in]    num     Event counter (0-ETHOSU_PMU_NCOUNTERS)
-  \param [in]    val     Conter value
-  \note   ETHOSU specific. Usage breaks CMSIS complience
-*/
+ * \brief   Set event counter value
+ * \param [in]    num     Event counter (0-ETHOSU_PMU_NCOUNTERS)
+ * \param [in]    val     Conter value
+ * \note   ETHOSU specific. Usage breaks CMSIS complience
+ */
 void ETHOSU_PMU_Set_EVCNTR(uint32_t num, uint32_t val);
 
 /**
-  \brief   Read counter overflow status
-  \return  Counter overflow status bits for the following:
-           - event counters (bit 0-ETHOSU_PMU_NCOUNTERS))
-           - cycle counter  (bit 31)
-*/
+ * \brief   Read counter overflow status
+ * \return  Counter overflow status bits for the following:
+ *          - event counters (bit 0-ETHOSU_PMU_NCOUNTERS))
+ *          - cycle counter  (bit 31)
+ */
 uint32_t ETHOSU_PMU_Get_CNTR_OVS(void);
 
 /**
-  \brief   Clear counter overflow status
-  \param [in]     mask    Counter overflow status bits to clear
-  \note    Clears overflow status bits for one or more of the following:
-           - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
-           - cycle counter  (bit 31)
-*/
+ * \brief   Clear counter overflow status
+ * \param [in]     mask    Counter overflow status bits to clear
+ * \note    Clears overflow status bits for one or more of the following:
+ *          - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
+ *          - cycle counter  (bit 31)
+ */
 void ETHOSU_PMU_Set_CNTR_OVS(uint32_t mask);
 
 /**
-  \brief   Enable counter overflow interrupt request
-  \param [in]     mask    Counter overflow interrupt request bits to set
-  \note    Sets overflow interrupt request bits for one or more of the following:
-           - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
-           - cycle counter  (bit 31)
-*/
+ * \brief   Enable counter overflow interrupt request
+ * \param [in]     mask    Counter overflow interrupt request bits to set
+ * \note    Sets overflow interrupt request bits for one or more of the following:
+ *          - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
+ *          - cycle counter  (bit 31)
+ */
 void ETHOSU_PMU_Set_CNTR_IRQ_Enable(uint32_t mask);
 
 /**
-  \brief   Disable counter overflow interrupt request
-  \param [in]     mask    Counter overflow interrupt request bits to clear
-  \note    Clears overflow interrupt request bits for one or more of the following:
-           - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
-           - cycle counter  (bit 31)
-*/
+ * \brief   Disable counter overflow interrupt request
+ * \param [in]     mask    Counter overflow interrupt request bits to clear
+ * \note    Clears overflow interrupt request bits for one or more of the following:
+ *          - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
+ *          - cycle counter  (bit 31)
+ */
 void ETHOSU_PMU_Set_CNTR_IRQ_Disable(uint32_t mask);
 
 /**
-  \brief   Get counters overflow interrupt request stiinings
-  \return   mask    Counter overflow interrupt request bits
-  \note    Sets overflow interrupt request bits for one or more of the following:
-           - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
-           - cycle counter  (bit 31)
-  \note   ETHOSU specific. Usage breaks CMSIS compliance
-*/
+ * \brief   Get counters overflow interrupt request stiinings
+ * \return   mask    Counter overflow interrupt request bits
+ * \note    Sets overflow interrupt request bits for one or more of the following:
+ *          - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
+ *          - cycle counter  (bit 31)
+ * \note   ETHOSU specific. Usage breaks CMSIS compliance
+ */
 uint32_t ETHOSU_PMU_Get_IRQ_Enable(void);
 
 /**
-  \brief   Software increment event counter
-  \param [in]     mask    Counters to increment
-           - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
-           - cycle counter  (bit 31)
-  \note    Software increment bits for one or more event counters.
-*/
+ * \brief   Software increment event counter
+ * \param [in]     mask    Counters to increment
+ *          - event counters (bit 0-ETHOSU_PMU_NCOUNTERS)
+ *          - cycle counter  (bit 31)
+ * \note    Software increment bits for one or more event counters.
+ */
 void ETHOSU_PMU_CNTR_Increment(uint32_t mask);
 
 /**
-  \brief   Set start event number for the cycle counter
-  \param [in]   start_event   Event number
-            - Start event (bits [9:0])
-  \note   Sets the event number that starts the cycle counter.
-            - Event number in the range 0..1023
-*/
+ * \brief   Set start event number for the cycle counter
+ * \param [in]   start_event   Event number
+ *           - Start event (bits [9:0])
+ * \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);
 
 /**
-  \brief   Set stop event number for the cycle counter
-  \param [in]   stop_event   Event number
-            - Stop event (bits [25:16])
-  \note   Sets the event number that stops the cycle counter.
-            - Event number in the range 0..1023
-*/
+ * \brief   Set stop event number for the cycle counter
+ * \param [in]   stop_event   Event number
+ *           - Stop event (bits [25:16])
+ * \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);
 
 #ifdef __cplusplus
diff --git a/src/ethosu_common.h b/src/ethosu_common.h
index 04acea2..30f07be 100644
--- a/src/ethosu_common.h
+++ b/src/ethosu_common.h
@@ -24,7 +24,6 @@
  ******************************************************************************/
 
 #include "ethosu55_interface.h"
-#include "ethosu_device.h"
 
 #include <stdio.h>
 
diff --git a/src/ethosu_config.h b/src/ethosu_config.h
index 91fe660..7a9fae2 100644
--- a/src/ethosu_config.h
+++ b/src/ethosu_config.h
@@ -116,87 +116,4 @@
 #define BASE_POINTER_OFFSET 0
 #endif
 
-#ifdef PMU_AUTOINIT
-/*
- * Register control
- *  b0 = CNT_EN = Enable counters (RW)
- *  b1 = EVENT_CNT_RST = Reset event counters (WO)
- *  b2 = CYCLE_CNT_RST = Reset cycle counter (WO)
- *  b[15:11] = Number of event counters (RO)
- */
-#ifndef INIT_PMCR
-#define INIT_PMCR 0x0
-#endif
-
-/*
- * Bit k enables event counter k
- *  k=31 enables the cycle counter
- *  Read value is current status
- */
-#ifndef INIT_PMCNTENSET
-#define INIT_PMCNTENSET 0x0
-#endif
-
-/*
- * Bit k disables event counter k
- *  k=31 disables the cycle counter
- *  Read value is current status
- */
-#ifndef INIT_PMCNTENCLR
-#define INIT_PMCNTENCLR 0x0
-#endif
-
-/*
- * Overflow detection set
- *  Bit k is for counter k
- *  k=31 is cycle counter
- */
-#ifndef INIT_PMOVSSET
-#define INIT_PMOVSSET 0x0
-#endif
-
-/*
- * Overflow detection clear
- *  Bit k is for counter k
- *  k=31 is cycle counter
- */
-#ifndef INIT_PMOVSCLR
-#define INIT_PMOVSCLR 0x0
-#endif
-
-/*
- * Interrupt set
- *  Bit k is for counter k
- *  k=31 is cycle counter
- */
-#ifndef INIT_PMINTSET
-#define INIT_PMINTSET 0x0
-#endif
-
-/*
- * Interrupt clear
- *  Bit k is for counter k
- *  k=31 is cycle counter
- */
-#ifndef INIT_PMINTCLR
-#define INIT_PMINTCLR 0x8003
-#endif
-
-/* Cycle counter
- *  48 bits value
- */
-#ifndef INIT_PMCCNTR
-#define INIT_PMCCNTR 0x0
-#endif
-
-/*
- * b[9:0] Start Event – this event number starts the cycle counter
- * b[25:16] Stop Event – this event number stops the cycle counter
- */
-#ifndef INIT_PMCCNTR_CFG
-#define INIT_PMCCNTR_CFG 0x0
-#endif
-
-#endif /* #ifdef PMU_AUTOINIT */
-
 #endif /* #ifndef ETHOSU_CONFIG_H */
diff --git a/src/ethosu_device.c b/src/ethosu_device.c
index 85dc022..8fee3f4 100644
--- a/src/ethosu_device.c
+++ b/src/ethosu_device.c
@@ -37,7 +37,7 @@
 enum ethosu_error_codes ethosu_dev_init(struct ethosu_device *dev, const void *base_address)
 {
 #if !defined(ARM_NPU_STUB)
-    dev->base_address = (uintptr_t)base_address;
+    dev->base_address = (volatile uint32_t *)base_address;
     ethosu_save_pmu_config(dev);
 #else
     UNUSED(dev);
@@ -189,12 +189,21 @@
         // Register access not permitted
         return ETHOSU_GENERIC_FAILURE;
     }
+
     // Reset and set security level
     ethosu_write_reg(dev, NPU_REG_RESET, reset.word);
 
+    // Wait for reset to complete
     return_code = ethosu_wait_for_reset(dev);
 
+    // Save the proto register
     dev->reset = ethosu_read_reg(dev, NPU_REG_PROT);
+
+    // Soft reset will clear the PMU configuration and counters. The shadow PMU counters
+    // are cleared by saving the PMU counters to ram, which will read back zeros.
+    // The PMU configuration will be restored in the invoke function after power save
+    // has been disabled.
+    ethosu_save_pmu_counters(dev);
 #else
     UNUSED(dev);
 #endif
@@ -534,8 +543,9 @@
 {
 #if !defined(ARM_NPU_STUB)
     ASSERT(dev->base_address != 0);
+    ASSERT(address % 4 == 0);
 
-    volatile uint32_t *reg = (uint32_t *)(uintptr_t)(dev->base_address + address);
+    volatile uint32_t *reg = dev->base_address + address / sizeof(uint32_t);
     return *reg;
 #else
     UNUSED(dev);
@@ -549,8 +559,9 @@
 {
 #if !defined(ARM_NPU_STUB)
     ASSERT(dev->base_address != 0);
+    ASSERT(address % 4 == 0);
 
-    volatile uint32_t *reg = (uint32_t *)(uintptr_t)(dev->base_address + address);
+    volatile uint32_t *reg = dev->base_address + address / sizeof(uint32_t);
     *reg                   = value;
 #else
     UNUSED(dev);
@@ -559,6 +570,12 @@
 #endif
 }
 
+void ethosu_write_reg_shadow(struct ethosu_device *dev, uint32_t address, uint32_t value, uint32_t *shadow)
+{
+    ethosu_write_reg(dev, address, value);
+    *shadow = ethosu_read_reg(dev, address);
+}
+
 enum ethosu_error_codes ethosu_save_pmu_config(struct ethosu_device *dev)
 {
 #if !defined(ARM_NPU_STUB)
@@ -574,13 +591,9 @@
     // Save start and stop event
     dev->pmccntr_cfg = ethosu_read_reg(dev, NPU_REG_PMCCNTR_CFG);
 
-    // Save the cycle counter
-    dev->pmccntr = ETHOSU_PMU_Get_CCNTR();
-
     // Save the event settings and counters
     for (uint32_t i = 0; i < ETHOSU_PMU_NCOUNTERS; i++)
     {
-        dev->pmu_evcntr[i] = ethosu_read_reg(dev, NPU_REG_PMEVCNTR0 + i * sizeof(uint32_t));
         dev->pmu_evtypr[i] = ethosu_read_reg(dev, NPU_REG_PMEVTYPER0 + i * sizeof(uint32_t));
     }
 #else
@@ -605,13 +618,9 @@
     // Restore start and stop event
     ethosu_write_reg(dev, NPU_REG_PMCCNTR_CFG, dev->pmccntr_cfg);
 
-    // Restore the cycle counter
-    ETHOSU_PMU_Set_CCNTR(dev->pmccntr);
-
-    // Restore event settings and counters
+    // Save the event settings and counters
     for (uint32_t i = 0; i < ETHOSU_PMU_NCOUNTERS; i++)
     {
-        ethosu_write_reg(dev, NPU_REG_PMEVCNTR0 + i * sizeof(uint32_t), dev->pmu_evcntr[i]);
         ethosu_write_reg(dev, NPU_REG_PMEVTYPER0 + i * sizeof(uint32_t), dev->pmu_evtypr[i]);
     }
 #else
@@ -621,6 +630,25 @@
     return ETHOSU_SUCCESS;
 }
 
+enum ethosu_error_codes ethosu_save_pmu_counters(struct ethosu_device *dev)
+{
+#if !defined(ARM_NPU_STUB)
+    // Save the cycle counter
+    dev->pmccntr[0] = ethosu_read_reg(dev, NPU_REG_PMCCNTR_LO);
+    dev->pmccntr[1] = ethosu_read_reg(dev, NPU_REG_PMCCNTR_HI);
+
+    // Save the event settings and counters
+    for (uint32_t i = 0; i < ETHOSU_PMU_NCOUNTERS; i++)
+    {
+        dev->pmu_evcntr[i] = ethosu_read_reg(dev, NPU_REG_PMEVCNTR0 + i * sizeof(uint32_t));
+    }
+#else
+    UNUSED(dev);
+#endif
+
+    return ETHOSU_SUCCESS;
+}
+
 bool ethosu_status_has_error(struct ethosu_device *dev)
 {
     bool status_error = false;
diff --git a/src/ethosu_driver.c b/src/ethosu_driver.c
index 6c55b0b..77b8727 100644
--- a/src/ethosu_driver.c
+++ b/src/ethosu_driver.c
@@ -149,7 +149,7 @@
  ******************************************************************************/
 
 struct ethosu_driver ethosu_drv = {
-    .dev = {.base_address = NULL, .reset = 0, .pmccntr = 0, .pmu_evcntr = {0, 0, 0, 0}, .pmu_evtypr = {0, 0, 0, 0}},
+    .dev = {.base_address = NULL, .reset = 0, .pmccntr = {0}, .pmu_evcntr = {0, 0, 0, 0}, .pmu_evtypr = {0, 0, 0, 0}},
     .abort_inference = false,
     .status_error    = false};
 
@@ -419,7 +419,7 @@
 
     if (!ethosu_drv.status_error)
     {
-        ethosu_save_pmu_config(&ethosu_drv.dev);
+        ethosu_save_pmu_counters(&ethosu_drv.dev);
         ethosu_set_clock_and_power(&ethosu_drv.dev, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_ENABLE);
     }
 
@@ -580,17 +580,14 @@
         return -1;
     }
 
-    // ETHOSU would have been reset in the IRQ handler if there were
-    // status error(s). So don't read the QREAD register.
     (void)ethosu_get_qread(&drv->dev, &qread);
     if (qread != cms_bytes)
     {
-        LOG_ERR(
+        LOG_WARN(
             "Failure: IRQ received but qread (%" PRIu32 ") not at end of stream (%" PRIu32 ").\n", qread, cms_bytes);
         return -1;
     }
 
-    // TODO Power off
     return 0;
 }
 
diff --git a/src/ethosu_pmu.c b/src/ethosu_pmu.c
index 7ca35fc..f7cb957 100644
--- a/src/ethosu_pmu.c
+++ b/src/ethosu_pmu.c
@@ -42,8 +42,6 @@
 
 #define EVID(A, name) (PMU_EVENT_TYPE_##name)
 
-#define ETHOSU_PMCCNTR_CFG_START_STOP_EVENT_MASK (0x3FF)
-
 #define NPU_REG_PMEVCNTR(x) (NPU_REG_PMEVCNTR0 + ((x) * sizeof(uint32_t)))
 #define NPU_REG_PMEVTYPER(x) (NPU_REG_PMEVTYPER0 + ((x) * sizeof(uint32_t)))
 
@@ -51,20 +49,13 @@
  * Variables
  *****************************************************************************/
 
-/**
- *  NOTE: A pointer to ethosu_driver will be added to the PMU functions
- * when multi-NPU functionality is implemented later. We shall use a
- * shared ethosu_driver instance till then.
- * */
-extern struct ethosu_driver ethosu_drv;
-
 static const enum pmu_event_type eventbyid[] = {EXPAND_PMU_EVENT_TYPE(EVID, COMMA)};
 
 /*****************************************************************************
- * Functions
+ * Static functions
  *****************************************************************************/
 
-enum ethosu_pmu_event_type pmu_event_type(uint32_t id)
+static enum ethosu_pmu_event_type pmu_event_type(uint32_t id)
 {
     switch (id)
     {
@@ -76,7 +67,7 @@
     return ETHOSU_PMU_SENTINEL;
 }
 
-uint32_t pmu_event_value(enum ethosu_pmu_event_type event)
+static uint32_t pmu_event_value(enum ethosu_pmu_event_type event)
 {
     int a = event;
     if ((a < ETHOSU_PMU_SENTINEL) && (a >= ETHOSU_PMU_NO_EVENT))
@@ -89,29 +80,9 @@
     }
 }
 
-void ethosu_pmu_driver_init(void)
-{
-#ifdef PMU_AUTOINIT
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCR, INIT_PMCR);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCNTENSET, INIT_PMCNTENSET);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCNTENCLR, INIT_PMCNTENCLR);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMOVSSET, INIT_PMOVSSET);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMOVSCLR, INIT_PMOVSCLR);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMINTSET, INIT_PMINTSET);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMINTCLR, INIT_PMINTCLR);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_LO, INIT_PMCCNTR);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_HI, INIT_PMCCNTR);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_CFG, INIT_PMCCNTR_CFG);
-
-    for (int i = 0; i < ETHOSU_PMU_NCOUNTERS; i++)
-    {
-        ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMEVCNTR(i), 0);
-        ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMEVTYPER(i), 0);
-    }
-#endif
-}
-
-void ethosu_pmu_driver_exit(void) {}
+/*****************************************************************************
+ * Functions
+ *****************************************************************************/
 
 void ETHOSU_PMU_Enable(void)
 {
@@ -119,8 +90,7 @@
     struct pmcr_r pmcr;
     pmcr.word   = ethosu_drv.dev.pmcr;
     pmcr.cnt_en = 1;
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCR, pmcr.word);
-    ethosu_drv.dev.pmcr = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMCR);
+    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCR, pmcr.word, &ethosu_drv.dev.pmcr);
 }
 
 void ETHOSU_PMU_Disable(void)
@@ -129,8 +99,7 @@
     struct pmcr_r pmcr;
     pmcr.word   = ethosu_drv.dev.pmcr;
     pmcr.cnt_en = 0;
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCR, pmcr.word);
-    ethosu_drv.dev.pmcr = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMCR);
+    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCR, pmcr.word, &ethosu_drv.dev.pmcr);
 }
 
 void ETHOSU_PMU_Set_EVTYPER(uint32_t num, enum ethosu_pmu_event_type type)
@@ -138,14 +107,13 @@
     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(&ethosu_drv.dev, NPU_REG_PMEVTYPER(num), val);
-    ethosu_drv.dev.pmu_evtypr[num] = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMEVTYPER(num));
+    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMEVTYPER(num), val, &ethosu_drv.dev.pmu_evtypr[num]);
 }
 
 enum ethosu_pmu_event_type ETHOSU_PMU_Get_EVTYPER(uint32_t num)
 {
     ASSERT(num < ETHOSU_PMU_NCOUNTERS);
-    uint32_t val                    = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMEVTYPER(num));
+    uint32_t val                    = ethosu_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;
@@ -157,9 +125,9 @@
     struct pmcr_r pmcr;
     pmcr.word          = ethosu_drv.dev.pmcr;
     pmcr.cycle_cnt_rst = 1;
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCR, pmcr.word);
-    ethosu_drv.dev.pmcr    = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMCR);
-    ethosu_drv.dev.pmccntr = 0;
+    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;
 }
 
 void ETHOSU_PMU_EVCNTR_ALL_Reset(void)
@@ -168,8 +136,7 @@
     struct pmcr_r pmcr;
     pmcr.word          = ethosu_drv.dev.pmcr;
     pmcr.event_cnt_rst = 1;
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCR, pmcr.word);
-    ethosu_drv.dev.pmcr = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMCR);
+    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCR, pmcr.word, &ethosu_drv.dev.pmcr);
 
     for (uint32_t i = 0; i < ETHOSU_PMU_NCOUNTERS; i++)
     {
@@ -180,55 +147,59 @@
 void ETHOSU_PMU_CNTR_Enable(uint32_t mask)
 {
     LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, mask);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCNTENSET, mask);
-    ethosu_drv.dev.pmcnten = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMCNTENSET);
+    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCNTENSET, mask, &ethosu_drv.dev.pmcnten);
 }
 
 void ETHOSU_PMU_CNTR_Disable(uint32_t mask)
 {
     LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, mask);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCNTENCLR, mask);
-    ethosu_drv.dev.pmcnten = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMCNTENSET);
+    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMCNTENCLR, mask, &ethosu_drv.dev.pmcnten);
 }
 
 uint32_t ETHOSU_PMU_CNTR_Status(void)
 {
-    uint32_t val = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMCNTENSET);
-    LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, val);
-    return val;
+    LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, ethosu_drv.dev.pmcnten);
+    return ethosu_drv.dev.pmcnten;
 }
 
 uint64_t ETHOSU_PMU_Get_CCNTR(void)
 {
-    uint64_t val = (((uint64_t)ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_HI)) << 32) |
-                   ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_LO);
+    uint32_t val_lo = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_HI);
+    uint32_t val_hi = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_LO);
+    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];
 
-    LOG_DEBUG("%s: val=%llu, pmccntr=%llu\n", __FUNCTION__, val, ethosu_drv.dev.pmccntr);
+    LOG_DEBUG("%s: val=%llu, shadow=%llu\n", __FUNCTION__, val, shadow);
 
-    // Return the cached value in case the NPU was powered off
-    if (ethosu_drv.dev.pmccntr > val)
+    // Return the shadow variable in case the NPU was powered off and lost the cycle count
+    if (shadow > val)
     {
-        return ethosu_drv.dev.pmccntr;
+        return shadow;
     }
 
+    // Update the shadow variable
+    ethosu_drv.dev.pmccntr[0] = val_lo;
+    ethosu_drv.dev.pmccntr[1] = val_hi;
+
     return val;
 }
 
 void ETHOSU_PMU_Set_CCNTR(uint64_t val)
 {
-    uint32_t mask = ETHOSU_PMU_CNTR_Status();
+    uint32_t active = ETHOSU_PMU_CNTR_Status() & ETHOSU_PMU_CCNT_Msk;
 
     LOG_DEBUG("%s: val=%llu\n", __FUNCTION__, val);
 
-    if (mask & ETHOSU_PMU_CCNT_Msk)
+    if (active)
     {
         ETHOSU_PMU_CNTR_Disable(ETHOSU_PMU_CCNT_Msk);
     }
 
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_LO, (val & MASK_0_31_BITS));
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_HI, (val & MASK_32_47_BITS) >> 32);
+    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]);
 
-    if (mask & ETHOSU_PMU_CCNT_Msk)
+    if (active)
     {
         ETHOSU_PMU_CNTR_Enable(ETHOSU_PMU_CCNT_Msk);
     }
@@ -238,14 +209,17 @@
 {
     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, pmu_evcntr=%u\n", __FUNCTION__, num, val, ethosu_drv.dev.pmu_evcntr[num]);
+    LOG_DEBUG("%s: num=%u, val=%u, shadow=%u\n", __FUNCTION__, num, val, ethosu_drv.dev.pmu_evcntr[num]);
 
-    // Return the cached value in case the NPU was powered off
+    // Return the shadow variable in case the NPU was powered off and lost the event count
     if (ethosu_drv.dev.pmu_evcntr[num] > val)
     {
         return ethosu_drv.dev.pmu_evcntr[num];
     }
 
+    // Update the shadow variable
+    ethosu_drv.dev.pmu_evcntr[num] = val;
+
     return val;
 }
 
@@ -262,8 +236,6 @@
     return ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMOVSSET);
 }
 
-// TODO: check if this function name match with the description &
-// implementation.
 void ETHOSU_PMU_Set_CNTR_OVS(uint32_t mask)
 {
     LOG_DEBUG("%s:\n", __FUNCTION__);
@@ -273,22 +245,19 @@
 void ETHOSU_PMU_Set_CNTR_IRQ_Enable(uint32_t mask)
 {
     LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, mask);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMINTSET, mask);
-    ethosu_drv.dev.pmint = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMINTSET);
+    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMINTSET, mask, &ethosu_drv.dev.pmint);
 }
 
 void ETHOSU_PMU_Set_CNTR_IRQ_Disable(uint32_t mask)
 {
     LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, mask);
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMINTCLR, mask);
-    ethosu_drv.dev.pmint = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMINTSET);
+    ethosu_write_reg_shadow(&ethosu_drv.dev, NPU_REG_PMINTCLR, mask, &ethosu_drv.dev.pmint);
 }
 
 uint32_t ETHOSU_PMU_Get_IRQ_Enable(void)
 {
-    uint32_t mask = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMINTSET);
-    LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, mask);
-    return mask;
+    LOG_DEBUG("%s: mask=0x%08x\n", __FUNCTION__, ethosu_drv.dev.pmint);
+    return ethosu_drv.dev.pmint;
 }
 
 void ETHOSU_PMU_CNTR_Increment(uint32_t mask)
@@ -296,49 +265,45 @@
     LOG_DEBUG("%s:\n", __FUNCTION__);
     uint32_t cntrs_active = ETHOSU_PMU_CNTR_Status();
 
+    // Disable counters
+    ETHOSU_PMU_CNTR_Disable(mask);
+
+    // Increment cycle counter
     if (mask & ETHOSU_PMU_CCNT_Msk)
     {
-        if (mask & ETHOSU_PMU_CCNT_Msk)
-        {
-            ETHOSU_PMU_CNTR_Disable(ETHOSU_PMU_CCNT_Msk);
-            uint64_t val = ETHOSU_PMU_Get_CCNTR() + 1;
-            ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_LO, (val & MASK_0_31_BITS));
-            ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_HI, (val & MASK_32_47_BITS) >> 32);
-            if (cntrs_active & ETHOSU_PMU_CCNT_Msk)
-            {
-                ETHOSU_PMU_CNTR_Enable(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]);
     }
+
     for (int i = 0; i < ETHOSU_PMU_NCOUNTERS; i++)
     {
-        uint32_t cntr = (0x0001 << i);
-
-        if (mask & cntr)
+        if (mask & (1 << i))
         {
-            ETHOSU_PMU_CNTR_Disable(cntr);
-            uint32_t val = ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PMEVCNTR(i));
-            ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMEVCNTR(i), val + 1);
-            if (cntrs_active & cntr)
-            {
-                ETHOSU_PMU_CNTR_Enable(cntr);
-            }
+            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]);
         }
     }
+
+    // Reenable the active counters
+    ETHOSU_PMU_CNTR_Enable(cntrs_active);
 }
 
 void ETHOSU_PMU_PMCCNTR_CFG_Set_Start_Event(uint32_t start_event)
 {
     LOG_DEBUG("%s: start_event=%u\n", __FUNCTION__, start_event);
-    struct pmccntr_cfg_r *cfg = (struct pmccntr_cfg_r *)&ethosu_drv.dev.pmccntr_cfg;
-    cfg->CYCLE_CNT_CFG_START  = start_event & ETHOSU_PMCCNTR_CFG_START_STOP_EVENT_MASK;
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_CFG, cfg->word);
+    struct pmccntr_cfg_r cfg;
+    cfg.word                = ethosu_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);
 }
 
 void ETHOSU_PMU_PMCCNTR_CFG_Set_Stop_Event(uint32_t stop_event)
 {
     LOG_DEBUG("%s: stop_event=%u\n", __FUNCTION__, stop_event);
-    struct pmccntr_cfg_r *cfg = (struct pmccntr_cfg_r *)&ethosu_drv.dev.pmccntr_cfg;
-    cfg->CYCLE_CNT_CFG_STOP   = stop_event & ETHOSU_PMCCNTR_CFG_START_STOP_EVENT_MASK;
-    ethosu_write_reg(&ethosu_drv.dev, NPU_REG_PMCCNTR_CFG, cfg->word);
+    struct pmccntr_cfg_r cfg;
+    cfg.word               = ethosu_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);
 }