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);
}