Program Listing for File l0id_decoder.cpp

Return to documentation for file (l0id_decoder.cpp)

#include <cstring>
#include <format>
#include "l0id_decoder.hpp"
#include "ers/ers.h"



L0Decoder::L0Decoder(uint64_t fid, int format)
    : check_sequence_error([](const void*, size_t) -> bool { return false; })
    , m_fid(fid)
{
    m_last.xl1id = 0x00ffffff;

    switch (format)
    {
    case 0:
        break;
    case L0ID_FMT::TTC2H:
        ERS_INFO("L1ID sequentiality check enabled: TTC2H only");
        check_sequence_error = std::bind(&L0Decoder::check_sequence_ttc2h, this, std::placeholders::_1, std::placeholders::_2);
        m_required_bytes = 8;
        m_ec_mask = 0x00ffffff;
        break;
    case L0ID_FMT::LATOME:
        ERS_INFO("L1ID sequentiality check enabled: TTC2H, LATOME data");
        check_sequence_error = std::bind(&L0Decoder::check_sequence_latome, this, std::placeholders::_1, std::placeholders::_2);
        m_required_bytes = 48;
        m_ec_mask = 0x00ffffff;
        break;
    case L0ID_FMT::FMEMU:
        ERS_INFO("L1ID sequentiality check enabled: TTC2H, FMEMU data");
        check_sequence_error = std::bind(&L0Decoder::check_sequence_fmemu, this, std::placeholders::_1, std::placeholders::_2);
        m_required_bytes = 4;
        m_ec_mask = 0x00ffffff;
        break;
    case L0ID_FMT::FELIG:
        ERS_INFO("L1ID sequentiality check enabled: TTC2H, FELIG data");
        check_sequence_error = std::bind(&L0Decoder::check_sequence_felig, this, std::placeholders::_1, std::placeholders::_2);
        m_required_bytes = 4;
        m_ec_mask = 0x0000ffff;
        break;
    case L0ID_FMT::NSW_VMM:
        ERS_INFO("L1ID sequentiality check enabled: TTC2H, NSW VMM data");
        check_sequence_error = std::bind(&L0Decoder::check_sequence_nsw_vmm, this, std::placeholders::_1, std::placeholders::_2);
        m_required_bytes = 4;
        m_ec_mask = 0x0000ffff;
        break;
    case L0ID_FMT::NSW_TP:
        ERS_INFO("L1ID sequentiality check enabled: TTC2H, NSW TP data");
        check_sequence_error = std::bind(&L0Decoder::check_sequence_nsw_tp, this, std::placeholders::_1, std::placeholders::_2);
        m_required_bytes = 8;
        m_ec_mask =  0x00ffffff;
        break;
    default:
        ers::warning(felix_log::l0id_decoder_issue(std::format("Check disabled. Invalid choice for L1ID sequentiality check, received {}", format)));
    }
}


bool L0Decoder::check_tohost_chunk(std::vector<iovec> const &data)
{
    std::vector<uint8_t> chunk(m_required_bytes);
    unsigned int copied = 0;

    for (unsigned int idx = 1; idx < data.size(); ++idx) {
        if (data[idx].iov_base == nullptr) {
            continue;
        }

        const unsigned int remaining = m_required_bytes - copied;
        const unsigned int to_copy = std::min(static_cast<unsigned int>(data[idx].iov_len), remaining);

        memcpy(chunk.data() + copied, data[idx].iov_base, to_copy);
        copied += to_copy;

        if (copied >= m_required_bytes) {
            break;
        }
    }

    return check_sequence_error(chunk.data(), copied);
}


bool inline L0Decoder::compare(xl1id_t current, xl1id_t expected)
{
  if(current.fields.ec != expected.fields.ec){
    ERS_INFO(std::format("fid {:#x} ({:#08x}) has L1ID {:#08x}, expected {:#x}",
                     m_fid, get_elink(m_fid), static_cast<u_int32_t>(current.fields.ec), static_cast<u_int32_t>(expected.fields.ec)));
    return true;
  }
  return false;
}


bool L0Decoder::check_sequence_ttc2h(const uint8_t* data, size_t len)
{
    if (len < m_required_bytes)
    {
        ERS_INFO(std::format("TTC2H message from fid {:#x} ({:#x}) too small to retrieve XL1ID, size {}.", m_fid, get_elink(m_fid), len));
        return true;
    }
    xl1id_t current, expected;
    current.xl1id = reinterpret_cast<const uint32_t *>(data)[1];

    [[maybe_unused]] bcid_t current_bc;
    current_bc.bc_data = reinterpret_cast<const uint32_t *>(data)[0];

    ERS_DEBUG(1, std::format("Current L1ID {:#8x} BCID {:#6x}", static_cast<uint32_t>(current.fields.ec), static_cast<uint16_t>(current_bc.fields.bcid)));
    if (current.fields.ecrc == (m_last.fields.ecrc + 1))
    {
        ERS_INFO(std::format("ECR on TTC2H fid {:#x} ({:#x})", m_fid, get_elink(m_fid)));
        expected.fields.ec = 0x0;
    }
    else
    {
        expected.fields.ec = (m_last.fields.ec + 1) & 0x00ffffff;
    }
    m_last.xl1id = current.xl1id;
    return compare(current, expected);
}


