Skip to content

Commit

Permalink
Merge pull request #25 from DUNE-DAQ/patch/fddaq-v4.4.x
Browse files Browse the repository at this point in the history
Patch/fddaq v4.4.x
  • Loading branch information
jcfreeman2 authored Jun 10, 2024
2 parents 2abdb97 + ae85404 commit fee6a1c
Show file tree
Hide file tree
Showing 6 changed files with 311 additions and 1 deletion.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.12)
project(fddetdataformats VERSION 1.2.0)
project(fddetdataformats VERSION 1.2.1)

find_package(daq-cmake REQUIRED)

Expand Down
166 changes: 166 additions & 0 deletions include/fddetdataformats/TDEEthFrame.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/**
* @file TDEEthFrame.hpp
*
* Contains declaration of TDEEthFrame, a class for accessing raw WIB v2 frames, as used in ProtoDUNE-SP-II
*
* The canonical definition of the WIB format is given in EDMS document 2088713: * https://edms.cern.ch/document/2088713/XXX
*
* This is part of the DUNE DAQ Application Framework, copyright 2020.
* Licensing/copyright details are in the COPYING file that you should have
* received with this code.
*/

#ifndef FDDETDATAFORMATS_INCLUDE_FDDETDATAFORMATS_TDEETHFRAME_HPP_
#define FDDETDATAFORMATS_INCLUDE_FDDETDATAFORMATS_TDEETHFRAME_HPP_

#include "detdataformats/DAQEthHeader.hpp"

#include <algorithm> // For std::min
#include <cassert> // For assert()
#include <cstdint> // For uint32_t etc
#include <cstdio>
#include <cstdlib>
#include <stdexcept> // For std::out_of_range

namespace dunedaq::fddetdataformats {

/**
* @brief Class for accessing raw WIB eth frames, as used in ProtoDUNE-II
*
* The canonical definition of the WIB format is given in EDMS document 2088713:
* https://edms.cern.ch/document/2088713/XXX
*/
class TDEEthFrame
{
public:
// ===============================================================
// Preliminaries
// ===============================================================

// The definition of the format is in terms of 64-bit words
typedef uint64_t word_t; // NOLINT

static constexpr int s_bits_per_adc = 14;
static constexpr int s_bits_per_word = 8 * sizeof(word_t);
static constexpr int s_time_samples_per_frame = 64;
static constexpr int s_channels_per_half_femb = 64;
static constexpr int s_half_fembs_per_frame = 1;
static constexpr int s_num_channels = s_channels_per_half_femb * s_half_fembs_per_frame;
static constexpr int s_num_adc_words_per_ts = s_num_channels * s_bits_per_adc / s_bits_per_word;
static constexpr int s_num_adc_words = s_time_samples_per_frame * s_num_channels * s_bits_per_adc / s_bits_per_word;


struct TDEEthHeader
{
uint64_t reserved : 26;
uint64_t tde_errors : 16;
uint64_t tde_header : 10;
uint64_t version : 4;
uint64_t channel : 8;
uint64_t TAItime : 64;
};

// ===============================================================
// Data members
// ===============================================================
detdataformats::DAQEthHeader daq_header;
TDEEthHeader header;
// word_t adc_words[s_num_adc_words_per_ts][s_time_samples_per_frame]; // NOLINT
word_t adc_words[s_time_samples_per_frame][s_num_adc_words_per_ts]; // NOLINT

// ===============================================================
// Accessors
// ===============================================================

/**
* @brief Get the ith ADC value in the frame
*
* The ADC words are 14 bits long;
* wrod_t stored packed in the data structure.
* The order is: 64 channels repeated for 64 time samples
*
*/
uint16_t get_adc(int i, int sample=0) const // NOLINT(build/unsigned)
{
if (i < 0 || i >= s_num_channels)
throw std::out_of_range("ADC index out of range");

// The index of the first (and sometimes only) word containing the required ADC value
int word_index = s_bits_per_adc * i / s_bits_per_word;
assert(word_index < s_num_adc_words_per_ts);
// Where in the word the lowest bit of our ADC value is located
int first_bit_position = (s_bits_per_adc * i) % s_bits_per_word;
// How many bits of our desired ADC are located in the `word_index`th word
int bits_from_first_word = std::min(s_bits_per_adc, s_bits_per_word - first_bit_position);
// uint16_t adc = adc_words[word_index][sample] >> first_bit_position; // NOLINT(build/unsigned)
uint16_t adc = adc_words[sample][word_index] >> first_bit_position; // NOLINT(build/unsigned)
// If we didn't get the full 14 bits from this word, we need the rest from the next word
if (bits_from_first_word < s_bits_per_adc) {
assert(word_index + 1 < s_num_adc_words_per_ts);
// adc |= adc_words[word_index + 1][sample] << bits_from_first_word;
adc |= adc_words[sample][word_index + 1] << bits_from_first_word;
}
// Mask out all but the lowest 14 bits;
return adc & 0x3FFFu;
}

/**
* @brief Set the ith ADC value in the frame to @p val
*/
void set_adc(int i, int sample, uint16_t val) // NOLINT(build/unsigned)
{
if (i < 0 || i >= s_num_channels)
throw std::out_of_range("ADC index out of range");
if (val >= (1 << s_bits_per_adc))
throw std::out_of_range("ADC value out of range");

// The index of the first (and sometimes only) word containing the required ADC value
int word_index = s_bits_per_adc * i / s_bits_per_word;
assert(word_index < s_num_adc_words);
// Where in the word the lowest bit of our ADC value is located
int first_bit_position = (s_bits_per_adc * i) % s_bits_per_word;
// How many bits of our desired ADC are located in the `word_index`th word
int bits_in_first_word = std::min(s_bits_per_adc, s_bits_per_word - first_bit_position);
uint64_t mask = (static_cast<uint64_t>(1) << first_bit_position) - 1;
adc_words[sample][word_index] = ((static_cast<uint64_t>(val) << first_bit_position) & ~mask) | (adc_words[sample][word_index] & mask);
// If we didn't put the full 14 bits in this word, we need to put the rest in the next word
if (bits_in_first_word < s_bits_per_adc) {
assert(word_index + 1 < s_num_adc_words);
mask = (1 << (s_bits_per_adc - bits_in_first_word)) - 1;
adc_words[sample][word_index + 1] = ((val >> bits_in_first_word) & mask) | (adc_words[sample][word_index + 1] & ~mask);
}
}

/** @brief Get the starting 64-bit timestamp of the frame
*/
uint64_t get_timestamp() const // NOLINT(build/unsigned)
{
return daq_header.get_timestamp() ; // NOLINT(build/unsigned)
}

/** @brief Set the starting 64-bit timestamp of the frame
*/
void set_timestamp(const uint64_t new_timestamp) // NOLINT(build/unsigned)
{
daq_header.timestamp = new_timestamp;
}

/** @brief Get the channel identifier of the frame
*/
uint8_t get_channel() const // NOLINT(build/unsigned)
{
return header.channel ; // NOLINT(build/unsigned)
}

/** @brief Set the channel identifier of the frame
*/
void set_channel(const uint8_t new_channel) // NOLINT(build/unsigned)
{
header.channel = new_channel;
}

};

} // namespace dunedaq::fddetdataformats

