support for FFDC-only isolation nodes in chip data

Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
Change-Id: Ib457329d31a9f01eb6992e57b11d84c06a63e6e7
diff --git a/src/chip_data/hei_chip_data.cpp b/src/chip_data/hei_chip_data.cpp
index 0c61985..f5c588d 100644
--- a/src/chip_data/hei_chip_data.cpp
+++ b/src/chip_data/hei_chip_data.cpp
@@ -257,8 +257,10 @@
         uint8_t numCapRegs, numIsoRules, numChildNodes;
         io_stream >> nodeInst >> numCapRegs >> numIsoRules >> numChildNodes;
 
-        // There must be at least one isolation rule defined.
-        HEI_ASSERT(0 != numIsoRules);
+        // It is possible to have rules defined and no child nodes, However, if
+        // there are no rules defined (FFDC-only node), there should not be
+        // any child nodes defined.
+        HEI_ASSERT(0 != numIsoRules || 0 == numChildNodes);
 
         // Allocate memory for this isolation node.
         auto isoNode =
diff --git a/src/isolator/hei_isolation_node.cpp b/src/isolator/hei_isolation_node.cpp
index 6791d6f..5bcfff8 100644
--- a/src/isolator/hei_isolation_node.cpp
+++ b/src/isolator/hei_isolation_node.cpp
@@ -39,58 +39,66 @@
         }
     }
 
-    // A rule for i_attnType must exist.
+    // Get the rule for this attention type.
     auto rule_itr = iv_rules.find(i_attnType);
-    HEI_ASSERT(iv_rules.end() != rule_itr);
 
-    // Get the returned BitString for this rule.
-    const BitString* bs = rule_itr->second->getBitString(i_chip);
-
-    // Ensure this BitString is not longer than the maximum bit field.
-    HEI_ASSERT(bs->getBitLen() <= (1 << (sizeof(BitPosition_t) * 8)));
-
-    // Find all active bits for this rule.
-    for (BitPosition_t bit = 0; bit < bs->getBitLen(); bit++)
+    // It is possible that a rule does not exist. The likely scenario is that
+    // this node is intended to only gather FFDC for a specific bit in the
+    // parent node.
+    if (iv_rules.end() != rule_itr)
     {
-        // Continue to the next bit if not active.
-        if (!bs->isBitSet(bit))
-            continue;
+        // Get the returned BitString for this rule.
+        const BitString* bs = rule_itr->second->getBitString(i_chip);
 
-        // At least one active bit was found.
-        o_activeAttn = true;
+        // Ensure this BitString is not longer than the maximum bit field.
+        HEI_ASSERT(bs->getBitLen() <= (1 << (sizeof(BitPosition_t) * 8)));
 
-        // Determine if this attention originated from another register or if it
-        // is a leaf in the isolation tree.
-        auto child_itr = iv_children.find(bit);
-        if (iv_children.end() != child_itr)
+        // Find all active bits for this rule.
+        for (BitPosition_t bit = 0; bit < bs->getBitLen(); bit++)
         {
-            // This bit was driven from an attention from another register.
-            // Continue down the isolation tree to look for more attentions.
-            bool attnFound =
-                child_itr->second->analyze(i_chip, i_attnType, io_isoData);
-            if (!attnFound)
+            // Continue to the next bit if not active.
+            if (!bs->isBitSet(bit))
+                continue;
+
+            // At least one active bit was found.
+            o_activeAttn = true;
+
+            // Determine if this attention originated from another register or
+            // if it is a leaf in the isolation tree.
+            auto child_itr = iv_children.find(bit);
+            if (iv_children.end() != child_itr)
             {
-                // Something went wrong. There should have been an active
-                // attention. It's possible there is a bug in the Chip Data
-                // File. Or, it is also possible some other piece of code is
-                // clearing the attention before this code is able to analyze
-                // it. Another possibility is that the hardware it not behaving
-                // as expected. Since we really don't know what happened, we
-                // should not assert. Instead, add this bit's signature to
-                // io_isoData. If there are no other active attentions, the user
-                // application could use this signature to help determine, and
-                // circumvent, the isolation problem.
+                // This bit was driven from an attention from another register.
+                // Continue down the isolation tree to look for more attentions.
+                bool attnFound =
+                    child_itr->second->analyze(i_chip, i_attnType, io_isoData);
+                if (!attnFound)
+                {
+                    // It is possible the child node is only intended for FFDC.
+                    // See comment near the check for a valid rule above.
+                    // Otherwise, it is possible something went wrong. If there
+                    // should have been an active attention, it's possible there
+                    // is a bug in the Chip Data File. Or, it is also possible
+                    // some other piece of code is clearing the attention before
+                    // this code is able to analyze it. Another possibility is
+                    // that the hardware it not behaving as expected. Since we
+                    // really don't know what happened, we should not assert.
+                    // Instead, add this bit's signature to io_isoData. If there
+                    // are no other active attentions, the user application
+                    // could use this signature to help determine, and
+                    // circumvent, the isolation problem.
+                    io_isoData.addSignature(
+                        Signature{i_chip, iv_id, iv_instance, bit, i_attnType});
+                }
+            }
+            else
+            {
+                // We have reached a leaf in the isolation tree. Add this bit's
+                // signature to io_isoData.
                 io_isoData.addSignature(
                     Signature{i_chip, iv_id, iv_instance, bit, i_attnType});
             }
         }
-        else
-        {
-            // We have reached a leaf in the isolation tree. Add this bit's
-            // signature to io_isoData.
-            io_isoData.addSignature(
-                Signature{i_chip, iv_id, iv_instance, bit, i_attnType});
-        }
     }
 
     // Analysis is complete on this node. So remove it from cv_isolationStack.
diff --git a/test/simulator/meson.build b/test/simulator/meson.build
index 590e381..1bcdc6a 100644
--- a/test/simulator/meson.build
+++ b/test/simulator/meson.build
@@ -8,6 +8,7 @@
 # Test cases
 test_src = [
     'sample_test_case.cpp',
+    'test_cases/exp20_foxhound7.cpp',
 ]
 
 gtest = dependency('gtest', main : true, required : false, method : 'system')
@@ -18,5 +19,5 @@
          executable('simulator', sim_src, test_src,
                     dependencies : gtest,
                     link_with : libhei_static,
-                    include_directories: incdir))
+                    include_directories: [ incdir, '.' ]))
 endif
diff --git a/test/simulator/simulator.cpp b/test/simulator/simulator.cpp
index 32eb31b..6bc3106 100644
--- a/test/simulator/simulator.cpp
+++ b/test/simulator/simulator.cpp
@@ -13,10 +13,10 @@
 const std::map<SimulatorData::SimChipType, const char*>
     SimulatorData::cv_chipPath = {
         {SAMPLE, "../test/simulator/sample_data/sample.cdb"},
-        {EXPLORER_11, "chip_data_explorer_11.cdb"},
-        {EXPLORER_20, "chip_data_explorer_20.cdb"},
-        {P10_10, "chip_data_p10_10.cdb"},
-        {P10_20, "chip_data_p10_20.cdb"},
+        {EXPLORER_11, "xml/chip_data_explorer_11.cdb"},
+        {EXPLORER_20, "xml/chip_data_explorer_20.cdb"},
+        {P10_10, "xml/chip_data_p10_10.cdb"},
+        {P10_20, "xml/chip_data_p10_20.cdb"},
 };
 
 //------------------------------------------------------------------------------
