Add plugin function for PLL unlock clock callouts

Change-Id: Ic0e9c9d7fee7afb5a6534266ab011d9d41d9ca48
Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
diff --git a/analyzer/plugins/p10-plugins.cpp b/analyzer/plugins/p10-plugins.cpp
index dd14457..7e20d2d 100644
--- a/analyzer/plugins/p10-plugins.cpp
+++ b/analyzer/plugins/p10-plugins.cpp
@@ -1,5 +1,7 @@
 
 #include <analyzer/plugins/plugin.hpp>
+#include <hei_util.hpp>
+#include <util/pdbg.hpp>
 #include <util/trace.hpp>
 
 namespace analyzer
@@ -17,10 +19,44 @@
  *  clock, the clock is called out with high priority. Otherwise, the clock
  *  callout priority is medium.
  */
-void pll_unlock(unsigned int i_instance, const libhei::Chip&, ServiceData&)
+void pll_unlock(unsigned int i_instance, const libhei::Chip&,
+                ServiceData& io_servData)
 {
-    // TODO
-    trace::inf("pll_unlock plugin: i_instance=%u", i_instance);
+    auto nodeId = libhei::hash<libhei::NodeId_t>("PLL_UNLOCK");
+
+    auto sigList = io_servData.getIsolationData().getSignatureList();
+
+    // The PLL list is initially the same size of the signature list.
+    std::vector<libhei::Signature> pllList{sigList.size()};
+
+    // Copy all signatures that match the node ID and bit position. Note that
+    // in this case the bit position is the same as the plugin instance.
+    auto itr = std::copy_if(sigList.begin(), sigList.end(), pllList.begin(),
+                            [&nodeId, &i_instance](const auto& s) {
+                                return (nodeId == s.getId() &&
+                                        i_instance == s.getBit());
+                            });
+
+    // Shrink the size of the PLL list if necessary.
+    pllList.resize(std::distance(pllList.begin(), itr));
+
+    // The clock callout priority is dependent on the number of chips with PLL
+    // unlock attentions.
+    auto clockPriority =
+        (1 < pllList.size()) ? callout::Priority::HIGH : callout::Priority::MED;
+
+    // Callout the clock.
+    auto clockCallout = (0 == i_instance) ? callout::ClockType::OSC_REF_CLOCK_0
+                                          : callout::ClockType::OSC_REF_CLOCK_1;
+    io_servData.calloutClock(clockCallout, clockPriority, true);
+
+    // Callout the processors connected to this clock that are reporting PLL
+    // unlock attentions. Always a medium callout and no guarding.
+    for (const auto& sig : pllList)
+    {
+        io_servData.calloutTarget(util::pdbg::getTrgt(sig.getChip()),
+                                  callout::Priority::MED, false);
+    }
 }
 
 } // namespace P10
diff --git a/analyzer/ras-data/data/ras-data-p10-10.json b/analyzer/ras-data/data/ras-data-p10-10.json
index d31ddc5..3c8f88c 100644
--- a/analyzer/ras-data/data/ras-data-p10-10.json
+++ b/analyzer/ras-data/data/ras-data-p10-10.json
@@ -936,12 +936,10 @@
             { "type": "action", "name": "self_L" }
         ],
         "pll_unlock_0": [
-            { "type": "plugin", "name": "pll_unlock", "instance": 0 },
-            { "type": "callout_self", "priority": "MED", "guard": false }
+            { "type": "plugin", "name": "pll_unlock", "instance": 0 }
         ],
         "pll_unlock_1": [
-            { "type": "plugin", "name": "pll_unlock", "instance": 1 },
-            { "type": "callout_self", "priority": "MED", "guard": false }
+            { "type": "plugin", "name": "pll_unlock", "instance": 1 }
         ],
         "pcb_slave_parity": [
             { "type": "action", "name": "self_H" }
diff --git a/analyzer/ras-data/data/ras-data-p10-20.json b/analyzer/ras-data/data/ras-data-p10-20.json
index 8ed5f00..a9dc314 100644
--- a/analyzer/ras-data/data/ras-data-p10-20.json
+++ b/analyzer/ras-data/data/ras-data-p10-20.json
@@ -936,12 +936,10 @@
             { "type": "action", "name": "self_L" }
         ],
         "pll_unlock_0": [
-            { "type": "plugin", "name": "pll_unlock", "instance": 0 },
-            { "type": "callout_self", "priority": "MED", "guard": false }
+            { "type": "plugin", "name": "pll_unlock", "instance": 0 }
         ],
         "pll_unlock_1": [
-            { "type": "plugin", "name": "pll_unlock", "instance": 1 },
-            { "type": "callout_self", "priority": "MED", "guard": false }
+            { "type": "plugin", "name": "pll_unlock", "instance": 1 }
         ],
         "pcb_slave_parity": [
             { "type": "action", "name": "self_H" }
diff --git a/test/meson.build b/test/meson.build
index 81ca2e2..45c5749 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -38,9 +38,11 @@
   'bin_stream_test',
   'ffdc_file_test',
   'resolution_test',
+  'test-pll-unlock',
 ]
 
 analyzer_src = files(
+  '../analyzer/plugins/p10-plugins.cpp',
   '../analyzer/service_data.cpp',
   '../analyzer/resolution.cpp',
 )
diff --git a/test/test-pll-unlock.cpp b/test/test-pll-unlock.cpp
new file mode 100644
index 0000000..c09bf90
--- /dev/null
+++ b/test/test-pll-unlock.cpp
@@ -0,0 +1,149 @@
+#include <stdio.h>
+
+#include <analyzer/plugins/plugin.hpp>
+#include <hei_util.hpp>
+#include <util/pdbg.hpp>
+#include <util/trace.hpp>
+
+#include "gtest/gtest.h"
+
+using namespace analyzer;
+
+static const auto nodeId =
+    static_cast<libhei::NodeId_t>(libhei::hash<libhei::NodeId_t>("PLL_UNLOCK"));
+
+// Sub-test #1 - single PLL unlock attention on proc 1, clock 1
+TEST(PllUnlock, TestSet1)
+{
+    pdbg_targets_init(nullptr);
+
+    libhei::Chip chip1{util::pdbg::getTrgt("/proc1"), P10_20};
+
+    libhei::Signature sig11{chip1, nodeId, 0, 1, libhei::ATTN_TYPE_CHECKSTOP};
+
+    auto plugin = PluginMap::getSingleton().get(chip1.getType(), "pll_unlock");
+
+    libhei::IsolationData isoData{};
+    isoData.addSignature(sig11);
+    ServiceData sd{sig11, AnalysisType::SYSTEM_CHECKSTOP, isoData};
+
+    // Call the PLL unlock plugin.
+    plugin(1, chip1, sd);
+
+    nlohmann::json j{};
+    std::string s{};
+
+    // Callout list
+    j = sd.getCalloutList();
+    s = R"([
+    {
+        "Deconfigured": false,
+        "Guarded": false,
+        "LocationCode": "P0",
+        "Priority": "M"
+    },
+    {
+        "Deconfigured": false,
+        "Guarded": false,
+        "LocationCode": "/proc1",
+        "Priority": "M"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+
+    // Callout FFDC
+    j = sd.getCalloutFFDC();
+    s = R"([
+    {
+        "Callout Type": "Clock Callout",
+        "Clock Type": "OSC_REF_CLOCK_1",
+        "Priority": "medium"
+    },
+    {
+        "Callout Type": "Hardware Callout",
+        "Guard": false,
+        "Priority": "medium",
+        "Target": "/proc1"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+}
+
+// Sub-test #2 - PLL unlock attention on multiple procs and clocks. Isolating
+//               only to proc 1 clock 0 PLL unlock attentions.
+TEST(PllUnlock, TestSet2)
+{
+    pdbg_targets_init(nullptr);
+
+    libhei::Chip chip0{util::pdbg::getTrgt("/proc0"), P10_20};
+    libhei::Chip chip1{util::pdbg::getTrgt("/proc1"), P10_20};
+
+    // PLL unlock signatures for each clock per processor.
+    libhei::Signature sig00{chip0, nodeId, 0, 0, libhei::ATTN_TYPE_CHECKSTOP};
+    libhei::Signature sig01{chip0, nodeId, 0, 1, libhei::ATTN_TYPE_CHECKSTOP};
+    libhei::Signature sig10{chip1, nodeId, 0, 0, libhei::ATTN_TYPE_CHECKSTOP};
+    libhei::Signature sig11{chip1, nodeId, 0, 1, libhei::ATTN_TYPE_CHECKSTOP};
+
+    // Plugins for each processor.
+    auto plugin = PluginMap::getSingleton().get(chip1.getType(), "pll_unlock");
+
+    libhei::IsolationData isoData{};
+    isoData.addSignature(sig00);
+    isoData.addSignature(sig01);
+    isoData.addSignature(sig10);
+    isoData.addSignature(sig11);
+    ServiceData sd{sig10, AnalysisType::SYSTEM_CHECKSTOP, isoData};
+
+    // Call the PLL unlock plugin.
+    plugin(0, chip1, sd);
+
+    nlohmann::json j{};
+    std::string s{};
+
+    // Callout list
+    j = sd.getCalloutList();
+    s = R"([
+    {
+        "Deconfigured": false,
+        "Guarded": false,
+        "LocationCode": "P0",
+        "Priority": "H"
+    },
+    {
+        "Deconfigured": false,
+        "Guarded": false,
+        "LocationCode": "/proc0",
+        "Priority": "M"
+    },
+    {
+        "Deconfigured": false,
+        "Guarded": false,
+        "LocationCode": "/proc1",
+        "Priority": "M"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+
+    // Callout FFDC
+    j = sd.getCalloutFFDC();
+    s = R"([
+    {
+        "Callout Type": "Clock Callout",
+        "Clock Type": "OSC_REF_CLOCK_0",
+        "Priority": "high"
+    },
+    {
+        "Callout Type": "Hardware Callout",
+        "Guard": false,
+        "Priority": "medium",
+        "Target": "/proc0"
+    },
+    {
+        "Callout Type": "Hardware Callout",
+        "Guard": false,
+        "Priority": "medium",
+        "Target": "/proc1"
+    }
+])";
+    EXPECT_EQ(s, j.dump(4));
+}