#endif // FDDETDATAFORMATS_INCLUDE_FDDETDATAFORMATS_TDEETHFRAME_HPP_
1 change: 1 addition & 0 deletions pybindsrc/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ PYBIND11_MODULE(_daq_fddetdataformats_py, m)
register_daphne(m);
register_ssp(m);
register_tde(m);
register_tdeeth(m);
}

} // namespace dunedaq::fddetdataformats::python
1 change: 1 addition & 0 deletions pybindsrc/registrators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ namespace dunedaq::fddetdataformats::python {
void register_daphne(pybind11::module&);
void register_ssp(pybind11::module&);
void register_tde(pybind11::module&);
void register_tdeeth(pybind11::module&);
}

#endif // FDDETDATAFORMATS_PYBINDSRC_REGISTRATORS_HPP_
1 change: 1 addition & 0 deletions pybindsrc/tde.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ register_tde(py::module& m)
.def("get_channel", &TDE16Frame::get_channel)
.def("get_daq_header", [](TDE16Frame& self) -> detdataformats::DAQEthHeader* {return self.get_daq_header();}, py::return_value_policy::reference_internal)
.def("get_tde_header", [](TDE16Frame& self) -> TDEHeader* {return self.get_tde_header();}, py::return_value_policy::reference_internal)
.def("get_adc_sample", &TDE16Frame::get_adc_sample)
.def_static("sizeof", [](){ return sizeof(TDE16Frame); })
.def("get_bytes",
[](TDE16Frame* fr) -> py::bytes {
Expand Down
141 changes: 141 additions & 0 deletions pybindsrc/tdeeth.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/**
* @file wibeth.cpp Python bindings for the TDEEthFrame format
*
* This is part of the DUNE DAQ Software Suite, copyright 2020.
* Licensing/copyright details are in the COPYING file that you should have
* received with this code.
*/

#include "fddetdataformats/TDEEthFrame.hpp"

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

namespace py = pybind11;

namespace dunedaq::fddetdataformats::python {

void
register_tdeeth(py::module& m)
{


// py::class_<TDEEthFrame::TDEHeader>(m, "TDEHeader")
// .def_property("channel",
// [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.channel;},
// [](TDEEthFrame::TDEHeader& self, uint32_t channel) {self.channel = channel;}
// )
// .def_property("version",
// [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.version;},
// [](TDEEthFrame::TDEHeader& self, uint32_t version) {self.version = version;}
// )
// // .def_property("reserved",
// // [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.reserved;},
// // [](TDEEthFrame::TDEHeader& self, uint32_t reserved) {self.reserved = reserved;}
// // )
// .def_property("cd",
// [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.cd;},
// [](TDEEthFrame::TDEHeader& self, uint32_t cd) {self.version = cd;}
// )
// .def_property("context",
// [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.context;},
// [](TDEEthFrame::TDEHeader& self, uint32_t context) {self.version = context;}
// )
// .def_property("ready",
// [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.ready;},
// [](TDEEthFrame::TDEHeader& self, uint32_t ready) {self.ready = ready;}
// )
// .def_property("calibration",
// [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.calibration;},
// [](TDEEthFrame::TDEHeader& self, uint32_t calibration) {self.calibration = calibration;}
// )
// .def_property("pulser",
// [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.pulser;},
// [](TDEEthFrame::TDEHeader& self, uint32_t pulser) {self.pulser = pulser;}
// )
// .def_property("femb_sync",
// [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.femb_sync;},
// [](TDEEthFrame::TDEHeader& self, uint32_t femb_sync) {self.femb_sync = femb_sync;}
// )
// .def_property("wib_sync",
// [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.wib_sync;},
// [](TDEEthFrame::TDEHeader& self, uint32_t wib_sync) {self.wib_sync = wib_sync;}
// )
// .def_property("lol",
// [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.lol;},
// [](TDEEthFrame::TDEHeader& self, uint32_t lol) {self.lol = lol;}
// )
// .def_property("link_valid",
// [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.link_valid;},
// [](TDEEthFrame::TDEHeader& self, uint32_t link_valid) {self.link_valid = link_valid;}
// )
// .def_property("crc_err",
// [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.crc_err;},
// [](TDEEthFrame::TDEHeader& self, uint32_t crc_err) {self.crc_err = crc_err;}
// )
// .def_property("colddata_timestamp_1",
// [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.colddata_timestamp_1;},
// [](TDEEthFrame::TDEHeader& self, uint32_t colddata_timestamp_1) {self.lol = colddata_timestamp_1;}
// )
// .def_property("colddata_timestamp_0",
// [](TDEEthFrame::TDEHeader& self) -> uint32_t {return self.colddata_timestamp_0;},
// [](TDEEthFrame::TDEHeader& self, uint32_t colddata_timestamp_0) {self.colddata_timestamp_0 = colddata_timestamp_0;}
// )
// .def_property("extra_data",
// [](TDEEthFrame::TDEHeader& self) -> uint64_t {return self.extra_data;},
// [](TDEEthFrame::TDEHeader& self, uint64_t extra_data) {self.extra_data = extra_data;}
// )
// ;

py::class_<TDEEthFrame::TDEEthHeader>(m, "TDEEthHeader")
.def_property("channel",
[](TDEEthFrame::TDEEthHeader& self) -> uint16_t {return self.channel;},
[](TDEEthFrame::TDEEthHeader& self, uint16_t channel) {self.channel = channel;}
)
.def_property("version",
[](TDEEthFrame::TDEEthHeader& self) -> uint16_t {return self.version;},
[](TDEEthFrame::TDEEthHeader& self, uint16_t version) {self.version = version;}
)
.def_property("tde_header",
[](TDEEthFrame::TDEEthHeader& self) -> uint16_t {return self.tde_header;},
[](TDEEthFrame::TDEEthHeader& self, uint16_t tde_header) {self.tde_header = tde_header;}
)
.def_property("tde_errors",
[](TDEEthFrame::TDEEthHeader& self) -> uint16_t {return self.tde_errors;},
[](TDEEthFrame::TDEEthHeader& self, uint16_t tde_errors) {self.tde_errors = tde_errors;}
)
.def_property("TAItime",
[](TDEEthFrame::TDEEthHeader& self) -> uint64_t {return self.TAItime;},
[](TDEEthFrame::TDEEthHeader& self, uint64_t TAItime) {self.TAItime = TAItime;}
)
;


py::class_<TDEEthFrame>(m, "TDEEthFrame", py::buffer_protocol())
.def(py::init())
.def(py::init([](py::capsule capsule) {
auto tfp = *static_cast<TDEEthFrame*>(capsule.get_pointer());
return tfp;
} ))
.def(py::init([](py::bytes bytes){
py::buffer_info info(py::buffer(bytes).request());
auto tfp = *static_cast<TDEEthFrame*>(info.ptr);
return tfp;
}))
.def("get_daqheader", [](TDEEthFrame& self) -> const detdataformats::DAQEthHeader& {return self.daq_header;}, py::return_value_policy::reference_internal)
.def("get_tdeheader", [](TDEEthFrame& self) -> const TDEEthFrame::TDEEthHeader& {return self.header;}, py::return_value_policy::reference_internal)
.def("get_adc", &TDEEthFrame::get_adc)
.def("set_adc", &TDEEthFrame::set_adc)
.def("get_timestamp", &TDEEthFrame::get_timestamp)
.def("set_timestamp", &TDEEthFrame::set_timestamp)
.def("get_channel", &TDEEthFrame::get_channel)
.def("set_channel", &TDEEthFrame::set_channel)
.def_static("sizeof", [](){ return sizeof(TDEEthFrame); })
.def("get_bytes",
[](TDEEthFrame* fr) -> py::bytes {
return py::bytes(reinterpret_cast<char*>(fr), sizeof(TDEEthFrame));
})
;
}

} // namespace dunedaq::fddetdataformats::python

0 comments on commit fee6a1c

Please sign in to comment.