Program Listing for File decoder.cpp
↰ Return to documentation for file (decoder.cpp
)
#include "decoder.hpp"
#include "log.hpp"
#include "publisher.hpp"
#include "device.hpp"
#include "tohost_monitor.hpp"
#include <cstdint>
#include <functional>
Decoder::Decoder(const Elink &elink, Publisher &publisher, flx_tohost_format fmt,
int l0id_decoder_fmt, unsigned int block_size, uint64_t buf_vaddr) :
m_stats(elink.fid), m_elink(elink), m_publisher(publisher),
m_buffer_vaddr(buf_vaddr), m_block_size(block_size)
{
switch (fmt) {
case flx_tohost_format::TOHOST_SUBCHUNK_TRAILER:
m_decode = [this](Block& b){return decode_subchunk_trailers(b);};
break;
case flx_tohost_format::TOHOST_SUBCHUNK_HEADER:
m_decode = [this](Block& b){return decode_subchunk_headers(b);};
break;
default:
LOG_ERR("ToHost data format %d not recognised. Assuming TOHOST_SUBCHUNK_TRAILER");
m_decode = [this](Block& b){return decode_subchunk_trailers(b);};
}
if ( l0id_decoder_fmt > 0 ){
if (elink.type == elink_type_t::TTC) {
m_l0id_checker = std::make_unique<L0Decoder>(L0Decoder(elink.fid, 1));
}
else if (elink.type == elink_type_t::DAQ) {
m_l0id_checker = std::make_unique<L0Decoder>(L0Decoder(elink.fid, l0id_decoder_fmt));
}
}
}
Publisher::Result Decoder::check_block_integrity(Block & block)
{
#if REGMAP_VERSION < 0x0500
uint16_t* marker_word = (uint16_t*)(&block);
if (block.marker != 0xABCD and marker_word[0] != marker_word[1])
{
LOG_ERR("Received invalid block header 0x%x. Block discarded.", block.marker);
m_stats.increment_dropped_blocks();
return Publisher::DECODING_ERROR;
}
#else
unsigned bsize = (block.marker == 0xABCD) ? 1024 : ((block.marker >> 8) - 0xC0 + 1) * 1024;
if (bsize != m_block_size) {
LOG_ERR("received block header 0x%x with irregular block size %d, expected %d. Block discarded.",
block.marker, bsize, m_block_size);
m_stats.increment_dropped_blocks();
return Publisher::DECODING_ERROR;
}
#endif
//Sequence number check
uint8_t expected_seqnr = (m_last_seqnr + 1) % 32;
if(block.sequence_number != expected_seqnr)
{
if(!(m_seqnr_err++ % 100)){
LOG_DBG("received wrong sequence number: %d instead of %d (E-link: %d)",
block.sequence_number, expected_seqnr, block.elink);
}
}
m_last_seqnr = block.sequence_number;
m_last_block = (reinterpret_cast<uint64_t>(&block) - m_buffer_vaddr) & 0xffffffff;
return Publisher::Result::OK;
}
Publisher::Result Decoder::decode(Block & block)
{
return m_decode(block);
}
Publisher::Result Decoder::decode_subchunk_headers(Block & block)
{
using subchunk_header_t = subchunk_trailer_t;
const bool first_time_decoding = (m_chunk_position == 0);
if (first_time_decoding) {
auto ret = check_block_integrity(block);
if (ret == Publisher::Result::DECODING_ERROR) {
return ret;
}
}
Publisher::Result r = Publisher::OK;
while (true) {
const auto chunk_header = *reinterpret_cast<subchunk_header_t*>(block.data + m_chunk_position);
uint16_t subchk_size = chunk_header.data.length;
uint16_t length = subchk_size + sizeof(subchunk_header_t) + (-subchk_size % sizeof(subchunk_header_t));
uint16_t next = m_chunk_position + length;
if (next > sizeof(block.data)) {
LOG_ERR("Block 0x%lx of fid 0x%lx (elink 0x%x) discarded due to broken chunk length %u. Next position %u, header value 0x%x",
&block, m_elink.fid, m_elink.lid, length, next, chunk_header.value);
m_chunk_position = 0;
m_stats.increment_processed_blocks();
return Publisher::DECODING_ERROR;
}
uint8_t fw_flags = (chunk_header.data.trunc) | (chunk_header.data.err << 2) | (chunk_header.data.crcerr << 3);
r = post_subchunk(block.data + m_chunk_position + sizeof(subchunk_header_t),
subchk_size,
static_cast<Decoder::SubchunkType>(chunk_header.data.type),
fw_flags);
if (r == Publisher::AGAIN or r == Publisher::PARTIAL) {
return r;
}
if (r == Publisher::DECODING_ERROR) {
m_scratch.clear();
break;
}
if (r == Publisher::ERROR) {
m_scratch.clear();
}
if (next == sizeof(block.data)) {
if(m_elink.type == elink_type_t::TTC){
m_publisher.flush(m_elink.fid);
}
break;
}
m_chunk_position = next;
}
m_stats.increment_processed_blocks();
m_chunk_position = 0;
return r;
}
Publisher::Result Decoder::decode_subchunk_trailers(Block & block)
{
//No chunks already decoded waiting to be sent
if (m_subchunks.empty()) {
auto ret = check_block_integrity(block);
if (ret != Publisher::Result::OK){
return ret;
}
//Starting from the end of the block, save location of all subchunk trailers
int pos = m_block_size - BLOCK_HEADER_SIZE;
unsigned int trailer_size = sizeof(subchunk_trailer_t);
while (pos > 0) {
subchunk_trailer_t trailer = *(reinterpret_cast<subchunk_trailer_t*>(block.data + pos - trailer_size));
#if REGMAP_VERSION < 0x0500
// Check for FE BUSY
if (trailer.value == 0xE05C) {
pos -= sizeof(subchunk_trailer_t);
trailer = *(reinterpret_cast<subchunk_trailer_t*>(block.data + pos - trailer_size));
}
#endif
uint32_t length = trailer.data.length;
//padding: (-length % trailer_size)
pos -= length + trailer_size + (-length % trailer_size);
if (pos < 0) {
LOG_ERR("Block 0x%lx of fid 0x%lx (elink 0x%x) discarded due to broken chunk length %u. Decoding position %u, trailer value 0x%x",
block.data, m_elink.fid, m_elink.lid, length, pos, trailer.value);
return Publisher::DECODING_ERROR;
}
SubchunkType t = (SubchunkType)trailer.data.type;
if ((t == TIMEOUT and !trailer.data.trunc) or t == NIL or t == OOB) {
continue;
}
uint16_t p = static_cast<uint16_t>(pos);
m_subchunks.emplace_back(p, trailer.value);
}
}
//Process and publish subchunks
while (not m_subchunks.empty()) {
const auto & sc = m_subchunks[m_subchunks.size() - 1];
subchunk_trailer_t tr;
tr.value = sc.second;
uint8_t fw_flags = (tr.data.trunc) | (tr.data.err << 2) | (tr.data.crcerr << 3);
auto r = post_subchunk(block.data + sc.first, tr.data.length, (Decoder::SubchunkType)tr.data.type, fw_flags);
if (r == Publisher::AGAIN) {
return r;
}
if (r == Publisher::ERROR) {
m_stats.increment_dropped_blocks();
m_subchunks.clear();
m_scratch.clear();
return r;
}
m_subchunks.resize(m_subchunks.size() - 1);
if(m_elink.type == elink_type_t::TTC && m_subchunks.empty()){
m_publisher.flush(m_elink.fid);
}
}
m_stats.increment_processed_blocks();
return Publisher::OK;
}
void Decoder::on_successful_send()
{
m_stats.update_processed_chunk(m_scratch.get_status_byte(), m_scratch.byte_size());
if (m_l0id_checker) {
if ( m_l0id_checker->check_tohost_chunk(m_scratch.iov) ) {
m_stats.increment_oosequence_l0id();
}
}
if ( m_elink.has_streams ){ m_elink.fid &= 0xFFFFFFFFFFFFFF00; }
m_scratch.clear();
}
Publisher::Result Decoder::post_subchunk(
uint8_t* data, uint32_t length, SubchunkType type, uint8_t fw_flags)
{
//First switch case mostly addresses error conditions
switch(type) {
case FIRST:
case WHOLE:
if (not m_scratch.empty()) {
//If scratch space not empty mark trucantion and publish leftover
m_scratch.update_status_byte(SW_TRUNC);
auto r = m_publisher.publish(m_elink.fid, m_scratch.iov_addr(), m_scratch.iov_len(), m_scratch.byte_size(), m_last_block, m_scratch.get_status_byte());
if (Publisher::OK != r) {
return r;
} else {
on_successful_send();
}
}
break;
case LAST:
case MIDDLE:
if (m_scratch.empty()) {
//If the scratch empty mark truncation and add subschunk
m_scratch.update_status_byte(SW_TRUNC);
}
m_scratch.push_back(iovec{data, length});
break;
case TIMEOUT:
if (fw_flags & FW_TRUNC) {
return post_subchunk(data, length, m_scratch.empty() ? WHOLE : LAST, fw_flags);
} else {
//Discarded if no truncation flagged by firmware
return Publisher::OK;
}
case NIL:
case OOB:
return Publisher::OK;
default:
LOG_ERR("invalid subchunk type=%d", type);
return Publisher::DECODING_ERROR;
}
//Second switch case, no error conditions
switch(type) {
case FIRST:
{
m_scratch.update_status_byte(fw_flags);
m_scratch.push_back(iovec{data, length});
if ( m_elink.has_streams ){ m_elink.fid |= data[0]; }
break;
}
case WHOLE:
{
m_scratch.update_status_byte(fw_flags);
m_scratch.push_back(iovec{data, length});
if ( m_elink.has_streams ){ m_elink.fid |= data[0]; }
auto r = m_publisher.publish(m_elink.fid, m_scratch.iov_addr(), m_scratch.iov_len(), m_scratch.byte_size(), m_last_block, m_scratch.get_status_byte());
if (Publisher::AGAIN == r) {
m_scratch.remove_last_entry();
} else {
on_successful_send();
}
return r;
}
case LAST:
{
auto r = m_publisher.publish(m_elink.fid, m_scratch.iov_addr(), m_scratch.iov_len(), m_scratch.byte_size(), m_last_block, m_scratch.get_status_byte());
if (Publisher::AGAIN == r) {
m_scratch.remove_last_entry();
} else {
on_successful_send();
}
return r;
}
default:
break;
}
return Publisher::OK;
}
ToHostElinkStats Decoder::get_decoder_stats_increment(ToHostElinkStats & previous)
{
return m_stats.get_increment(previous);
}