From 876ed7df22933364cf16d61a7914a1eeba4e31f7 Mon Sep 17 00:00:00 2001 From: Ondrej Kovanda <okovanda@lxplus729.cern.ch> Date: Mon, 13 May 2024 22:53:08 +0200 Subject: [PATCH] Adding the encoder and generator files --- .../src/HitMapGenerator.cpp | 75 +++++++ .../src/HitMapGenerator.h | 52 +++++ .../src/ItkpixEncoder.cpp | 196 ++++++++++++++++++ .../ITkPixelByteStreamCnv/src/ItkpixEncoder.h | 68 ++++++ .../ITkPixelByteStreamCnv/src/ItkpixLayout.h | 42 ++++ .../src/Itkpixv2Encoder.cpp | 43 ++++ .../src/Itkpixv2Encoder.h | 24 +++ 7 files changed, 500 insertions(+) create mode 100644 InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/HitMapGenerator.cpp create mode 100644 InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/HitMapGenerator.h create mode 100644 InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/ItkpixEncoder.cpp create mode 100644 InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/ItkpixEncoder.h create mode 100644 InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/ItkpixLayout.h create mode 100644 InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/Itkpixv2Encoder.cpp create mode 100644 InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/Itkpixv2Encoder.h diff --git a/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/HitMapGenerator.cpp b/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/HitMapGenerator.cpp new file mode 100644 index 000000000000..cdcf694ee04e --- /dev/null +++ b/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/HitMapGenerator.cpp @@ -0,0 +1,75 @@ +/* +* Author: Ondra Kovanda, ondrej.kovanda at cern.ch +* Date: 02/2024 +* Description: Generation of hits for ITkPix* emulation +*/ +#include "HitMapGenerator.h" + +HitMapGenerator::HitMapGenerator(const uint nCol, const uint nRow, const uint nColInCCol, const uint nRowInQRow, const uint seed): m_nCol(nCol), m_nRow(nRow), m_nColInCCol(nColInCCol), m_nRowInQRow(nRowInQRow), m_seed(seed){ + //initialize the pdfs for assigning a hit and for + //the ToT value of that hit + m_hitProb = std::uniform_real_distribution<float>(0., 1.); + m_totProb = std::uniform_int_distribution<uint16_t>(1, 15); + + m_nCCol = nCol/m_nColInCCol; + m_nQRow = nRow/m_nRowInQRow; + + generator.seed(m_seed); + +} + +void HitMapGenerator::setSeed(const uint seed){ + m_seed = seed; + generator.seed(m_seed); +} + +void HitMapGenerator::randomQCore(const uint CCol, const uint QRow){ + //These are the coordinates of the top left corner of the QCore + const int m_col = CCol * m_nColInCCol; + const int m_row = QRow * m_nRowInQRow; + + //Loop through pixels in that QCore, generate a random hit for each + //and store the truth accordingly. Loop "backwards" to get the + //truth hits in the same order as the default Yarr decoder + for (int pixRow = m_row + m_nRowInQRow -1; pixRow >= m_row; pixRow--){ + for (int pixCol = m_col + m_nColInCCol -1; pixCol >= m_col; pixCol--){ + //does the pixel have a hit? + float hitprob = m_hitProb(generator); + + //std::cout << hitprob << " vs occupancy " << m_occupancy << "\n"; + if (hitprob < m_occupancy){ + //Then give it a tot! + uint16_t tot = m_totProb(generator); + m_hitMap(pixCol, pixRow) = tot;//m_totProb(generator); + + //and save it into the truth output, respecting the + //decoder numbering conventions + m_truthEvt.addHit(pixRow + 1, pixCol + 1, m_hitMap(pixCol, pixRow) - 1); + } + } + } +} + + +void HitMapGenerator::randomHitMap(float occupancy){ + //Generate uniformly random hit map for dev/testing purposes + //initialize the hit map with all zeros + m_hitMap = ItkpixLayout<uint16_t>(); + m_truthEvt = FrontEndEvent(0, 0, m_nGenerated); + m_occupancy = occupancy; + + //Loop over the hit map and fill random tot values. + //In order to preserve the order of hits as it emerges + //from the decoder & ensure easy comparison, the generation + //has to be per-QCore, and keep reverse order of the hits + //compared to the encoding recipe in each QCore. This happens + //in the randomQCore(...) function + for (uint CCol = 0; CCol < m_nCCol; CCol++){ + for (uint QRow = 0; QRow < m_nQRow; QRow++){ + randomQCore(CCol, QRow); + } + + } + + m_nGenerated++; +} \ No newline at end of file diff --git a/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/HitMapGenerator.h b/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/HitMapGenerator.h new file mode 100644 index 000000000000..de1b0e7a1edc --- /dev/null +++ b/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/HitMapGenerator.h @@ -0,0 +1,52 @@ +/* +* Author: Ondra Kovanda, ondrej.kovanda at cern.ch +* Date: 02/2024 +* Description: Generation of hits for ITkPix* emulation +*/ + +#ifndef HITMAPGENERATOR_H +#define HITMAPGENERATOR_H + +#include <vector> +#include <random> +#include <iostream> +#include "EventData.h" +#include "ItkpixLayout.h" + + +class HitMapGenerator{ + + typedef ItkpixLayout<uint16_t> HitMap; + + + public: + HitMapGenerator(const uint nCol = 400, const uint nRow = 384, const uint nColInCCol = 8, const uint nRowInQrow = 2, const uint seed = 0); + + void setSeed(const uint seed = 0); + + void randomHitMap(const float occupancy = 1e-3); + + void randomQCore(const uint CCol, const uint QRow); + + HitMap& outHits(){return m_hitMap;} + + FrontEndEvent& outTruth(){return m_truthEvt;} + + private: + //generation machinery + std::mt19937 generator; + std::uniform_real_distribution<float> m_hitProb; + std::uniform_int_distribution<uint16_t> m_totProb; + + + //geometry and config + uint m_seed, m_nCol, m_nRow, m_nCCol, m_nQRow, m_nColInCCol, m_nRowInQRow; + float m_occupancy; + + //output + HitMap m_hitMap; + FrontEndEvent m_truthEvt; + uint m_nGenerated; +}; + +#endif \ No newline at end of file diff --git a/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/ItkpixEncoder.cpp b/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/ItkpixEncoder.cpp new file mode 100644 index 000000000000..ff755e0b821a --- /dev/null +++ b/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/ItkpixEncoder.cpp @@ -0,0 +1,196 @@ +/* +* Author: Ondra Kovanda, ondrej.kovanda at cern.ch +* Date: 03/2024 +* Description: ITkPix* encoding base class +*/ + +#include "ItkpixEncoder.h" +#include "ItkpixQCoreEncodingLUT.h" + +//Constructor sets up the geometry for all future loops + +ItkpixEncoder::ItkpixEncoder(const uint nCol, const uint nRow, const uint nColInCCol, const uint nRowInQRow, const uint nEventsPerStream, const bool plainHitMap, const bool dropToT): m_nCol(nCol), m_nRow(nRow), m_nColInCCol(nColInCCol), m_nRowInQRow(nRowInQRow), m_nEventsPerStream(nEventsPerStream), m_plainHitMap(plainHitMap), m_dropToT(dropToT){ + m_nCCol = nCol/m_nColInCCol; + m_nQRow = nRow/m_nRowInQRow; + m_currBlock = 0x0ULL; + m_currBit = 0; + m_currEvent = 0; + m_currStream = 0; +} + +void ItkpixEncoder::addBits64(const uint64_t value, const uint8_t length){ + //This adds 'length' lowest bits to the current block. If the current + //block gets filled, push it to the output and start a new block with + //the rest of the bits that didn't make it. Need to keep track of the + //remaining space in the current word. Also, we only have 63 bits for + //the added data, as the first bit is EoS. + //Case 1: there's enough space for the entire information to be added + if (length <= (63 - m_currBit)){ + + //The position at which the new bits should be inserted into the block + //is (length of the block - 1) - (currently last bit) - (length) + //We need to keep the first bit for EoS, hence the -1 + m_currBlock |= (value << (63 - m_currBit - length)); + m_currBit += length; + return; + } + + //Case 2: there's not enough space, so we put what we can, push the block + //to the output, and the rest to a new block + else { + + //How much space do we have? + uint8_t remainingBits = 63 - m_currBit; + + //Add that many bits + m_currBlock |= ((value >> (length - remainingBits))); + + //Push the current block to the output and reset it and the current + //bit counter. The block needs to be split in 32-bit halves before the output + pushWords32(); + + //What are we left with? + uint8_t leftoverBits = length - remainingBits; + + //Now we can add the remainder + //Make sure that the remainder we're adding is really only the bits we've not yet + //added, otherwise there can be a rogue "1" in the EoS bit, which was already added + //in the previous block + addBits64((value & (0xFFFFFFFFFFFFFFFF >> (64 - leftoverBits))), leftoverBits); + + } +} + +void ItkpixEncoder::pushWords32(){ + //whenever the current block is ready for output, + //split it into two 32-bit words and push them to + //the output container. Reset the current bloc/bit + uint32_t word1 = m_currBlock >> 32; + uint32_t word2 = m_currBlock & 0xFFFFFFFF; + m_words.push_back(word1); + m_words.push_back(word2); + m_currBlock = 0x0ULL; + m_currBit = 0; +} + +void ItkpixEncoder::encodeQCore(const uint nCCol, const uint nQRow){ + //produce hit map and ToTs + //First, get the top-left pixel in the QCore + uint m_col = nCCol * m_nColInCCol; + uint m_row = nQRow * m_nRowInQRow; + + //now loop, store ToTs, and build index of the + //compressed hit map in the LUT + uint16_t lutIndex = 0x0000; + std::vector<uint16_t> tots; + tots.reserve(16); + int pix = 0; + for (uint pixRow = m_row; pixRow < m_row + m_nRowInQRow; pixRow++){ + for (uint pixCol = m_col; pixCol < m_col + m_nColInCCol; pixCol++){ + if (m_hitMap(pixCol, pixRow)){ + lutIndex |= 0x1 << pix; + tots.push_back(m_hitMap(pixCol, pixRow) - 1); + } + pix++; + } + } + + //now add the binary-tree encoded & compressed map + //from the LUT to the stream. If, instead, the plain + //hit map is requested, add the index (which is the + //plain hit map in fact) + + m_plainHitMap ? addBits64(lutIndex, 16) : addBits64(ItkpixEncoding::Itkpixv2QCoreEncodingLUT_Tree[lutIndex], ItkpixEncoding::Itkpixv2QCoreEncodingLUT_Length[lutIndex]); + + //if dropToT is requested, we can return here + if (m_dropToT) return; + + //and add the four-bit ToT information for each hit + for (auto& tot : tots){ + addBits64(tot, 4); + } +} + +bool ItkpixEncoder::hitInQCore(const uint CCol, const uint QRow){ + //Was there a hit in this QCore? + + uint m_col = CCol * m_nColInCCol; + uint m_row = QRow * m_nRowInQRow; + + for (uint pixRow = m_row; pixRow < m_row + m_nRowInQRow; pixRow++){ + for (uint pixCol = m_col; pixCol < m_col + m_nColInCCol; pixCol++){ + if (m_hitMap(pixCol, pixRow)) return true; + } + } + + return false; +} + +void ItkpixEncoder::scanHitMap(){ + //Fill in a helper map of hit QCores and a vector of last qrow in each ccol + m_hitQCores = std::vector<std::vector<bool>>(m_nCCol, std::vector<bool>(m_nQRow, false)); + m_lastQRow = std::vector<uint>(m_nCCol, 0); + + for (uint CCol = 0; CCol < m_nCCol; CCol++){ + for (uint QRow = 0; QRow < m_nQRow; QRow++){ + //if there's a hit in the qcore, flag the helper map + m_hitQCores[CCol][QRow] = hitInQCore(CCol, QRow); + + //and keep track of the last qrow in each CCol, so that we can + //easily set the isLast bit. Numbering starts at 1, in order to + //keep the m_lastQRow[CCol] == 0 case denoting "no hit" in the CCol + //to save some looping/ifs later on + if (m_hitQCores[CCol][QRow]) m_lastQRow[CCol] = QRow + 1; + } + } + +} + +void ItkpixEncoder::encodeEvent(){ + //This produces the bits for one event. + //First, scan the map and produce helpers + scanHitMap(); + + for (uint CCol = 0; CCol < m_nCCol; CCol++){ + //if there are no hits in this CCol, continue + if (m_lastQRow[CCol] == 0) continue; + //add the 6-bit (CCol + 1) address + addBits64(CCol + 1, 6); + + int previousQRow = -666; + for (uint QRow = 0; QRow < m_nQRow; QRow++){ + //if there's no hit in this row, continue + if (!m_hitQCores[CCol][QRow]) continue; + + //add the isLast bit + QRow + 1 == m_lastQRow[CCol] ? addBits64(0x1, 1) : addBits64(0x0, 1); + + //add the isNeighbor bit. If false, add the QRow address as well. + if (QRow == previousQRow + 1){ + addBits64(0x1, 1); + } + else { + addBits64(0x0, 1); + addBits64(QRow, 8); + }; + + //add the map and ToT + encodeQCore(CCol, QRow); + + //update the previous QRow + previousQRow = QRow; + } + } +} + +void ItkpixEncoder::streamTag(const uint8_t nStream){ + //this adds the 8-bit 'global' stream tag + addBits64(nStream, 8); +} + +void ItkpixEncoder::intTag(const uint16_t nEvt){ + //this adds 11 bits of interal tagging between events. + //does the tag always need to start with 111? + uint16_t tag = nEvt | (0b111 << 8); + addBits64(tag, 11); +} \ No newline at end of file diff --git a/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/ItkpixEncoder.h b/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/ItkpixEncoder.h new file mode 100644 index 000000000000..7a856e286078 --- /dev/null +++ b/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/ItkpixEncoder.h @@ -0,0 +1,68 @@ +/* +* Author: Ondra Kovanda, ondrej.kovanda at cern.ch +* Date: 03/2024 +* Description: ITkPix* encoding base class +*/ + +#ifndef ITKPIXENCODER_H +#define ITKPIXENCODER_H + +#include <vector> +#include <iostream> +#include <cstdint> +#include "ItkpixLayout.h" + +class ItkpixEncoder{ + public: + typedef ItkpixLayout<uint16_t> HitMap; + + ItkpixEncoder(const uint nCol = 400, const uint nRow = 384, const uint nColInCCol = 8, const uint nRowInQRow = 2, const uint nEventsPerStream = 16, const bool plainHitMap = false, const bool dropToT = false); + + std::vector<uint32_t>& getWords(){return m_words;} + + void addBits64(const uint64_t value, const uint8_t length); + + void pushWords32(); + + void encodeQCore(const uint nCCol, const uint nQRow); + + void encodeEvent(); + + void streamTag(const uint8_t nStream); + + void intTag(const uint16_t nEvt); + + void scanHitMap(); + + bool hitInQCore(const uint CCol, const uint QRow); + + void setHitMap(const HitMap& hitMap){m_hitMap = hitMap;} + + void setEventsPerStream(const uint nEventsPerStream = 16){m_nEventsPerStream = nEventsPerStream;} + + protected: + // Output + std::vector<uint32_t> m_words; + uint m_nEventsPerStream, m_currCCol, m_currQRow, m_currEvent;//, m_lastQRow; + uint8_t m_currStream; + + // Encoding machinery + uint64_t m_currBlock; + uint8_t m_currBit; + std::vector<std::vector<bool>> m_hitQCores; + std::vector<uint> m_lastQRow; + + // Chip geometry + uint m_nCol, m_nRow, m_nCCol, m_nQRow, m_nColInCCol, m_nRowInQRow; + + //Globals - could be replace with compile-time conditioning instead of run-time if performance is critical + bool m_plainHitMap, m_dropToT; + + // Input + HitMap m_hitMap; + + +}; + + +#endif \ No newline at end of file diff --git a/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/ItkpixLayout.h b/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/ItkpixLayout.h new file mode 100644 index 000000000000..147cdd2b3299 --- /dev/null +++ b/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/ItkpixLayout.h @@ -0,0 +1,42 @@ +/* +* Author: Ondra Kovanda, ondrej.kovanda at cern.ch +* Date: 04/2024 +* Description: ITkPix* chip layout template. This aims for a contiguously stored +* array of arbitrary type, representing the individual pixels in +* ITkPix* chips with a 'physically' motivated (col, row) access +*/ + +#ifndef ITKPIXLAYOUT_H +#define ITKPIXLAYOUT_H + +#include <array> +#include <cstdint> + +template<class T> class ItkpixLayout{ + + public: + + ItkpixLayout(){}; + + T& operator()(const uint16_t col, const uint16_t row){ + + //Columns are stored after one another + return pixels[ col * 384 + row ]; + + } + + T operator()(const uint16_t col, const uint16_t row) const { + + //Columns are stored after one another + return pixels[ col * 384 + row ]; + + } + + private: + + //All chips will allways have 400*384 pixels + std::array<T, 153600> pixels = {}; + +}; + +#endif \ No newline at end of file diff --git a/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/Itkpixv2Encoder.cpp b/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/Itkpixv2Encoder.cpp new file mode 100644 index 000000000000..21219c4efd24 --- /dev/null +++ b/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/Itkpixv2Encoder.cpp @@ -0,0 +1,43 @@ +/* +* Author: Ondra Kovanda, ondrej.kovanda at cern.ch +* Date: 02/2024 +* Description: ITkPix* encoding +*/ + +#include "Itkpixv2Encoder.h" + +void Itkpixv2Encoder::endStream(){ + m_currBlock |= (0x1ULL << 63); + pushWords32(); +} + +void Itkpixv2Encoder::addToStream(const HitMap& hitMap, bool last){ + //This is a high-level interface function that can take care of + //adding an event into the current stream, this can be called + //easily from the outside, and automatically tags/ends streams + //and events based on internal vars only + + //If this is the first event, start a new stream. Otherwise, add an + //internal tag + if (m_currEvent == 0){ + streamTag(m_currStream); + m_currStream++; + } + else { + intTag(m_currEvent); + } + + //Then add the actual encoded event information + setHitMap(hitMap); + encodeEvent(); + m_currEvent++; + + //If this is the last event in the stream or if we explicitly + //want to end the stream (i. e. total number of generated events + //is not a multiple of nEventsPerStream), end the stream + if (m_currEvent == m_nEventsPerStream || last){ + endStream(); + m_currEvent = 0; + } + +} \ No newline at end of file diff --git a/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/Itkpixv2Encoder.h b/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/Itkpixv2Encoder.h new file mode 100644 index 000000000000..fafb41bf524a --- /dev/null +++ b/InnerDetector/InDetEventCnv/ITkPixelByteStreamCnv/src/Itkpixv2Encoder.h @@ -0,0 +1,24 @@ +/* +* Author: Ondra Kovanda, ondrej.kovanda at cern.ch +* Date: 02/2024 +* Description: ITkPixv2 encoding +*/ + +#ifndef ITKPIXV2ENCODER_H +#define ITKPIXV2ENCODER_H + +#include <vector> +#include <iostream> +#include <random> +#include "ItkpixEncoder.h" + +class Itkpixv2Encoder : public ItkpixEncoder { + + public: + + void endStream(); + + void addToStream(const HitMap& hitMap, bool last = false); +}; + +#endif -- GitLab