diff --git a/test/simulator/test_cases/exp20_foxhound7.cpp b/test/simulator/test_cases/exp20_foxhound7.cpp
new file mode 100644
index 0000000..65a4966
--- /dev/null
+++ b/test/simulator/test_cases/exp20_foxhound7.cpp
@@ -0,0 +1,16 @@
+#include "simulator.hpp"
+
+START_TEST_CASE(exp20_foxhoundfatal)
+
+CHIP(ocmb0, EXPLORER_20)
+
+START_ITERATION
+
+REG_SCOM(ocmb0, 0x08040000, 0x1000000000000000) // OCMB_CHIPLET_CS_FIR
+REG_SCOM(ocmb0, 0x0804000a, 0x0000000001000000) // OCMB_LFIR
+
+EXP_SIG(ocmb0, 0xbdcd, 0, 39, UNIT_CS)
+
+END_ITERATION
+
+END_TEST_CASE
diff --git a/xml/.gitignore b/xml/.gitignore
new file mode 100644
index 0000000..84c048a
--- /dev/null
+++ b/xml/.gitignore
@@ -0,0 +1 @@
+/build/
diff --git a/xml/chip_data_xml.md b/xml/chip_data_xml.md
index d7d839a..5052056 100644
--- a/xml/chip_data_xml.md
+++ b/xml/chip_data_xml.md
@@ -101,11 +101,13 @@
    * Register length:   8 bytes
    * Bit order:         ascending (0-63, left to right)
 
-### 2.4) Element `<register>` (required 1 or more)
+### 2.4) Element `<register>` (conditionally optional)
 
-These provide a list of all registers required for isolation of this node. At
-a minimum, a `<register>` element must exist for each register referenced by
-the `<rule>` elements.
+These provide a list of all registers required for isolation of this node.
+
+At a minimum, a `<register>` element must exist for each register referenced by
+the `<capture_group>` or `<rule>` elements. Also, see the `<local_fir>` element
+for a possible shorthand definition.
 
 #### 2.4.1) Attribute `name` (required)
 
@@ -187,13 +189,18 @@
 number of instances represented by the the `node_inst` attribute of the
 `<capture_group>` element.
 
-### 2.6) Element `<rule>` (required 1 or more)
+### 2.6) Element `<rule>` (conditionally optional)
 
 A rule helps specify if an attention is being raised from a register and what
 type of attention is being raised. A rule is constructed by a series of
 expressions (see `<expr>` below). The result of the expressions will indicate
 all active attentions for a rule.
 
+At least one `<rule>` must exist if any `<bit>` elements are defined. The
+`<rule>` elements are optional for nodes that are intended to only capture FFDC
+for a bit defined in the parent node. Also, see the `<local_fir>` element for a
+possible shorthand definition.
+
 #### 2.6.1) Attribute `attn_type` (required)
 
 The attention type for this rule. See appendix for supported values.
@@ -259,10 +266,13 @@
    The length of the number is defined by the `reg_type` attribute of the root
    `<attn_node>` element.
 
-### 2.7) Element `<bit>` (required 1 or more)
+### 2.7) Element `<bit>` (conditionally optional)
 
-These provide metadata for each bit in this node. There should be a `<bit>`
-element for each bit that could generate an attention.
+These provide metadata for each bit in this node.
+
+The `<bit>` elements should only be defined if there are `<rule>` elements
+defined and there should be a `<bit>` element for each bit in the rules that
+could generate an attention.
 
 #### 2.7.1) Attribute `pos` (required)
 
diff --git a/xml/explorer/node_ocmb_lfir.xml b/xml/explorer/node_ocmb_lfir.xml
index 7cb4543..4c8c1a1 100644
--- a/xml/explorer/node_ocmb_lfir.xml
+++ b/xml/explorer/node_ocmb_lfir.xml
@@ -31,7 +31,14 @@
     <bit pos="36"   >DDR4 PHY fatal</bit>
     <bit pos="37"   >DDR4 PHY non fatal</bit>
     <bit pos="38"   >DDR4 PHY interrupt</bit>
-    <bit pos="39:46">foxhound fatal</bit>
+    <bit pos="39" child_node="OCMB_LFIR_FFDC_FOXHOUND" node_inst="7">foxhound fatal lane 7</bit>
+    <bit pos="40" child_node="OCMB_LFIR_FFDC_FOXHOUND" node_inst="6">foxhound fatal lane 6</bit>
+    <bit pos="41" child_node="OCMB_LFIR_FFDC_FOXHOUND" node_inst="5">foxhound fatal lane 5</bit>
+    <bit pos="42" child_node="OCMB_LFIR_FFDC_FOXHOUND" node_inst="4">foxhound fatal lane 4</bit>
+    <bit pos="43" child_node="OCMB_LFIR_FFDC_FOXHOUND" node_inst="3">foxhound fatal lane 3</bit>
+    <bit pos="44" child_node="OCMB_LFIR_FFDC_FOXHOUND" node_inst="2">foxhound fatal lane 2</bit>
+    <bit pos="45" child_node="OCMB_LFIR_FFDC_FOXHOUND" node_inst="1">foxhound fatal lane 1</bit>
+    <bit pos="46" child_node="OCMB_LFIR_FFDC_FOXHOUND" node_inst="0">foxhound fatal lane 0</bit>
     <bit pos="47:54">foxhound non fatal</bit>
     <bit pos="55:62">foxhound serdes interrupt</bit>
     <bit pos="63"   >GIF2PCB parity error on FSM or Registers</bit>
diff --git a/xml/explorer/node_ocmb_lfir_ffdc_foxhound.xml b/xml/explorer/node_ocmb_lfir_ffdc_foxhound.xml
new file mode 100644
index 0000000..531c9ff
--- /dev/null
+++ b/xml/explorer/node_ocmb_lfir_ffdc_foxhound.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<attn_node name="OCMB_LFIR_FFDC_FOXHOUND" model_ec="EXPLORER_11,EXPLORER_20" reg_type="SCOM">
+
+    <!--
+        These registers are on the MicroChip side of Explorer and are not in
+        the SCOM def.
+    -->
+
+    <register name="ADSP_PCBI">
+        <instance reg_inst="0" addr="0x00200860" />
+        <instance reg_inst="1" addr="0x00201860" />
+        <instance reg_inst="2" addr="0x00202860" />
+        <instance reg_inst="3" addr="0x00203860" />
+        <instance reg_inst="4" addr="0x00204860" />
+        <instance reg_inst="5" addr="0x00205860" />
+        <instance reg_inst="6" addr="0x00206860" />
+        <instance reg_inst="7" addr="0x00207860" />
+    </register>
+
+    <register name="CSU_PCBI">
+        <instance reg_inst="0" addr="0x002000B0" />
+        <instance reg_inst="1" addr="0x002010B0" />
+        <instance reg_inst="2" addr="0x002020B0" />
+        <instance reg_inst="3" addr="0x002030B0" />
+        <instance reg_inst="4" addr="0x002040B0" />
+        <instance reg_inst="5" addr="0x002050B0" />
+        <instance reg_inst="6" addr="0x002060B0" />
+        <instance reg_inst="7" addr="0x002070B0" />
+    </register>
+
+    <capture_group node_inst="0:7">
+        <capture_register reg_name="ADSP_PCBI" reg_inst="0:7" />
+        <capture_register reg_name="CSU_PCBI"  reg_inst="0:7" />
+    </capture_group>
+
+</attn_node>
+
diff --git a/xml/node.xsd b/xml/node.xsd
index 5edcb32..2bbe4ec 100644
--- a/xml/node.xsd
+++ b/xml/node.xsd
@@ -17,7 +17,7 @@
             <xsd:element name="capture_group" type="capture_group" minOccurs="0"
                          maxOccurs="255"/>
             <xsd:element name="rule" type="rule" minOccurs="0" maxOccurs="255"/>
-            <xsd:element name="bit"  type="bit"  minOccurs="1" maxOccurs="255"/>
+            <xsd:element name="bit"  type="bit"  minOccurs="0" maxOccurs="255"/>
         </xsd:sequence>
 
         <xsd:attribute name="name"     type="name_type"     use="required"/>
diff --git a/xml/parse_chip_data_xml b/xml/parse_chip_data_xml
index 131ff56..b91de9d 100755
--- a/xml/parse_chip_data_xml
+++ b/xml/parse_chip_data_xml
@@ -552,12 +552,6 @@
 {
     my ( $node, $insts_data ) = @_;
 
-    # There must be at least one rule entry.
-    unless ( defined $node->{rule} and 0 < scalar @{$node->{rule}} )
-    {
-        FAIL( "Node $node->{name} does not contain at least one rule" );
-    }
-
     # There should be only one rule per attention type and node instance for
     # this node.
     my $rule_dups = {};
@@ -607,12 +601,6 @@
 {
     my ( $node, $sigs, $insts_data ) = @_;
 
-    # There must be at least one bit entry.
-    unless ( defined $node->{bit} and 0 < scalar @{$node->{bit}} )
-    {
-        FAIL( "Node $node->{name} does not contain at least one bit" );
-    }
-
     my @node_insts = sort keys %{$insts_data};
     my $sz_insts = scalar @node_insts;
 
@@ -720,13 +708,27 @@
     # Split the capture group information per node instance.
     __normalizeCaptureGroup($node, $insts_data);
 
-    # Split the rule information per node instance. The sorted instance list
-    # will be used as indexes for the node_inst attribute of the <bit> elements.
-    __normalizeRule($node, $insts_data);
+    my $is_rule = (defined $node->{rule} and 0 < scalar @{$node->{rule}}) ? 1 : 0;
+    my $is_bit  = (defined $node->{bit}  and 0 < scalar @{$node->{bit}})  ? 1 : 0;
 
-    # Finally, collect the signature details and split the bit information per
-    # node instance.
-    __normalizeBit($node, $sigs, $insts_data);
+    # If a rule is defined, a bit must be defined as well. It is possible for
+    # neither to be defined (FFDC-only node).
+    if ( $is_rule and $is_bit )
+    {
+        # Split the rule information per node instance. The sorted instance list
+        # will be used as indexes for the node_inst attribute of the <bit>
+        # elements.
+        __normalizeRule($node, $insts_data);
+
+        # Finally, collect the signature details and split the bit information
+        # per node instance.
+        __normalizeBit($node, $sigs, $insts_data);
+    }
+    elsif ( $is_rule or $is_bit )
+    {
+        # One is defined and the other is not. This is an error.
+        FAIL("Node $node->{name} has a bit or rule defined and the other is not.");
+    }
 
     # Now that we have all of the node data, collapse the instance data into
     # a list.
@@ -1068,12 +1070,8 @@
             my $num_cap_regs = (defined $i->{capture_group})
                                     ? scalar @{$i->{capture_group}} : 0;
 
-            # At least one rule is required.
-            my $num_rules = scalar @{$i->{rule}};
-            unless ( 0 < $num_rules )
-            {
-                FAIL("No rule for $n->{name} $i->{node_inst}");
-            }
+            # Rules may not exist for this node.
+            my $num_rules = (defined $i->{rule}) ? scalar @{$i->{rule}} : 0;
 
             # Child nodes may not exist for this node.
             my $num_bit = (defined $i->{bit}) ? scalar @{$i->{bit}} : 0;
@@ -1094,11 +1092,14 @@
                 }
             }
 
-            for my $r ( @{$i->{rule}} )
+            if ( 0 < $num_rules )
             {
-                # Register rule metadata
-                __bin($fh, 1, $ATTN_TYPE->{$r->{attn_type}}->[0]);
-                __printExpr($fh, $reg_size, $r->{expr});
+                for my $r ( @{$i->{rule}} )
+                {
+                    # Register rule metadata
+                    __bin($fh, 1, $ATTN_TYPE->{$r->{attn_type}}->[0]);
+                    __printExpr($fh, $reg_size, $r->{expr});
+                }
             }
 
             if ( 0 < $num_bit )