Commit 2c039d6a authored by Volker Friese's avatar Volker Friese
Browse files

Lean version of TRD unpacking algorithm

parent 48c21b9e
/**
* @file CbmTrdUnpackSpadic.h
* @author David Schledt (praisig@ikf.uni-frankfurt.de)
* @author Pascal Raisig (praisig@ikf.uni-frankfurt.de)
* @copyright Copyright (c) 2021
*/
#include "CbmTrdUnpackSpadic.h"
//#include "CbmTrdParManager.h"
//#include "CbmTrdParSetAsic.h"
#include "CbmTrdRawMessageSpadic.h"
#include "MicrosliceDescriptor.hpp"
#include "Timeslice.hpp"
#include <FairTask.h>
#include <Logger.h>
#include <RtypesCore.h>
#include <typeinfo>
#include <algorithm>
#include <cstdint>
#include <memory>
using std::pair;
using std::vector;
// ---- Constructor ----
CbmTrdUnpackSpadic::CbmTrdUnpackSpadic() { }
// ---- Destructor ----
CbmTrdUnpackSpadic::~CbmTrdUnpackSpadic() { }
// ---- ExtractSample ----
int16_t CbmTrdUnpackSpadic::ExtractSample(size_t adcBuffer, size_t& nAdcBits)
{
// An ADC sample consists of 9 bit
if (nAdcBits < 9) {
LOG(fatal) << GetName()
<< "::ExtractSample can not extract samples from a buffer with less than 9 bits";
}
nAdcBits -= 9;
int16_t temp = 0x1ff;
temp &= (adcBuffer >> (nAdcBits));
int16_t sample = (temp & 0x0100) ? (temp | 0xff00) : temp;
return sample;
}
// ---- Frame type ----
Spadic::MsMessageType CbmTrdUnpackSpadic::FrameType(const std::uint32_t frame)
{
std::uint32_t checkframe = frame;
checkframe &= 0xffffff; // consider only the last 24 bits, stripping the eLink info
// SOM (start of message), msb 001...
if ((checkframe >> 21) == 1) return Spadic::MsMessageType::kSOM;
// RDA (data frame), msb 01...
else if ((checkframe >> 22) == 1) return Spadic::MsMessageType::kRDA;
// EOM (end of message), msb 0001...
else if ((checkframe >> 20) == 1) return Spadic::MsMessageType::kEOM;
// TS_MSB (epoch marker), msb 11...
else if ((checkframe >> 22) == 3) return Spadic::MsMessageType::kEPO;
// INF (info)
else if (0 < (checkframe >> 18) && (checkframe >> 18) <= 3) return Spadic::MsMessageType::kINF;
// NUL (null frame, last word in the microslice)
else if (checkframe == 0) return Spadic::MsMessageType::kNUL;
// UNK (unknown, not a SPADIC frame)
else return Spadic::MsMessageType::kUNK;
}
// ---- Frame type info ----
const char* CbmTrdUnpackSpadic::FrameTypeInfo(const std::uint32_t frame)
{
if (FrameType(frame) == Spadic::MsMessageType::kSOM) return "SOM";
if (FrameType(frame) == Spadic::MsMessageType::kRDA) return "RDA";
if (FrameType(frame) == Spadic::MsMessageType::kEOM) return "EOM";
if (FrameType(frame) == Spadic::MsMessageType::kEPO) return "EPO";
if (FrameType(frame) == Spadic::MsMessageType::kINF) return "INF";
if (FrameType(frame) == Spadic::MsMessageType::kNUL) return "NUL";
return "UNK";
}
// ---- MakeRaw ----
CbmTrdRawMessageSpadic CbmTrdUnpackSpadic::MakeRaw(const std::uint32_t frame,
uint16_t criId,
uint8_t iStream)
{
// Should be called only for SOM frames
assert(FrameType(frame) == Spadic::MsMessageType::kSOM);
// CROB is is always zero (?)
// TODO: What is it good for, then?
uint8_t crobId = 0;
// Independent of the frame type the elinkId is written into bit 31...24
auto elink = static_cast<std::uint8_t>((frame >> 24) & 0x3f);
auto channel = static_cast<std::uint8_t>(((frame >> 17) & 0xf));
auto timestamp = static_cast<std::uint16_t>((frame >> 9) & 0xff);
bool multihit = ((frame >> 8) & 0x1);
auto hitType = static_cast<std::uint8_t>((frame >> 6) & 0x3);
std::uint8_t nsamples = 0;
std::vector<std::int16_t> samples = std::vector<std::int16_t>(0);
// Full time in units of clock cycles
size_t fulltime = fMsStartTimeCC + (fNrTsmsb.at(iStream) * fTsmsbLengthCC) + timestamp;
// Create message
CbmTrdRawMessageSpadic retval(channel, elink, crobId, criId, hitType, nsamples, multihit,
fulltime, samples);
return retval;
}
// ---- Unpack ----
std::pair<std::vector<CbmTrdRawMessageSpadic>, CbmRecoTrdUnpackMoni>
CbmTrdUnpackSpadic::Unpack(const fles::Timeslice& ts, uint64_t component,
const CbmRecoTrdUnpackPar& /*par*/)
{
LOG(info) << "UnpackSpadic component " << component << " Microslices " << ts.num_microslices(component);;
// Create output vector
vector<CbmTrdRawMessageSpadic> output = { };
// Create monitor data container
CbmRecoTrdUnpackMoni monitor = { };
// Loop over microslices (including overlap)
for (UInt_t iMslice = 0; iMslice < ts.num_microslices(component); iMslice++) {
UnpackMicroslice(ts, component, iMslice, output);
}
return make_pair(output, monitor);
}
// ---- Unpack microslice ----
bool CbmTrdUnpackSpadic::UnpackMicroslice(const fles::Timeslice& ts, std::uint16_t iComp, UInt_t imSlice,
vector<CbmTrdRawMessageSpadic>& output)
{
bool unpackOk = true;
// Microslice descriptor
auto msDesc = ts.descriptor(iComp, imSlice);
// CRI id
uint16_t criId = msDesc.eq_id;
// Microslice start time in clock cycles
fMsStartTimeCC = msDesc.idx / fClockCycle;
// Microslice size in Bytes
auto msSize = msDesc.size;
// Number of complete words in the microslice
uint32_t nWords = msSize / fBytesPerWord;
LOG(debug) << "Unpack component " << iComp << " microslice " << imSlice << " Words " << nWords;
// Microslice content
const auto msContent = reinterpret_cast<const uint64_t*>(ts.content(iComp, imSlice));
// Loop over data words in microslice
// Each word is comprised of a number of frames (2 for the time being). A frame is a 32 bit unit
// in which the data are transported. The frames are arranged in parallel streams, such that
// the loop over the first frame in each word is the first stream, etc.
for (uint32_t iStream = 0; iStream < fStreamsPerWord; iStream++) {
for (uint32_t iWord = 0; iWord < nWords; ++iWord) {
// Access the actual data word
uint64_t word = static_cast<uint64_t>(msContent[iWord]);
// Access the actual frame from the word (see fStreamsPerWord)
// TODO: Remove hard-coded values or pay into the hard-code kitty
uint32_t frame = (word >> (32 * iStream)) & 0xffffffff;
// Get the type of the frame
auto frameType = FrameType(frame);
//LOG(info) << iStream << " " << iWord << " " << FrameTypeInfo(frame);
// The action now depends on the frame type. A data message consists of an SOM frame,
// several RDA frames, and a EOM frame. There may also be EPO or other frames, but not
// between SOM and EOM.
switch(frameType) {
// Epoch marker
case Spadic::MsMessageType::kEPO: {
fNrTsmsb.at(iStream)++;
// if (!checkTsMsb(frame)) ++fNrNonMajorTsMsb;
// fNrTsMsbVec.at(istream)++;
// fNrEpochMsgs++;
break;
// FIXME in the kEPO msg we also have further flags that should be extracted
}
// Start of message
case Spadic::MsMessageType::kSOM: {
// Create a new raw message and fill it with all information we can get from the SOM frame
CbmTrdRawMessageSpadic raw = MakeRaw(frame, criId, iStream);
// The payload of the message consists of 9-bit ADC samples. The first one is in the
// last 6 bits of the SOM frame.
size_t adcBuffer = frame & 0x3f;
size_t nAdcBits = 6;
size_t nAdcBitsTotal = 6;
size_t iSample = 0;
// Now we loop over all following RDA frames
size_t nRda = 0;
iWord++;
word = static_cast<std::uint64_t>(msContent[iWord]);
frame = (word >> (32 * iStream)) & 0xffffffff;
//LOG(info) << iStream << " " << iWord << " " << FrameTypeInfo(frame);
while (FrameType(frame) == Spadic::MsMessageType::kRDA) {
// We have to count the number of RDA frames for sample reconstruction in EOM
nRda++;
// Check the eLink. Should be the same as in SOM.
auto eLinkId = (frame >> 24) & 0x3f;
if (eLinkId != raw.GetElinkId()) {
LOG(debug) << GetName() << "::UnpackMicroslice: SOM eLinkId("
<< static_cast<std::uint16_t>(raw.GetElinkId())
<< ") does not match the RDA eLinkId(" << eLinkId << ")";
}
// We have 22 ADC data bits per RDA frame (the first 2 bits signal the frame type).
// Lets add them to the buffer...
adcBuffer <<= 22;
adcBuffer |= static_cast<std::uint64_t>((frame & 0x3fffff));
nAdcBits += 22;
nAdcBitsTotal += 22;
// If we have 9 or more bits stored, we can extract n samples
while (nAdcBits >= 9) {
raw.IncNrSamples();
raw.SetSample(ExtractSample(adcBuffer, nAdcBits), iSample);
iSample++;
}
// Next frame
iWord++;
word = static_cast<std::uint64_t>(msContent[iWord]);
frame = (word >> (32 * iStream)) & 0xffffffff;
//LOG(info) << iStream << " " << iWord << " " << FrameTypeInfo(frame);
} //# RDA frames
// The next non-RDA frame should be an EOM.
if (FrameType(frame) != Spadic::MsMessageType::kEOM) {
LOG(error) << GetName() << "::UnpackMicroslice: "
<< "RDA sequence not terminated by an EOM frame!";
continue;
}
// Check eLink; should be the same as in SOM
auto elinkId = (frame >> 24) & 0x3f;
if (elinkId != raw.GetElinkId()) {
LOG(error) << GetName() << "::UnpackMicroslice: EOM eLinkId(" << elinkId
<< ") does not match SOM eLinkId(" << static_cast<std::uint16_t>(raw.GetElinkId()) << ")";
break;
}
// Number of samples indicator = nSamples % 4; is in EOM bits 18 and 19
std::uint8_t nSamplesIndicator = (frame >> 18) & 0x3;
// Number of required samples as indicated
auto nSamplesRequired = (nAdcBitsTotal + 18) / 9;
std::uint8_t nn = nSamplesRequired % 4;
for (std::uint8_t itest = 0; itest < 3; itest++) {
if (nn == nSamplesIndicator) break;
nSamplesRequired--;
nn = nSamplesRequired % 4;
}
// Now extract from the above values the number of required ADC bits from the eom
std::int8_t nRequiredBits = (nSamplesRequired - iSample) * 9 - nAdcBits;
adcBuffer <<= nRequiredBits;
// The EOM frame carries at maximum 18 ADC bits
adcBuffer |= static_cast<std::uint64_t>((frame & 0x3ffff) >> (18 - nRequiredBits));
nAdcBits += nRequiredBits;
while (nAdcBits >= 9) {
raw.IncNrSamples();
raw.SetSample(ExtractSample(adcBuffer, nAdcBits), iSample);
iSample++;
}
// Now the raw message is completed and can be added to the output vector
output.emplace_back(raw);
LOG(info) << "Raw message created";
break;
} //? SOM
// RDA frame out of message context
case Spadic::MsMessageType::kRDA: {
LOG(error) << GetName() << "::UnpackMicroslice: Unexpected wild RDA word";
//fNrWildRda++;
break;
}
// EOM frame out of message context
case Spadic::MsMessageType::kEOM: {
LOG(error) << GetName() << "::UnpackMicroslice: Unexpected wild EOM word";
//fNrWildEom++;
break;
}
// INF
case Spadic::MsMessageType::kINF: {
/// Save info message if needed.
//if (fOutInfoSpadicVec) { fOutInfoSpadicVec->emplace_back(std::make_pair(fLastFulltime, frame)); }
//fNrCreatedInfoMsgs++;
//Spadic::MsInfoType infotype = getInfoType(word);
// "Spadic_Info_Types";
//if (fMonitor) fMonitor->FillHisto(infotype);
break;
}
// Unexpected end of microslice
case Spadic::MsMessageType::kNUL: {
if (iWord != (nWords - 1) || (iStream != (fStreamsPerWord - 1))) {
LOG(error) << GetName() << "::UnpackMicroslice: Null frame but not at end of Microslice.";
//fNrWildNul++;
}
break;
}
// Unknown frame type
case Spadic::MsMessageType::kUNK: {
LOG(error) << GetName() << "::UnpackMicroslice: Unknown frame type - microslice corrupted.";
//++fNrUnknownWords;
return false;
break;
}
default: break;
} //? Frame type switch
} //# data words
} //# streams
return unpackOk;
}
ClassImp(CbmTrdUnpackSpadic)
/**
* @file CbmTrdUnpackSpadic.h
* @author David Schledt (praisig@ikf.uni-frankfurt.de)
* @author Pascal Raisig (praisig@ikf.uni-frankfurt.de)
* @copyright Copyright (c) 2021
*/
#ifndef CBMTRDUNPACKSPADIC_H
#define CBMTRDUNPACKSPADIC_H 1
#include "CbmTrdRawMessageSpadic.h"
#include "CbmRecoTrdUnpackPar.h"
#include "CbmRecoTrdUnpackMoni.h"
//#include <MicrosliceDescriptor.hpp> // fles subsystemId
#include <Timeslice.hpp> // timeslice
#include <vector>
/** @class CbmTrdUnpackSpadic
** @brief Algo class to unpack the TRD-SPADIC raw data stream (2021)
** @author David Schledt (praisig@ikf.uni-frankfurt.de)
** @author Pascal Raisig (praisig@ikf.uni-frankfurt.de)
** @version 0.1
** @date 2021-06-29
**
** The algorithm extract objects of type CbmTrdRawMessageSpadic from the data stream
** for one component of a timeslice. The current implementation holds for mCBM 2021 data.
**/
class CbmTrdUnpackSpadic {
public:
/** @brief Default constructor **/
CbmTrdUnpackSpadic();
/** @brief Destructor **/
virtual ~CbmTrdUnpackSpadic();
/** @brief Copy constructor - not implemented **/
CbmTrdUnpackSpadic(const CbmTrdUnpackSpadic&) = delete;
/** @brief Assignment operator - not implemented **/
CbmTrdUnpackSpadic& operator=(const CbmTrdUnpackSpadic&) = delete;
/** @brief Class name **/
const char* GetName() const { return "TrdUnpackSpadic"; }
/** @brief Unpack one component of a time slice (main API)
** @param ts Read-only reference to timeslice
** @param component Index of time slice component
** @param par Parameter container
** @return Vector of raw messages, monitor data container
**/
std::pair<std::vector<CbmTrdRawMessageSpadic>, CbmRecoTrdUnpackMoni>
Unpack(const fles::Timeslice& ts, uint64_t component, const CbmRecoTrdUnpackPar& par);
protected:
/** @brief Extract one ADC sample from an ADC buffer
** @param[in] adcBuffer
** @param[in,out] nAdcBits
** @return sample
*/
int16_t ExtractSample(size_t adcBuffer, size_t& nAdcBits);
/** @brief Identify the message type of a given 32bit frame inside a Microslice
** @param frame Readout frame (32-bit word)
** @return Frame type
**/
Spadic::MsMessageType FrameType(const uint32_t frame);
/** For debugging **/
const char* FrameTypeInfo(const uint32_t frame);
/** @brief Create a CbmTrdRawMessageSpadic from the frame input
** @param frame Readout frame (32-bit word)
** @param msDesc Microslice descriptor
** @param iStream Stream number
** @return Raw message
** @todo Check if we can get rid of the future obsolete microslice stuff.
**/
CbmTrdRawMessageSpadic MakeRaw(const uint32_t frame, uint16_t criId, uint8_t iStream);
/** @brief Unpack a microslice
** @param ts Timeslice reference (read only)
** @param component Index to the component to be unpacked
** @param iMslice Index of the microslice to be unpacked
** @return True of unpacking was successful, else false
** @remark The content of the microslice can only be accessed via the timeslice.
** Hence, we need to pass the reference to the full timeslice
**/
bool UnpackMicroslice(const fles::Timeslice& ts, uint16_t component, UInt_t imslice,
std::vector<CbmTrdRawMessageSpadic>& output);
private: // constants
/** @brief SPADIC clock cycle in ns **/
static constexpr double fClockCycle = 62.5;
/** @brief Bytes per SPADIC frame stored in the microslices **/
static const std::uint8_t fBytesPerWord = 8;
/** @brief Number of streams per word
** For the msg format used from 2021 ongoing we have 2 parallel streams per word.
** All data from eLinks 0..20 go to one stream and 21..41 to the other
**/
static const std::uint8_t fStreamsPerWord = 2;
/** @brief length of one ts_msb in [ns] **/
static constexpr uint16_t fTsmsbLength = 16000;
/** @brief length of one ts_msb in clock cycles **/
size_t fTsmsbLengthCC = fTsmsbLength / fClockCycle;
private: // data members
/** @brief Counter for the ts_msb used to reconstruct the time */
std::vector<uint8_t> fNrTsmsb = {fStreamsPerWord, 0};
/** @brief Start time of the current µSlice in Spadic CC */
uint64_t fMsStartTimeCC = 0;
private:
ClassDef(CbmTrdUnpackSpadic, 1)
};
#endif // CbmTRDUNPACKSPADIC_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment