dev-arm: Implement LevelSensitive SPIs in GICv3
authorGiacomo Travaglini <giacomo.travaglini@arm.com>
Fri, 17 Jul 2020 12:25:04 +0000 (13:25 +0100)
committerGiacomo Travaglini <giacomo.travaglini@arm.com>
Thu, 23 Jul 2020 16:51:03 +0000 (16:51 +0000)
Change-Id: If918a8aea934f0037818cc64bf458076bfd0251d
Signed-off-by: Giacomo Travaglini <giacomo.travaglini@arm.com>
Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/31515
Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
Maintainer: Andreas Sandberg <andreas.sandberg@arm.com>
Tested-by: kokoro <noreply+kokoro@google.com>
src/dev/arm/gic_v3.cc
src/dev/arm/gic_v3_distributor.cc
src/dev/arm/gic_v3_distributor.hh

index 575194004930d00deb4975d0fa1d958b9d886713..8e75b1c5bad5f17886d072b10d841c6c9c54b072 100644 (file)
@@ -177,9 +177,10 @@ Gicv3::sendInt(uint32_t int_id)
 }
 
 void
-Gicv3::clearInt(uint32_t number)
+Gicv3::clearInt(uint32_t int_id)
 {
-    distributor->deassertSPI(number);
+    DPRINTF(Interrupt, "Gicv3::clearInt(): received SPI %d\n", int_id);
+    distributor->clearInt(int_id);
 }
 
 void
index f8605da27308ab8bbb3f6782923f3743aa192d87..27f404b2bf4a1bf2482c32f922b8704a6cb0b81c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 ARM Limited
+ * Copyright (c) 2019-2020 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -74,6 +74,7 @@ Gicv3Distributor::Gicv3Distributor(Gicv3 * gic, uint32_t it_lines)
       irqGroup(it_lines, 0),
       irqEnabled(it_lines, false),
       irqPending(it_lines, false),
+      irqPendingIspendr(it_lines, false),
       irqActive(it_lines, false),
       irqPriority(it_lines, 0xAA),
       irqConfig(it_lines, Gicv3::INT_LEVEL_SENSITIVE),
@@ -608,6 +609,7 @@ Gicv3Distributor::write(Addr addr, uint64_t data, size_t size,
                 DPRINTF(GIC, "Gicv3Distributor::write() (GICD_ISPENDR): "
                         "int_id %d (SPI) pending bit set\n", int_id);
                 irqPending[int_id] = true;
+                irqPendingIspendr[int_id] = true;
             }
         }
 
@@ -634,7 +636,7 @@ Gicv3Distributor::write(Addr addr, uint64_t data, size_t size,
 
             bool clear = data & (1 << i) ? 1 : 0;
 
-            if (clear) {
+            if (clear && treatAsEdgeTriggered(int_id)) {
                 irqPending[int_id] = false;
                 clearIrqCpuInterface(int_id);
             }
@@ -1000,11 +1002,22 @@ Gicv3Distributor::sendInt(uint32_t int_id)
     panic_if(int_id < Gicv3::SGI_MAX + Gicv3::PPI_MAX, "Invalid SPI!");
     panic_if(int_id > itLines, "Invalid SPI!");
     irqPending[int_id] = true;
+    irqPendingIspendr[int_id] = false;
     DPRINTF(GIC, "Gicv3Distributor::sendInt(): "
             "int_id %d (SPI) pending bit set\n", int_id);
     update();
 }
 
+void
+Gicv3Distributor::clearInt(uint32_t int_id)
+{
+    // Edge-triggered interrupts remain pending until software
+    // writes GICD_ICPENDR, GICD_CLRSPI_* or activates them via ICC_IAR
+    if (isLevelSensitive(int_id)) {
+        deassertSPI(int_id);
+    }
+}
+
 void
 Gicv3Distributor::deassertSPI(uint32_t int_id)
 {
@@ -1145,7 +1158,9 @@ Gicv3Distributor::getIntGroup(int int_id) const
 void
 Gicv3Distributor::activateIRQ(uint32_t int_id)
 {
-    irqPending[int_id] = false;
+    if (treatAsEdgeTriggered(int_id)) {
+        irqPending[int_id] = false;
+    }
     irqActive[int_id] = true;
 }
 
@@ -1166,6 +1181,7 @@ Gicv3Distributor::serialize(CheckpointOut & cp) const
     SERIALIZE_CONTAINER(irqGroup);
     SERIALIZE_CONTAINER(irqEnabled);
     SERIALIZE_CONTAINER(irqPending);
+    SERIALIZE_CONTAINER(irqPendingIspendr);
     SERIALIZE_CONTAINER(irqActive);
     SERIALIZE_CONTAINER(irqPriority);
     SERIALIZE_CONTAINER(irqConfig);
@@ -1185,6 +1201,7 @@ Gicv3Distributor::unserialize(CheckpointIn & cp)
     UNSERIALIZE_CONTAINER(irqGroup);
     UNSERIALIZE_CONTAINER(irqEnabled);
     UNSERIALIZE_CONTAINER(irqPending);
+    UNSERIALIZE_CONTAINER(irqPendingIspendr);
     UNSERIALIZE_CONTAINER(irqActive);
     UNSERIALIZE_CONTAINER(irqPriority);
     UNSERIALIZE_CONTAINER(irqConfig);
index 62bed186d590c235ad674f5203094240ca963ffb..99b65ed9a85e0f77a10ddbfc39c85c4a8bcc3aa1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 ARM Limited
+ * Copyright (c) 2019-2020 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -151,6 +151,7 @@ class Gicv3Distributor : public Serializable
     std::vector <uint8_t> irqGroup;
     std::vector <bool> irqEnabled;
     std::vector <bool> irqPending;
+    std::vector <bool> irqPendingIspendr;
     std::vector <bool> irqActive;
     std::vector <uint8_t> irqPriority;
     std::vector <Gicv3::IntTriggerType> irqConfig;
@@ -222,6 +223,27 @@ class Gicv3Distributor : public Serializable
         }
     }
 
+    bool isLevelSensitive(uint32_t int_id) const
+    {
+        return irqConfig[int_id] == Gicv3::INT_LEVEL_SENSITIVE;
+    }
+
+    /**
+     * This helper is used to check if an interrupt should be treated as
+     * edge triggered in the following scenarios:
+     *
+     * a) While activating the interrupt
+     * b) While clearing an interrupt via ICPENDR
+     *
+     * In fact, in these two situations, a level sensitive interrupt
+     * which had been made pending via a write to ISPENDR, will be
+     * treated as it if was edge triggered.
+     */
+    bool treatAsEdgeTriggered(uint32_t int_id) const
+    {
+        return !isLevelSensitive(int_id) || irqPendingIspendr[int_id];
+    }
+
     inline bool nsAccessToSecInt(uint32_t int_id, bool is_secure_access) const
     {
         return !DS && !is_secure_access && getIntGroup(int_id) != Gicv3::G1NS;
@@ -236,11 +258,12 @@ class Gicv3Distributor : public Serializable
 
     Gicv3Distributor(Gicv3 * gic, uint32_t it_lines);
 
+    void sendInt(uint32_t int_id);
+    void clearInt(uint32_t int_id);
     void deassertSPI(uint32_t int_id);
     void clearIrqCpuInterface(uint32_t int_id);
     void init();
     uint64_t read(Addr addr, size_t size, bool is_secure_access);
-    void sendInt(uint32_t int_id);
     void write(Addr addr, uint64_t data, size_t size,
                bool is_secure_access);
 };