bool L0Decoder::check_sequence_latome(const uint8_t* data, size_t len)
{
    if (len < m_required_bytes)
    {
        ERS_INFO(std::format("LATOME message from fid {:#x} ({:#x}) too small to retrieve XL1ID, size {}.", m_fid, get_elink(m_fid), len));
        return true;
    }
    xl1id_t current, expected;
    current.xl1id = reinterpret_cast<const uint32_t *>(data)[5];

    if (current.fields.ecrc == (m_last.fields.ecrc + 1))
    {
        ERS_DEBUG(1, std::format( "ECR on fid {:#x} ({:#x})", m_fid, get_elink(m_fid)));
        expected.fields.ec = 0x0;
    }
    else
    {
        expected.fields.ec = (m_last.fields.ec + 1) & 0x00ffffff;
    }
    m_last.xl1id = current.xl1id;
    return compare(current, expected);
}


bool L0Decoder::check_sequence_fmemu(const uint8_t* data, size_t len)
{
    if (len < m_required_bytes)
    {
        ERS_INFO(std::format("FMEMU message from fid {:#x} ({:#x}) too small to retrieve XL1ID, size {}.", m_fid, get_elink(m_fid), len));
        return true;
    }
    xl1id_t current, expected;
    current.xl1id = reinterpret_cast<const uint32_t *>(data)[0];
    if (current.fields.ecrc == (m_last.fields.ecrc + 1))
    {
        ERS_DEBUG(1, std::format( "ECR on fid {:#x} ({:#x})", m_fid, get_elink(m_fid)));
        expected.fields.ec = 0x0;
    }
    else
    {
        expected.fields.ec = (m_last.fields.ec + 1) & 0x00ffffff;
    }
    m_last.xl1id = current.xl1id;
    return compare(current, expected);
}


bool L0Decoder::check_sequence_felig(const uint8_t* data, size_t len)
{
    if (len < m_required_bytes)
    {
        ERS_INFO(std::format("FELIG message from fid {:#x} ({:#x}) smaller than FELIG header, size {}.", m_fid, get_elink(m_fid), len));
        return true;
    }
    xl1id_t current, expected;
    uint8_t* chunk_data = const_cast<uint8_t*>(data);
    current.fields.ec = ( chunk_data[4] | (chunk_data[3] << 8) );
    expected.fields.ec = (m_last.fields.ec + 1) & 0x0000ffff;
    m_last.xl1id = current.xl1id;
    return compare(current, expected);
}


bool L0Decoder::check_sequence_nsw_vmm(const uint8_t* data, size_t len)
{
    if (len < 2){
        ERS_INFO(std::format("NSW VMM message from fid {:#x} ({:#x}) too small to retrieve header, size {}.",
            m_fid, get_elink(m_fid), len));
        return true;
    }
    if (len > 4*(512+4)) {
        ERS_INFO(std::format("NSW VMM message from fid {:#x} ({:#x}) too long, size {}.", m_fid, get_elink(m_fid), len));
    }
    xl1id_t current, expected;
    //enum packetType { FULL_MM=0, NULL_EVENT=1, FULL_STGC=2 , ILLEGAL=3};
    unsigned char headerType = data[0] >> 6;
    switch (headerType)
    {
    case 0: // MM   full header
    case 2: // sTGC full header
        current.fields.ec = (data[3] | (data[2] << 8)) & 0xff;
        expected.fields.ec = (m_last.fields.ec + 1) & 0xff;
        m_last.fields.ec = (data[3] | (data[2] << 8)) & 0xffff;
        m_ec_mask = 0xffff;
        break;
    case 1: // null header
        current.fields.ec = data[1];
        expected.fields.ec = (m_last.fields.ec + 1) & 0xff;
        m_last.fields.ec = data[1];
        m_ec_mask = 0xff;
        break;
    case 3: // illegal header type
    {
        unsigned int vmmHeader = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
        ERS_INFO(std::format("Illegal VMM message from fid {:#x} ({:#x}), headerType = 3, headerword = {:#x}, size {}.", m_fid, get_elink(m_fid), vmmHeader, len));
        break;
    }
    default:
        ERS_INFO(std::format("Invalid header from fid {:#x} {:#x}, type {:#x}", m_fid, get_elink(m_fid), headerType));
    }

    return compare(current, expected);
}


bool L0Decoder::check_sequence_nsw_tp(const uint8_t* data, size_t len)
{
    if (len < m_required_bytes)
    {
        ERS_INFO(std::format("NSW TP message from fid {:#x} ({:#x}) too small to retrieve XL1ID, size {}.", m_fid, get_elink(m_fid), len));
        return true;
    }
    xl1id_t current, expected;
    current.fields.ec =  ( data[5]<<16 | data[6]<<8 | data[7] ) & 0x00ffffff;
    current.fields.ecrc = (data[4]<<24) & 0xff;
    if(current.fields.ecrc == (m_last.fields.ecrc+1) ){
      ERS_DEBUG(1, std::format( "ECR on fid {:#x} ({:#x})", m_fid, get_elink(m_fid)));
      expected.fields.ec = 0x0;
    } else{
      expected.fields.ec = (m_last.fields.ec+1) & 0x00ffffff;
    }
    m_last.xl1id = current.xl1id;
    return compare(current, expected);
}