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/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);
 }