.. _program_listing_file_decoder.cpp: Program Listing for File decoder.cpp ==================================== |exhale_lsh| :ref:`Return to documentation for file ` (``decoder.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "decoder.hpp" #include "log.hpp" #include "publisher.hpp" #include "device.hpp" #include "tohost_monitor.hpp" #include #include 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(elink.fid, 1)); } else if (elink.type == elink_type_t::DAQ) { m_l0id_checker = std::make_unique(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(&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(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(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(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(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(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); }