LDMX Software
EcalRawDecoder.cxx
1#include "Ecal/EcalRawDecoder.h"
2
3#include <bitset>
4#include <iomanip>
5#include <optional>
6
8#include "DetDescr/EcalID.h"
10#include "Packing/Utility/BufferReader.h"
11#include "Packing/Utility/CRC.h"
12#include "Packing/Utility/Hex.h"
13#include "Packing/Utility/Mask.h"
15
16namespace ecal {
17
18// we are using 32-bit words here
21
23 input_name_ = ps.getParameter<std::string>("input_name");
24 input_pass_ = ps.getParameter<std::string>("input_pass");
25 output_name_ = ps.getParameter<std::string>("output_name");
26 roc_version_ = ps.getParameter<int>("roc_version");
27 translate_eid_ = ps.getParameter<bool>("translate_eid");
28}
29
34 static const unsigned int common_mode_channel = roc_version_ == 2 ? 19 : 1;
36 static uint32_t head1, head2, w;
37
38 BufferReader reader{event.getCollection<uint8_t>(input_name_, input_pass_)};
39
46 // fill map of **electronic** IDs to the digis that were read out
48 std::vector<ldmx::HgcrocDigiCollection::Sample>>
49 eid_to_samples;
50 while (reader >> head1 >> head2) {
52 if (head1 == 0x11111111 and head2 == 0xbeef2021) {
53 /* whole event header word looks like
54 *
55 * VERSION (4) | FPGA ID (8) | NSAMPLES (4) | LEN (16)
56 */
57 uint32_t whole_event_header;
58 reader >> whole_event_header;
59 /* event length not currently used but is stored in header as defined by
60 the spec uint32_t version = (whole_event_header >> 28) &
61 packing::utility::mask<4>; uint32_t fpga = (whole_event_header >> 20) &
62 packing::utility::mask<8>; uint32_t eventlen = whole_event_header &
63 packing::utility::mask<16>;
64 */
65 uint32_t nsamples =
66 (whole_event_header >> 16) & packing::utility::mask<4>;
67 // sample counters
68 std::vector<uint32_t> length_per_sample(nsamples, 0);
69 for (uint32_t i_sample{0}; i_sample < nsamples; i_sample++) {
70 if (i_sample % 2 == 0) {
71 reader >> w;
72 }
73 uint32_t shift_in_word = 16 * (i_sample % 2);
74 length_per_sample[i_sample] =
75 (w >> shift_in_word) & packing::utility::mask<12>;
76 }
77
78 // read first sample headers
79 reader >> head1 >> head2;
80 } else if (head1 == 0xd07e2021 and head2 == 0x12345678) {
81 // these are the special footer words at the end,
82 // done with event
83 break;
84 }
85
101 packing::utility::CRC fpga_crc;
102 fpga_crc << head1;
103 std::cout << hex(head1) << " : ";
104 uint32_t version = (head1 >> 28) & packing::utility::mask<4>;
105 // std::cout << "version " << version << std::flush;
106 uint32_t one{1};
107 if (version != one)
108 EXCEPTION_RAISE("VersMis",
109 "EcalRawDecoder only knows version 1 of DAQ format.");
110
111 uint32_t fpga = (head1 >> 20) & packing::utility::mask<8>;
112 uint32_t nlinks = (head1 >> 14) & packing::utility::mask<6>;
113 // total length not used but is available in header as defined by spec
114 // uint32_t len = head1 & packing::utility::mask<12>;
115
116 // std::cout << ", fpga: " << fpga << ", nlinks: " << nlinks << ", len: " <<
117 // len << std::endl;
118 fpga_crc << head2;
119 // std::cout << hex(head2) << " : ";
120
121 // bunch ID (bx), read request (rreq) and orbit counter (orbit) are defined
122 // in header but not used right now uint32_t bx_id = (head2 >> 20) &
123 // packing::utility::mask<12>; uint32_t rreq = (head2 >> 10) &
124 // packing::utility::mask<10>; uint32_t orbit = head2 &
125 // packing::utility::mask<10>;
126
127 // std::cout << "bx_id: " << bx_id << ", rreq: " << rreq << ", orbit: " <<
128 // orbit << std::endl;
129
130 std::vector<uint32_t> length_per_link(nlinks, 0);
131 for (uint32_t i_link{0}; i_link < nlinks; i_link++) {
132 if (i_link % 4 == 0) {
133 reader >> w;
134 fpga_crc << w;
135 // std::cout << hex(w) << " : Four Link Pack " << std::endl;
136 }
137 uint32_t shift_in_word = 8 * (i_link % 4);
138 // the check sums performed by the HGCROC and downstream chips can
139 // confirm the validity of the data, not used here (yet)
140 // bool rid_ok = ((w >> (shift_in_word + 7)) & packing::utility::mask<1>)
141 // == 1; bool cdc_ok = ((w >> (shift_in_word + 6)) &
142 // packing::utility::mask<1>) == 1;
143 length_per_link[i_link] =
144 (w >> shift_in_word) & packing::utility::mask<6>;
145 // std::cout << " Link " << i_link << " readout " <<
146 // length_per_link.at(i_link) << " channels" << std::endl;
147 }
148
158 for (uint32_t i_link{0}; i_link < nlinks; i_link++) {
159 // move on from last word counting links or previous link
160 // std::cout << "RO Link " << i_link << std::endl;
161 packing::utility::CRC link_crc;
162 reader >> w;
163 fpga_crc << w;
164 link_crc << w;
165 uint32_t roc_id = (w >> 16) & packing::utility::mask<16>;
166 // checksum for this chunk of data can be used to confirm data validity
167 // bool crc_ok = (w >> 15) & packing::utility::mask<1> == 1;
168 // std::cout << hex(w) << " : roc_id " << roc_id << ", crc_ok (v2
169 // always false) " << std::boolalpha << crc_ok << std::endl;
170
171 // get readout map from the last 8 bits of this word
172 // and the entire next word
173 std::bitset<40> ro_map = w & packing::utility::mask<8>;
174 ro_map <<= 32;
175 reader >> w;
176 fpga_crc << w;
177 link_crc << w;
178 ro_map |= w;
179
180 // std::cout << "Start looping through channels..." << std::endl;
181 // loop through channels on this link,
182 // since some channels may have been suppressed because of low
183 // amplitude the channel ID is not the same as the index it
184 // is listed in.
185 int channel_id{-1};
186 for (uint32_t j{0}; j < length_per_link.at(i_link) - 2; j++) {
187 // skip zero-suppressed channel IDs
188 do {
189 channel_id++;
190 } while (channel_id < 40 and not ro_map.test(channel_id));
191
192 // next word is this channel
193 reader >> w;
194 fpga_crc << w;
195 // std::cout << hex(w) << " " << channel_id;
196
197 if (channel_id == 0) {
206 // std::cout << " : ROC Header";
207 /*
208 * These are unused, but are available as defined by the spec
209 * additionally, we are calculating our own copy of the link checksum
210 for later comparison
211 * if the data packet has readout its own checksum
212 link_crc << w;
213 uint32_t bx_id = (w >> 16) & packing::utility::mask<12>;
214 uint32_t short_event = (w >> 10) & packing::utility::mask<6>;
215 uint32_t short_orbit = (w >> 7) & packing::utility::mask<3>;
216 uint32_t hamming_errs = (w >> 4) & packing::utility::mask<3>;
217 */
218 } else if (channel_id == common_mode_channel) {
222 link_crc << w;
223 // std::cout << " : Common Mode";
224 } else if (channel_id == 39) {
225 /*
226 // CRC checksum from ROC
227 uint32_t crc = w;
228 // std::cout << " : CRC checksum : " << hex(link_crc.get()) << " =? "
229 << hex(crc);
230 // keeping this there in comment is helpful when the HGCROC is sending
231 along its own CRC checksum of the data for us to compare against.
232 // We had to comment it out because the v2 HGCROC we were using was
233 not sending a checksum and thus the check would always fail. if
234 (link_crc.get() != crc) { EXCEPTION_RAISE("BadCRC", "Our calculated
235 link checksum doesn't match the " "one from raw data.");
236 }
237 */
238 } else {
240
241 link_crc << w;
252 // std::cout << " : DAQ Channel ";
253
254 // std::cout << fpga << " " << roc_id-256 << " " << channel_id << " ";
265 ldmx::EcalElectronicsID eid(fpga - 1, roc_id - 256, channel_id);
266 // std::cout << eid.index();
267
268 // copy data into EID->sample map
269 eid_to_samples[eid].emplace_back(w);
270 } // type of channel
271 // std::cout << std::endl;
272 } // loop over channels (j in Table 4)
273 // std::cout << "done looping through channels" << std::endl;
274 } // loop over links
275
276 // another CRC checksum from FPGA
277 reader >> w;
278 // this word would be the CRC checksum as deteremined by the chip
279 // uint32_t crc = w;
280 // std::cout << "FPGA Checksum : " << hex(fpga_crc.get()) << " =? "
281 // << hex(crc) << std::endl;
282 /* TODO
283 * fix calculation of FPGA checksum
284 * I can't figure out why it isn't matching, but there
285 * is definitely a word here where the FPGA checksum would be.
286 if (fpga_crc.get() != crc) {
287 EXCEPTION_RAISE(
288 "BadCRC",
289 "Our calculated FPGA checksum doesn't match the one read in.");
290 }
291 */
292 }
293
295 // assume all channels have same number of samples
296 digis.setNumSamplesPerDigi(eid_to_samples.begin()->second.size());
297 digis.setSampleOfInterestIndex(0); // TODO configurable
299 if (translate_eid_) {
307 auto detmap{
308 getCondition<EcalDetectorMap>(EcalDetectorMap::CONDITIONS_OBJECT_NAME)};
309 for (auto const& [eid, digi] : eid_to_samples) {
310 // The electronics map returns an empty ID of the correct
311 // type when the electronics ID is not found.
312 // need to check if the electronics ID exists
313 // TODO: do we want to end processing if this happens?
314 if (detmap.exists(eid)) {
315 uint32_t did_raw = detmap.get(eid).raw();
316 digis.addDigi(did_raw, digi);
317 } else {
327 }
328 }
329 } else {
337 for (auto const& [eid, digi] : eid_to_samples) {
338 digis.addDigi(eid.raw(), digi);
339 }
340 }
341
342 // std::cout << "adding " << digis.getNumDigis() << " digis each with " <<
343 // digis.getNumSamplesPerDigi() << " samples to event bus" << std::endl;
344 event.add(output_name_, digis);
345 return;
346} // produce
347
348} // namespace ecal
349
350DECLARE_PRODUCER_NS(ecal, EcalRawDecoder);
Class which contains logic for how the detector items connect to and relate with the reconstruction c...
Class that identifies a location in the Ecal readout chain.
Class that defines an ECal detector ID with a cell number.
#define DECLARE_PRODUCER_NS(NS, CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Class that represents a digitized hit in a calorimeter cell readout by an HGCROC.
static constexpr const char * CONDITIONS_OBJECT_NAME
The name of the EID <-> DetID map for the ECal.
std::string input_pass_
input pass of creating encoded data
int roc_version_
version of HGC ROC we are decoding
bool translate_eid_
should we translate electronic IDs to detector IDs
virtual void produce(framework::Event &event)
Process the event and put new data products into it.
std::string input_name_
input object of encoded data
std::string output_name_
output object to put onto event bus
virtual void configure(framework::config::Parameters &)
Callback for the EventProcessor to configure itself from the given set of parameters.
Implements an event buffer system for storing event data.
Definition Event.h:41
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:27
T getParameter(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:89
Identifies a location in the Ecal readout chain.
Represents a collection of the digi hits readout by an HGCROC.
void setNumSamplesPerDigi(unsigned int n)
Set number of samples for each digi.
void setSampleOfInterestIndex(unsigned int n)
Set index of sample of interest.
void setVersion(int v)
Set the version of the ROC we have read.
void addDigi(unsigned int id, const std::vector< Sample > &digi)
Add samples to collection.
This class is a helper class for reading the buffer stored in the raw data format.
The HGC ROC and FPGA use a CRC checksum to double check that the data transfer has been done correctl...
Definition CRC.h:50
A very simple wrapper enabling us to more easily tell the output stream to style the input word in he...
Definition Hex.h:18