Skip to content
Snippets Groups Projects
Commit 876ed7df authored by Ondrej Kovanda's avatar Ondrej Kovanda
Browse files

Adding the encoder and generator files

parent 8f5c5159
No related branches found
No related tags found
23 merge requests!78241Draft: FPGATrackSim: GenScan code refactor,!78236Draft: Switching Streams https://its.cern.ch/jira/browse/ATR-27417,!78056AFP monitoring: new synchronization and cleaning,!78041AFP monitoring: new synchronization and cleaning,!77990Updating TRT chip masks for L1TRT trigger simulation - ATR-28372,!77733Draft: add new HLT NN JVT, augmented with additional tracking information,!77731Draft: Updates to ZDC reconstruction,!77728Draft: updates to ZDC reconstruction,!77522Draft: sTGC Pad Trigger Emulator,!76725ZdcNtuple: Fix cppcheck warning.,!76611L1CaloFEXByteStream: Fix out-of-bounds array accesses.,!76475Punchthrough AF3 implementation in FastG4,!76474Punchthrough AF3 implementation in FastG4,!75729New implementation of ZDC nonlinear FADC correction.,!75703Draft: Update to HI han config for HLT jets,!75184Draft: Update file heavyions_run.config,!74430Draft: Fixing upper bound for Delayed Jet Triggers,!73963Changing the path of the histograms to "Expert" area,!73875updating ID ART reference plots,!73874AtlasCLHEP_RandomGenerators: Fix cppcheck warnings.,!73449Add muon detectors to DarkJetPEBTLA partial event building,!73343Draft: [TrigEgamma] Add photon ringer chains on bootstrap mechanism,!71771Itk pixel bytestream encoding
/*
* 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
/*
* 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
/*
* 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
/*
* 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
/*
* 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
/*
* 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
/*
* 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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment