From 47d5f536bc042aebc368497fa992e9ed1699e07b Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Wed, 25 Mar 2026 14:29:19 +0000 Subject: [PATCH] Add coverage report output for covergroup Signed-off-by: Matthew Ballance --- src/V3Covergroup.cpp | 9 +- src/VlcMain.cpp | 3 + src/VlcOptions.h | 2 + src/VlcPoint.h | 5 + src/VlcTop.cpp | 162 ++++++++++++++++++ src/VlcTop.h | 1 + test_regress/driver.py | 8 +- test_regress/t/t_covergroup_cross_3way.out | 24 +-- test_regress/t/t_covergroup_cross_4way.out | 32 ++-- test_regress/t/t_covergroup_cross_inline.out | 32 ++-- test_regress/t/t_covergroup_cross_simple.out | 8 +- .../t/t_covergroup_cross_sparse_map.out | 162 +++++++++--------- 12 files changed, 315 insertions(+), 133 deletions(-) diff --git a/src/V3Covergroup.cpp b/src/V3Covergroup.cpp index bc66a1c06..f28a62621 100644 --- a/src/V3Covergroup.cpp +++ b/src/V3Covergroup.cpp @@ -1599,12 +1599,15 @@ class FunctionalCoverageVisitor final : public VNVisitor { + "\", " "\"column\", \"" + std::to_string(fl->firstColumn()) + "\", "); + const std::string crossSuffix = crossp ? ", \"cross\", \"1\"" : ""; if (binp->binsType() == VCoverBinsType::BINS_IGNORE) { - cstmtp->add("\"bin\", \"" + binName + "\", \"bin_type\", \"ignore\");"); + cstmtp->add("\"bin\", \"" + binName + "\", \"bin_type\", \"ignore\"" + + crossSuffix + ");"); } else if (binp->binsType() == VCoverBinsType::BINS_ILLEGAL) { - cstmtp->add("\"bin\", \"" + binName + "\", \"bin_type\", \"illegal\");"); + cstmtp->add("\"bin\", \"" + binName + "\", \"bin_type\", \"illegal\"" + + crossSuffix + ");"); } else { - cstmtp->add("\"bin\", \"" + binName + "\");"); + cstmtp->add("\"bin\", \"" + binName + "\"" + crossSuffix + ");"); } // Add to constructor diff --git a/src/VlcMain.cpp b/src/VlcMain.cpp index e5113a966..40172d75b 100644 --- a/src/VlcMain.cpp +++ b/src/VlcMain.cpp @@ -66,6 +66,7 @@ void VlcOptions::parseOptsList(int argc, char** argv) { DECL_OPTION("-annotate-all", OnOff, &m_annotateAll); DECL_OPTION("-annotate-min", Set, &m_annotateMin); DECL_OPTION("-annotate-points", OnOff, &m_annotatePoints); + DECL_OPTION("-covergroup", OnOff, &m_covergroup); DECL_OPTION("-debug", CbCall, []() { V3Error::debugDefault(3); }); DECL_OPTION("-debugi", CbVal, [](int v) { V3Error::debugDefault(v); }); DECL_OPTION("-filter-type", Set, &m_filterType); @@ -143,6 +144,8 @@ int main(int argc, char** argv) { V3Error::abortIfWarnings(); if (!top.opt.annotateOut().empty()) top.annotate(top.opt.annotateOut()); + if (top.opt.covergroup()) top.covergroup(); + if (top.opt.rank()) { top.rank(); top.tests().dump(false); diff --git a/src/VlcOptions.h b/src/VlcOptions.h index 7d957f722..9cd91f0a2 100644 --- a/src/VlcOptions.h +++ b/src/VlcOptions.h @@ -39,6 +39,7 @@ class VlcOptions final { bool m_annotateAll = false; // main switch: --annotate-all int m_annotateMin = 10; // main switch: --annotate-min I bool m_annotatePoints = false; // main switch: --annotate-points + bool m_covergroup = false; // main switch: --covergroup string m_filterType = "*"; // main switch: --filter-type VlStringSet m_readFiles; // main switch: --read bool m_rank = false; // main switch: --rank @@ -67,6 +68,7 @@ public: int annotateMin() const { return m_annotateMin; } bool countOk(uint64_t count) const { return count >= static_cast(m_annotateMin); } bool annotatePoints() const { return m_annotatePoints; } + bool covergroup() const { return m_covergroup; } bool rank() const { return m_rank; } bool unlink() const { return m_unlink; } string writeFile() const { return m_writeFile; } diff --git a/src/VlcPoint.h b/src/VlcPoint.h index 372b9f2bf..3734b3c04 100644 --- a/src/VlcPoint.h +++ b/src/VlcPoint.h @@ -65,6 +65,11 @@ public: string comment() const { return keyExtract(VL_CIK_COMMENT, m_name.c_str()); } string hier() const { return keyExtract(VL_CIK_HIER, m_name.c_str()); } string type() const { return typeExtract(m_name.c_str()); } + // Covergroup-specific key accessors (long keys, no short-key alias) + string page() const { return keyExtract("page", m_name.c_str()); } + string bin() const { return keyExtract("bin", m_name.c_str()); } + string binType() const { return keyExtract("bin_type", m_name.c_str()); } + bool isCross() const { return !keyExtract("cross", m_name.c_str()).empty(); } string thresh() const { // string as maybe "" return keyExtract(VL_CIK_THRESH, m_name.c_str()); diff --git a/src/VlcTop.cpp b/src/VlcTop.cpp index aad1f7f0d..49583f4f1 100644 --- a/src/VlcTop.cpp +++ b/src/VlcTop.cpp @@ -25,6 +25,9 @@ #include #include +#include +#include +#include #include #include @@ -205,6 +208,165 @@ void VlcTop::rank() { } } +void VlcTop::covergroup() { + UINFO(2, "covergroup..."); + // Structs for accumulating report data + struct BinEntry { + std::string name; + std::string binType; // "ignore", "illegal", or "" (normal) + bool covered = false; + uint64_t count = 0; + }; + struct CpEntry { + std::string name; + bool isCross = false; + std::vector bins; + uint64_t normalTotal = 0; + uint64_t normalCovered = 0; + }; + struct CgEntry { + std::string typeName; + std::string filename; + int lineno = 0; + std::vector coverpoints; + std::map cpIndex; + }; + + std::map cgMap; + + // Collect covergroup points from all loaded coverage data + for (const auto& nameNum : m_points) { + const VlcPoint& pt = m_points.pointNumber(nameNum.second); + if (pt.type() != "covergroup") continue; + + const std::string page = pt.page(); + // Page format: "v_covergroup/" + const std::string pagePrefix = "v_covergroup/"; + if (page.size() <= pagePrefix.size()) continue; + const std::string cgTypeName = page.substr(pagePrefix.size()); + + // Parse hier: ".." + const std::string hier = pt.hier(); + const size_t dot1 = hier.find('.'); + if (dot1 == std::string::npos) continue; + const size_t dot2 = hier.find('.', dot1 + 1); + if (dot2 == std::string::npos) continue; + const std::string cpName = hier.substr(dot1 + 1, dot2 - dot1 - 1); + const std::string binName = hier.substr(dot2 + 1); + + auto& cg = cgMap[cgTypeName]; + if (cg.typeName.empty()) { + cg.typeName = cgTypeName; + cg.filename = pt.filename(); + cg.lineno = pt.lineno(); + } + + auto it = cg.cpIndex.find(cpName); + size_t cpIdx; + if (it == cg.cpIndex.end()) { + cpIdx = cg.coverpoints.size(); + cg.cpIndex[cpName] = cpIdx; + CpEntry cp; + cp.name = cpName; + cp.isCross = pt.isCross(); + cg.coverpoints.push_back(cp); + } else { + cpIdx = it->second; + } + + BinEntry bin; + bin.name = binName; + bin.binType = pt.binType(); + // Threshold: use per-bin thresh key (option.at_least) if present, else 1 (SV default) + const std::string threshStr = pt.thresh(); + const uint64_t binThresh = threshStr.empty() ? 1 : std::stoull(threshStr); + bin.count = pt.count(); + bin.covered = (bin.count >= binThresh); + cg.coverpoints[cpIdx].bins.push_back(bin); + + if (bin.binType.empty()) { + ++cg.coverpoints[cpIdx].normalTotal; + if (bin.covered) ++cg.coverpoints[cpIdx].normalCovered; + } + } + + // Compute grand totals + uint64_t grandTotal = 0, grandCovered = 0, grandIgnored = 0, grandIllegal = 0; + for (const auto& cgPair : cgMap) { + for (const auto& cp : cgPair.second.coverpoints) { + grandTotal += cp.normalTotal; + grandCovered += cp.normalCovered; + for (const auto& bin : cp.bins) { + if (bin.binType == "ignore") ++grandIgnored; + else if (bin.binType == "illegal") ++grandIllegal; + } + } + } + + // Format a percentage string "xx.xx" + const auto pctStr = [](uint64_t covered, uint64_t total) -> std::string { + std::ostringstream oss; + const double pct = (total == 0) ? 100.0 : (100.0 * covered / total); + oss << std::fixed << std::setprecision(2) << pct; + return oss.str(); + }; + + const std::string divider(78, '-'); + + // Header and grand total + std::cout << "COVERGROUP COVERAGE REPORT\n"; + std::cout << "==========================\n"; + std::cout << "\n"; + std::cout << "TOTAL: " << grandCovered << "/" << grandTotal + << " bins covered (" << pctStr(grandCovered, grandTotal) << "%)\n"; + if (grandIgnored || grandIllegal) + std::cout << " (" << grandIgnored << " ignored, " << grandIllegal << " illegal)\n"; + + // One section per covergroup type (map is sorted alphabetically) + for (const auto& cgPair : cgMap) { + const CgEntry& cg = cgPair.second; + + uint64_t cgTotal = 0, cgCovered = 0; + for (const auto& cp : cg.coverpoints) { + cgTotal += cp.normalTotal; + cgCovered += cp.normalCovered; + } + + std::cout << "\n" << divider << "\n"; + std::cout << "Covergroup Type: " << cg.typeName + << " [" << cg.filename << ":" << cg.lineno << "]\n"; + std::cout << " Type Coverage: " << cgCovered << "/" << cgTotal + << " bins (" << pctStr(cgCovered, cgTotal) << "%)\n"; + + for (const auto& cp : cg.coverpoints) { + std::cout << "\n"; + std::cout << " " << (cp.isCross ? "Cross" : "Coverpoint") << ": " << cp.name << "\n"; + std::cout << " Coverage: " << cp.normalCovered << "/" << cp.normalTotal + << " bins (" << pctStr(cp.normalCovered, cp.normalTotal) << "%)\n"; + std::cout << " Bins:\n"; + + // Align bin name column to max name length in this coverpoint + size_t maxNameLen = 0; + for (const auto& bin : cp.bins) + if (bin.name.size() > maxNameLen) maxNameLen = bin.name.size(); + + for (const auto& bin : cp.bins) { + const char* status; + if (bin.binType == "ignore") status = "IGNORE "; + else if (bin.binType == "illegal") status = "ILLEGAL"; + else if (bin.covered) status = "COVERED"; + else status = "ZERO "; + + std::cout << " " << status << " " + << std::left << std::setw(static_cast(maxNameLen)) << bin.name + << std::right << " " << bin.count << " hits\n"; + } + } + } + + std::cout << "\n" << divider << "\n"; +} + //###################################################################### void VlcTop::annotateCalc() { diff --git a/src/VlcTop.h b/src/VlcTop.h index 13e0d93d6..06007fdd3 100644 --- a/src/VlcTop.h +++ b/src/VlcTop.h @@ -55,6 +55,7 @@ public: // METHODS void annotate(const string& dirname); + void covergroup(); void readCoverage(const string& filename, bool nonfatal = false); void writeCoverage(const string& filename); void writeInfo(const string& filename); diff --git a/test_regress/driver.py b/test_regress/driver.py index 5acbe3b57..731e7272e 100755 --- a/test_regress/driver.py +++ b/test_regress/driver.py @@ -2823,7 +2823,13 @@ class VlTest: continue hier = h_m.group(1) bt_m = re.search(r'\x01bin_type\x02([^\x01]+)', entry) - label = f"{hier} [{bt_m.group(1)}]" if bt_m else hier + cross_m = re.search(r'\x01cross\x021', entry) + annotations = [] + if bt_m: + annotations.append(bt_m.group(1)) + if cross_m: + annotations.append("cross") + label = f"{hier} [{','.join(annotations)}]" if annotations else hier entries.append((hier, label, int(count))) entries.sort() with open(outfile, 'w', encoding='utf-8') as fh: diff --git a/test_regress/t/t_covergroup_cross_3way.out b/test_regress/t/t_covergroup_cross_3way.out index 2c63d31be..fa424c7e6 100644 --- a/test_regress/t/t_covergroup_cross_3way.out +++ b/test_regress/t/t_covergroup_cross_3way.out @@ -1,15 +1,15 @@ -cg.addr_cmd_mode.addr0_x_read_x_debug: 0 -cg.addr_cmd_mode.addr0_x_read_x_normal: 1 -cg.addr_cmd_mode.addr0_x_write_x_debug: 1 -cg.addr_cmd_mode.addr0_x_write_x_normal: 0 -cg.addr_cmd_mode.addr1_x_read_x_debug: 0 -cg.addr_cmd_mode.addr1_x_read_x_normal: 0 -cg.addr_cmd_mode.addr1_x_write_x_debug: 0 -cg.addr_cmd_mode.addr1_x_write_x_normal: 1 -cg.addr_cmd_mode.addr2_x_read_x_debug: 1 -cg.addr_cmd_mode.addr2_x_read_x_normal: 0 -cg.addr_cmd_mode.addr2_x_write_x_debug: 0 -cg.addr_cmd_mode.addr2_x_write_x_normal: 0 +cg.addr_cmd_mode.addr0_x_read_x_debug [cross]: 0 +cg.addr_cmd_mode.addr0_x_read_x_normal [cross]: 1 +cg.addr_cmd_mode.addr0_x_write_x_debug [cross]: 1 +cg.addr_cmd_mode.addr0_x_write_x_normal [cross]: 0 +cg.addr_cmd_mode.addr1_x_read_x_debug [cross]: 0 +cg.addr_cmd_mode.addr1_x_read_x_normal [cross]: 0 +cg.addr_cmd_mode.addr1_x_write_x_debug [cross]: 0 +cg.addr_cmd_mode.addr1_x_write_x_normal [cross]: 1 +cg.addr_cmd_mode.addr2_x_read_x_debug [cross]: 1 +cg.addr_cmd_mode.addr2_x_read_x_normal [cross]: 0 +cg.addr_cmd_mode.addr2_x_write_x_debug [cross]: 0 +cg.addr_cmd_mode.addr2_x_write_x_normal [cross]: 0 cg.cp_addr.addr0: 2 cg.cp_addr.addr1: 1 cg.cp_addr.addr2: 1 diff --git a/test_regress/t/t_covergroup_cross_4way.out b/test_regress/t/t_covergroup_cross_4way.out index 8ed8ceb6b..f376f5437 100644 --- a/test_regress/t/t_covergroup_cross_4way.out +++ b/test_regress/t/t_covergroup_cross_4way.out @@ -1,19 +1,19 @@ -cg.addr_cmd_mode_parity.addr0_x_read_x_debug_x_even: 0 -cg.addr_cmd_mode_parity.addr0_x_read_x_debug_x_odd: 0 -cg.addr_cmd_mode_parity.addr0_x_read_x_normal_x_even: 1 -cg.addr_cmd_mode_parity.addr0_x_read_x_normal_x_odd: 0 -cg.addr_cmd_mode_parity.addr0_x_write_x_debug_x_even: 1 -cg.addr_cmd_mode_parity.addr0_x_write_x_debug_x_odd: 0 -cg.addr_cmd_mode_parity.addr0_x_write_x_normal_x_even: 0 -cg.addr_cmd_mode_parity.addr0_x_write_x_normal_x_odd: 0 -cg.addr_cmd_mode_parity.addr1_x_read_x_debug_x_even: 0 -cg.addr_cmd_mode_parity.addr1_x_read_x_debug_x_odd: 1 -cg.addr_cmd_mode_parity.addr1_x_read_x_normal_x_even: 0 -cg.addr_cmd_mode_parity.addr1_x_read_x_normal_x_odd: 0 -cg.addr_cmd_mode_parity.addr1_x_write_x_debug_x_even: 0 -cg.addr_cmd_mode_parity.addr1_x_write_x_debug_x_odd: 0 -cg.addr_cmd_mode_parity.addr1_x_write_x_normal_x_even: 0 -cg.addr_cmd_mode_parity.addr1_x_write_x_normal_x_odd: 1 +cg.addr_cmd_mode_parity.addr0_x_read_x_debug_x_even [cross]: 0 +cg.addr_cmd_mode_parity.addr0_x_read_x_debug_x_odd [cross]: 0 +cg.addr_cmd_mode_parity.addr0_x_read_x_normal_x_even [cross]: 1 +cg.addr_cmd_mode_parity.addr0_x_read_x_normal_x_odd [cross]: 0 +cg.addr_cmd_mode_parity.addr0_x_write_x_debug_x_even [cross]: 1 +cg.addr_cmd_mode_parity.addr0_x_write_x_debug_x_odd [cross]: 0 +cg.addr_cmd_mode_parity.addr0_x_write_x_normal_x_even [cross]: 0 +cg.addr_cmd_mode_parity.addr0_x_write_x_normal_x_odd [cross]: 0 +cg.addr_cmd_mode_parity.addr1_x_read_x_debug_x_even [cross]: 0 +cg.addr_cmd_mode_parity.addr1_x_read_x_debug_x_odd [cross]: 1 +cg.addr_cmd_mode_parity.addr1_x_read_x_normal_x_even [cross]: 0 +cg.addr_cmd_mode_parity.addr1_x_read_x_normal_x_odd [cross]: 0 +cg.addr_cmd_mode_parity.addr1_x_write_x_debug_x_even [cross]: 0 +cg.addr_cmd_mode_parity.addr1_x_write_x_debug_x_odd [cross]: 0 +cg.addr_cmd_mode_parity.addr1_x_write_x_normal_x_even [cross]: 0 +cg.addr_cmd_mode_parity.addr1_x_write_x_normal_x_odd [cross]: 1 cg.cp_addr.addr0: 2 cg.cp_addr.addr1: 2 cg.cp_cmd.read: 2 diff --git a/test_regress/t/t_covergroup_cross_inline.out b/test_regress/t/t_covergroup_cross_inline.out index 99c7e5dd0..3b930a824 100644 --- a/test_regress/t/t_covergroup_cross_inline.out +++ b/test_regress/t/t_covergroup_cross_inline.out @@ -6,19 +6,19 @@ cg.cp_b.b0: 21 cg.cp_b.b1: 0 cg.cp_b.b2: 0 cg.cp_b.b3: 0 -cg.cross_ab.a0_x_b0: 9 -cg.cross_ab.a0_x_b1: 0 -cg.cross_ab.a0_x_b2: 0 -cg.cross_ab.a0_x_b3: 0 -cg.cross_ab.a1_x_b0: 4 -cg.cross_ab.a1_x_b1: 0 -cg.cross_ab.a1_x_b2: 0 -cg.cross_ab.a1_x_b3: 0 -cg.cross_ab.a2_x_b0: 4 -cg.cross_ab.a2_x_b1: 0 -cg.cross_ab.a2_x_b2: 0 -cg.cross_ab.a2_x_b3: 0 -cg.cross_ab.a3_x_b0: 4 -cg.cross_ab.a3_x_b1: 0 -cg.cross_ab.a3_x_b2: 0 -cg.cross_ab.a3_x_b3: 0 +cg.cross_ab.a0_x_b0 [cross]: 9 +cg.cross_ab.a0_x_b1 [cross]: 0 +cg.cross_ab.a0_x_b2 [cross]: 0 +cg.cross_ab.a0_x_b3 [cross]: 0 +cg.cross_ab.a1_x_b0 [cross]: 4 +cg.cross_ab.a1_x_b1 [cross]: 0 +cg.cross_ab.a1_x_b2 [cross]: 0 +cg.cross_ab.a1_x_b3 [cross]: 0 +cg.cross_ab.a2_x_b0 [cross]: 4 +cg.cross_ab.a2_x_b1 [cross]: 0 +cg.cross_ab.a2_x_b2 [cross]: 0 +cg.cross_ab.a2_x_b3 [cross]: 0 +cg.cross_ab.a3_x_b0 [cross]: 4 +cg.cross_ab.a3_x_b1 [cross]: 0 +cg.cross_ab.a3_x_b2 [cross]: 0 +cg.cross_ab.a3_x_b3 [cross]: 0 diff --git a/test_regress/t/t_covergroup_cross_simple.out b/test_regress/t/t_covergroup_cross_simple.out index d8b12fa1a..55fc0aae4 100644 --- a/test_regress/t/t_covergroup_cross_simple.out +++ b/test_regress/t/t_covergroup_cross_simple.out @@ -1,7 +1,7 @@ -cg.addr_cmd.addr0_x_read: 1 -cg.addr_cmd.addr0_x_write: 1 -cg.addr_cmd.addr1_x_read: 1 -cg.addr_cmd.addr1_x_write: 1 +cg.addr_cmd.addr0_x_read [cross]: 1 +cg.addr_cmd.addr0_x_write [cross]: 1 +cg.addr_cmd.addr1_x_read [cross]: 1 +cg.addr_cmd.addr1_x_write [cross]: 1 cg.cp_addr.addr0: 2 cg.cp_addr.addr1: 2 cg.cp_cmd.read: 2 diff --git a/test_regress/t/t_covergroup_cross_sparse_map.out b/test_regress/t/t_covergroup_cross_sparse_map.out index a8fbab406..319b5df8b 100644 --- a/test_regress/t/t_covergroup_cross_sparse_map.out +++ b/test_regress/t/t_covergroup_cross_sparse_map.out @@ -10,84 +10,84 @@ cg.cp_c.c2: 6 cg.cp_d.d0: 21 cg.cp_d.d1: 0 cg.cp_d.d2: 0 -cg.cross_abcd.a0_x_b0_x_c0_x_d0: 10 -cg.cross_abcd.a0_x_b0_x_c0_x_d1: 0 -cg.cross_abcd.a0_x_b0_x_c0_x_d2: 0 -cg.cross_abcd.a0_x_b0_x_c1_x_d0: 0 -cg.cross_abcd.a0_x_b0_x_c1_x_d1: 0 -cg.cross_abcd.a0_x_b0_x_c1_x_d2: 0 -cg.cross_abcd.a0_x_b0_x_c2_x_d0: 0 -cg.cross_abcd.a0_x_b0_x_c2_x_d1: 0 -cg.cross_abcd.a0_x_b0_x_c2_x_d2: 0 -cg.cross_abcd.a0_x_b1_x_c0_x_d0: 0 -cg.cross_abcd.a0_x_b1_x_c0_x_d1: 0 -cg.cross_abcd.a0_x_b1_x_c0_x_d2: 0 -cg.cross_abcd.a0_x_b1_x_c1_x_d0: 0 -cg.cross_abcd.a0_x_b1_x_c1_x_d1: 0 -cg.cross_abcd.a0_x_b1_x_c1_x_d2: 0 -cg.cross_abcd.a0_x_b1_x_c2_x_d0: 0 -cg.cross_abcd.a0_x_b1_x_c2_x_d1: 0 -cg.cross_abcd.a0_x_b1_x_c2_x_d2: 0 -cg.cross_abcd.a0_x_b2_x_c0_x_d0: 0 -cg.cross_abcd.a0_x_b2_x_c0_x_d1: 0 -cg.cross_abcd.a0_x_b2_x_c0_x_d2: 0 -cg.cross_abcd.a0_x_b2_x_c1_x_d0: 0 -cg.cross_abcd.a0_x_b2_x_c1_x_d1: 0 -cg.cross_abcd.a0_x_b2_x_c1_x_d2: 0 -cg.cross_abcd.a0_x_b2_x_c2_x_d0: 0 -cg.cross_abcd.a0_x_b2_x_c2_x_d1: 0 -cg.cross_abcd.a0_x_b2_x_c2_x_d2: 0 -cg.cross_abcd.a1_x_b0_x_c0_x_d0: 0 -cg.cross_abcd.a1_x_b0_x_c0_x_d1: 0 -cg.cross_abcd.a1_x_b0_x_c0_x_d2: 0 -cg.cross_abcd.a1_x_b0_x_c1_x_d0: 5 -cg.cross_abcd.a1_x_b0_x_c1_x_d1: 0 -cg.cross_abcd.a1_x_b0_x_c1_x_d2: 0 -cg.cross_abcd.a1_x_b0_x_c2_x_d0: 0 -cg.cross_abcd.a1_x_b0_x_c2_x_d1: 0 -cg.cross_abcd.a1_x_b0_x_c2_x_d2: 0 -cg.cross_abcd.a1_x_b1_x_c0_x_d0: 0 -cg.cross_abcd.a1_x_b1_x_c0_x_d1: 0 -cg.cross_abcd.a1_x_b1_x_c0_x_d2: 0 -cg.cross_abcd.a1_x_b1_x_c1_x_d0: 0 -cg.cross_abcd.a1_x_b1_x_c1_x_d1: 0 -cg.cross_abcd.a1_x_b1_x_c1_x_d2: 0 -cg.cross_abcd.a1_x_b1_x_c2_x_d0: 0 -cg.cross_abcd.a1_x_b1_x_c2_x_d1: 0 -cg.cross_abcd.a1_x_b1_x_c2_x_d2: 0 -cg.cross_abcd.a1_x_b2_x_c0_x_d0: 0 -cg.cross_abcd.a1_x_b2_x_c0_x_d1: 0 -cg.cross_abcd.a1_x_b2_x_c0_x_d2: 0 -cg.cross_abcd.a1_x_b2_x_c1_x_d0: 0 -cg.cross_abcd.a1_x_b2_x_c1_x_d1: 0 -cg.cross_abcd.a1_x_b2_x_c1_x_d2: 0 -cg.cross_abcd.a1_x_b2_x_c2_x_d0: 0 -cg.cross_abcd.a1_x_b2_x_c2_x_d1: 0 -cg.cross_abcd.a1_x_b2_x_c2_x_d2: 0 -cg.cross_abcd.a2_x_b0_x_c0_x_d0: 0 -cg.cross_abcd.a2_x_b0_x_c0_x_d1: 0 -cg.cross_abcd.a2_x_b0_x_c0_x_d2: 0 -cg.cross_abcd.a2_x_b0_x_c1_x_d0: 0 -cg.cross_abcd.a2_x_b0_x_c1_x_d1: 0 -cg.cross_abcd.a2_x_b0_x_c1_x_d2: 0 -cg.cross_abcd.a2_x_b0_x_c2_x_d0: 6 -cg.cross_abcd.a2_x_b0_x_c2_x_d1: 0 -cg.cross_abcd.a2_x_b0_x_c2_x_d2: 0 -cg.cross_abcd.a2_x_b1_x_c0_x_d0: 0 -cg.cross_abcd.a2_x_b1_x_c0_x_d1: 0 -cg.cross_abcd.a2_x_b1_x_c0_x_d2: 0 -cg.cross_abcd.a2_x_b1_x_c1_x_d0: 0 -cg.cross_abcd.a2_x_b1_x_c1_x_d1: 0 -cg.cross_abcd.a2_x_b1_x_c1_x_d2: 0 -cg.cross_abcd.a2_x_b1_x_c2_x_d0: 0 -cg.cross_abcd.a2_x_b1_x_c2_x_d1: 0 -cg.cross_abcd.a2_x_b1_x_c2_x_d2: 0 -cg.cross_abcd.a2_x_b2_x_c0_x_d0: 0 -cg.cross_abcd.a2_x_b2_x_c0_x_d1: 0 -cg.cross_abcd.a2_x_b2_x_c0_x_d2: 0 -cg.cross_abcd.a2_x_b2_x_c1_x_d0: 0 -cg.cross_abcd.a2_x_b2_x_c1_x_d1: 0 -cg.cross_abcd.a2_x_b2_x_c1_x_d2: 0 -cg.cross_abcd.a2_x_b2_x_c2_x_d0: 0 -cg.cross_abcd.a2_x_b2_x_c2_x_d1: 0 -cg.cross_abcd.a2_x_b2_x_c2_x_d2: 0 +cg.cross_abcd.a0_x_b0_x_c0_x_d0 [cross]: 10 +cg.cross_abcd.a0_x_b0_x_c0_x_d1 [cross]: 0 +cg.cross_abcd.a0_x_b0_x_c0_x_d2 [cross]: 0 +cg.cross_abcd.a0_x_b0_x_c1_x_d0 [cross]: 0 +cg.cross_abcd.a0_x_b0_x_c1_x_d1 [cross]: 0 +cg.cross_abcd.a0_x_b0_x_c1_x_d2 [cross]: 0 +cg.cross_abcd.a0_x_b0_x_c2_x_d0 [cross]: 0 +cg.cross_abcd.a0_x_b0_x_c2_x_d1 [cross]: 0 +cg.cross_abcd.a0_x_b0_x_c2_x_d2 [cross]: 0 +cg.cross_abcd.a0_x_b1_x_c0_x_d0 [cross]: 0 +cg.cross_abcd.a0_x_b1_x_c0_x_d1 [cross]: 0 +cg.cross_abcd.a0_x_b1_x_c0_x_d2 [cross]: 0 +cg.cross_abcd.a0_x_b1_x_c1_x_d0 [cross]: 0 +cg.cross_abcd.a0_x_b1_x_c1_x_d1 [cross]: 0 +cg.cross_abcd.a0_x_b1_x_c1_x_d2 [cross]: 0 +cg.cross_abcd.a0_x_b1_x_c2_x_d0 [cross]: 0 +cg.cross_abcd.a0_x_b1_x_c2_x_d1 [cross]: 0 +cg.cross_abcd.a0_x_b1_x_c2_x_d2 [cross]: 0 +cg.cross_abcd.a0_x_b2_x_c0_x_d0 [cross]: 0 +cg.cross_abcd.a0_x_b2_x_c0_x_d1 [cross]: 0 +cg.cross_abcd.a0_x_b2_x_c0_x_d2 [cross]: 0 +cg.cross_abcd.a0_x_b2_x_c1_x_d0 [cross]: 0 +cg.cross_abcd.a0_x_b2_x_c1_x_d1 [cross]: 0 +cg.cross_abcd.a0_x_b2_x_c1_x_d2 [cross]: 0 +cg.cross_abcd.a0_x_b2_x_c2_x_d0 [cross]: 0 +cg.cross_abcd.a0_x_b2_x_c2_x_d1 [cross]: 0 +cg.cross_abcd.a0_x_b2_x_c2_x_d2 [cross]: 0 +cg.cross_abcd.a1_x_b0_x_c0_x_d0 [cross]: 0 +cg.cross_abcd.a1_x_b0_x_c0_x_d1 [cross]: 0 +cg.cross_abcd.a1_x_b0_x_c0_x_d2 [cross]: 0 +cg.cross_abcd.a1_x_b0_x_c1_x_d0 [cross]: 5 +cg.cross_abcd.a1_x_b0_x_c1_x_d1 [cross]: 0 +cg.cross_abcd.a1_x_b0_x_c1_x_d2 [cross]: 0 +cg.cross_abcd.a1_x_b0_x_c2_x_d0 [cross]: 0 +cg.cross_abcd.a1_x_b0_x_c2_x_d1 [cross]: 0 +cg.cross_abcd.a1_x_b0_x_c2_x_d2 [cross]: 0 +cg.cross_abcd.a1_x_b1_x_c0_x_d0 [cross]: 0 +cg.cross_abcd.a1_x_b1_x_c0_x_d1 [cross]: 0 +cg.cross_abcd.a1_x_b1_x_c0_x_d2 [cross]: 0 +cg.cross_abcd.a1_x_b1_x_c1_x_d0 [cross]: 0 +cg.cross_abcd.a1_x_b1_x_c1_x_d1 [cross]: 0 +cg.cross_abcd.a1_x_b1_x_c1_x_d2 [cross]: 0 +cg.cross_abcd.a1_x_b1_x_c2_x_d0 [cross]: 0 +cg.cross_abcd.a1_x_b1_x_c2_x_d1 [cross]: 0 +cg.cross_abcd.a1_x_b1_x_c2_x_d2 [cross]: 0 +cg.cross_abcd.a1_x_b2_x_c0_x_d0 [cross]: 0 +cg.cross_abcd.a1_x_b2_x_c0_x_d1 [cross]: 0 +cg.cross_abcd.a1_x_b2_x_c0_x_d2 [cross]: 0 +cg.cross_abcd.a1_x_b2_x_c1_x_d0 [cross]: 0 +cg.cross_abcd.a1_x_b2_x_c1_x_d1 [cross]: 0 +cg.cross_abcd.a1_x_b2_x_c1_x_d2 [cross]: 0 +cg.cross_abcd.a1_x_b2_x_c2_x_d0 [cross]: 0 +cg.cross_abcd.a1_x_b2_x_c2_x_d1 [cross]: 0 +cg.cross_abcd.a1_x_b2_x_c2_x_d2 [cross]: 0 +cg.cross_abcd.a2_x_b0_x_c0_x_d0 [cross]: 0 +cg.cross_abcd.a2_x_b0_x_c0_x_d1 [cross]: 0 +cg.cross_abcd.a2_x_b0_x_c0_x_d2 [cross]: 0 +cg.cross_abcd.a2_x_b0_x_c1_x_d0 [cross]: 0 +cg.cross_abcd.a2_x_b0_x_c1_x_d1 [cross]: 0 +cg.cross_abcd.a2_x_b0_x_c1_x_d2 [cross]: 0 +cg.cross_abcd.a2_x_b0_x_c2_x_d0 [cross]: 6 +cg.cross_abcd.a2_x_b0_x_c2_x_d1 [cross]: 0 +cg.cross_abcd.a2_x_b0_x_c2_x_d2 [cross]: 0 +cg.cross_abcd.a2_x_b1_x_c0_x_d0 [cross]: 0 +cg.cross_abcd.a2_x_b1_x_c0_x_d1 [cross]: 0 +cg.cross_abcd.a2_x_b1_x_c0_x_d2 [cross]: 0 +cg.cross_abcd.a2_x_b1_x_c1_x_d0 [cross]: 0 +cg.cross_abcd.a2_x_b1_x_c1_x_d1 [cross]: 0 +cg.cross_abcd.a2_x_b1_x_c1_x_d2 [cross]: 0 +cg.cross_abcd.a2_x_b1_x_c2_x_d0 [cross]: 0 +cg.cross_abcd.a2_x_b1_x_c2_x_d1 [cross]: 0 +cg.cross_abcd.a2_x_b1_x_c2_x_d2 [cross]: 0 +cg.cross_abcd.a2_x_b2_x_c0_x_d0 [cross]: 0 +cg.cross_abcd.a2_x_b2_x_c0_x_d1 [cross]: 0 +cg.cross_abcd.a2_x_b2_x_c0_x_d2 [cross]: 0 +cg.cross_abcd.a2_x_b2_x_c1_x_d0 [cross]: 0 +cg.cross_abcd.a2_x_b2_x_c1_x_d1 [cross]: 0 +cg.cross_abcd.a2_x_b2_x_c1_x_d2 [cross]: 0 +cg.cross_abcd.a2_x_b2_x_c2_x_d0 [cross]: 0 +cg.cross_abcd.a2_x_b2_x_c2_x_d1 [cross]: 0 +cg.cross_abcd.a2_x_b2_x_c2_x_d2 [cross]: 0