MLBEDSW-3094 Improve soft reset of ETHOSU when running jobs

The ETHOSU is soft-reset only if the previous job failed or if
the current privilege level is not user or if the current security
level is not secure.

Change-Id: Id10b96058d67805d179ac693537606d55e10379b
diff --git a/include/ethosu_device.h b/include/ethosu_device.h
index e08aa3d..5edba94 100644
--- a/include/ethosu_device.h
+++ b/include/ethosu_device.h
@@ -55,6 +55,7 @@
 struct ethosu_device
 {
     uintptr_t base_address;
+    uint32_t reset;
     uint32_t pmcr;
     uint64_t pmccntr;
     uint32_t pmcnten;
@@ -392,6 +393,14 @@
  */
 enum ethosu_error_codes ethosu_restore_pmu_config(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,
+ *                             false otherwise.
+ */
+bool ethosu_status_has_error(struct ethosu_device *dev);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/ethosu_driver.h b/include/ethosu_driver.h
index d6e9c6d..2594fbe 100644
--- a/include/ethosu_driver.h
+++ b/include/ethosu_driver.h
@@ -43,6 +43,7 @@
     bool abort_inference;
     uint64_t fast_memory;
     size_t fast_memory_size;
+    bool status_error;
 };
 
 struct ethosu_version_id
diff --git a/src/ethosu_device.c b/src/ethosu_device.c
index 973caad..85dc022 100644
--- a/src/ethosu_device.c
+++ b/src/ethosu_device.c
@@ -193,6 +193,8 @@
     ethosu_write_reg(dev, NPU_REG_RESET, reset.word);
 
     return_code = ethosu_wait_for_reset(dev);
+
+    dev->reset = ethosu_read_reg(dev, NPU_REG_PROT);
 #else
     UNUSED(dev);
 #endif
@@ -618,3 +620,17 @@
 
     return ETHOSU_SUCCESS;
 }
+
+bool ethosu_status_has_error(struct ethosu_device *dev)
+{
+    bool status_error = false;
+#if !defined(ARM_NPU_STUB)
+    struct status_r status;
+    status.word  = ethosu_read_reg(dev, NPU_REG_STATUS);
+    status_error = ((1 == status.bus_status) || (1 == status.cmd_parse_error) || (1 == status.wd_fault) ||
+                    (1 == status.ecc_fault));
+#else
+    UNUSED(dev);
+#endif
+    return status_error;
+}
diff --git a/src/ethosu_driver.c b/src/ethosu_driver.c
index 2a2423f..6c55b0b 100644
--- a/src/ethosu_driver.c
+++ b/src/ethosu_driver.c
@@ -149,8 +149,9 @@
  ******************************************************************************/
 
 struct ethosu_driver ethosu_drv = {
-    .dev             = {.base_address = 0, .pmccntr = 0, .pmu_evcntr = {0, 0, 0, 0}, .pmu_evtypr = {0, 0, 0, 0}},
-    .abort_inference = false};
+    .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};
 
 // IRQ
 static volatile bool irq_triggered = false;
@@ -172,9 +173,15 @@
     // Clear interrupt
     (void)ethosu_clear_irq_status(&ethosu_drv.dev);
 
-    // Verify that interrupt has been successfully cleard
+    // Verify that interrupt has been successfully cleared
     (void)ethosu_is_irq_raised(&ethosu_drv.dev, &irq_raised);
     ASSERT(irq_raised == 0);
+
+    if (ethosu_status_has_error(&ethosu_drv.dev))
+    {
+        ethosu_soft_reset(&ethosu_drv.dev);
+        ethosu_drv.status_error = true;
+    }
 }
 
 static inline void wait_for_irq(struct ethosu_driver *drv)
@@ -257,6 +264,7 @@
         LOG_ERR("Failed reset of Ethos-U\n");
         return -1;
     }
+    ethosu_drv.status_error = false;
 
     return return_code;
 }
@@ -339,7 +347,11 @@
         }
     }
 
-    ethosu_soft_reset(&ethosu_drv.dev);
+    if (ethosu_drv.dev.reset != ethosu_read_reg(&ethosu_drv.dev, NPU_REG_PROT))
+    {
+        ethosu_soft_reset(&ethosu_drv.dev);
+    }
+    ethosu_drv.status_error = false;
     ethosu_set_clock_and_power(&ethosu_drv.dev, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_DISABLE);
     ethosu_restore_pmu_config(&ethosu_drv.dev);
 
@@ -405,8 +417,11 @@
         }
     }
 
-    ethosu_save_pmu_config(&ethosu_drv.dev);
-    ethosu_set_clock_and_power(&ethosu_drv.dev, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_ENABLE);
+    if (!ethosu_drv.status_error)
+    {
+        ethosu_save_pmu_config(&ethosu_drv.dev);
+        ethosu_set_clock_and_power(&ethosu_drv.dev, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_ENABLE);
+    }
 
     return return_code;
 }
@@ -560,6 +575,13 @@
 
     wait_for_irq(drv);
 
+    if (drv->status_error)
+    {
+        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)
     {