Line data Source code
1 : /**
2 : * @file DaphneV2ControllerModule.cpp
3 : *
4 : * Implementations of DaphneV2ControllerModule's functions
5 : *
6 : * This is part of the DUNE DAQ Software Suite, copyright 2020.
7 : * Licensing/copyright details are in the COPYING file that you should have
8 : * received with this code.
9 : */
10 :
11 : #include "DaphneV2ControllerModule.hpp"
12 : #include "appmodel/DaphneConf.hpp"
13 : #include "appmodel/DaphneV2ADC.hpp"
14 : #include "appmodel/DaphneV2AFE.hpp"
15 : #include "appmodel/DaphneV2BoardConf.hpp"
16 : #include "appmodel/DaphneV2Channel.hpp"
17 : #include "appmodel/DaphneV2LNA.hpp"
18 : #include "appmodel/DaphneV2PGA.hpp"
19 :
20 : #include "fddetdataformats/DAPHNEFrame.hpp"
21 :
22 : #include <algorithm>
23 : #include <bitset>
24 : #include <chrono>
25 : #include <cmath>
26 : #include <ctime>
27 : #include <fmt/format.h>
28 : #include <fstream>
29 : #include <iomanip>
30 : #include <logging/Logging.hpp>
31 : #include <memory>
32 : #include <regex>
33 : #include <stdexcept>
34 : #include <string>
35 : #include <thread>
36 : #include <utility>
37 : #include <vector>
38 :
39 : namespace dunedaq::daphnemodules {
40 :
41 0 : DaphneV2ControllerModule::DaphneV2ControllerModule(const std::string& name)
42 0 : : dunedaq::appfwk::DAQModule(name)
43 : {
44 0 : register_command("conf", &DaphneV2ControllerModule::do_conf);
45 0 : register_command("start", &DaphneV2ControllerModule::do_start);
46 0 : register_command("scrap", &DaphneV2ControllerModule::do_scrap);
47 : // register_command("dump_buffers", &DaphneV2ControllerModule::dump_buffers);
48 0 : }
49 :
50 : void
51 0 : DaphneV2ControllerModule::init(std::shared_ptr<appfwk::ConfigurationManager> cfgMgr)
52 : {
53 :
54 0 : auto mdal = cfgMgr->get_dal<conf_t>(get_name());
55 0 : if (!mdal) {
56 0 : throw ConfigurationFailed(ERS_HERE, get_name());
57 : }
58 0 : m_module_config = mdal;
59 0 : }
60 :
61 : void
62 0 : DaphneV2ControllerModule::generate_opmon_data()
63 : {
64 :
65 0 : if (!m_interface)
66 0 : return;
67 :
68 0 : if (m_scrap_called.load())
69 : return;
70 :
71 : // read the channel counters
72 0 : constexpr uint64_t s_dropped_counter_address = 0x40700000; // NOLINT
73 0 : constexpr uint64_t s_start_counter_buffer = 0x40800000; // NOLINT
74 0 : constexpr auto s_packets_counter_address = s_start_counter_buffer + s_max_channels * 8;
75 0 : constexpr auto s_tot_packets_counter_address = s_packets_counter_address + s_max_channels * 8;
76 :
77 : // this lock is not completely necessary because of the internal locks in the interface
78 : // but it's a safety measure to make sure that this does not interfere with complex operations
79 0 : const std::lock_guard<std::mutex> lock(m_mutex);
80 :
81 0 : try {
82 :
83 0 : opmon::StreamInfo stream_info;
84 :
85 : // read total packages sent to felix
86 0 : auto tot_pack_buf = m_interface->read_register(s_tot_packets_counter_address, 1);
87 0 : const auto& total_packets = tot_pack_buf[0];
88 0 : auto old_total_packets = m_last_package_counter.exchange(total_packets);
89 :
90 0 : stream_info.set_total_packets(total_packets);
91 :
92 0 : if (total_packets < old_total_packets) {
93 0 : stream_info.set_new_packets(total_packets);
94 : } else {
95 0 : stream_info.set_new_packets(total_packets - old_total_packets);
96 : }
97 :
98 : // read total packages not sent to felix
99 0 : auto tot_dropped_buf = m_interface->read_register(s_dropped_counter_address, 1);
100 0 : const auto& tot_dropped = tot_dropped_buf[0];
101 0 : auto old_tot_dropped = m_last_unsent_counter.exchange(tot_dropped);
102 :
103 0 : stream_info.set_total_dropped_packets(tot_dropped);
104 0 : if (tot_dropped < old_tot_dropped) {
105 0 : stream_info.set_new_dropped_packets(tot_dropped);
106 : } else {
107 0 : stream_info.set_new_dropped_packets(tot_dropped - old_tot_dropped);
108 : }
109 :
110 0 : publish(std::move(stream_info));
111 0 : } catch (const ers::Issue& e) {
112 0 : ers::warning(MonitoringFailed(ERS_HERE, "data stream", e));
113 0 : }
114 :
115 0 : for (ChannelId c = 0; c < s_max_channels; ++c) {
116 :
117 0 : try {
118 0 : opmon::ChannelInfo c_info;
119 :
120 0 : auto& channel_counters = m_channel_counters[c]; // NOLINT c is an integer
121 :
122 0 : auto trig_buf = m_interface->read_register(s_start_counter_buffer + c * 8, 1);
123 0 : const auto& trig = trig_buf[0];
124 0 : auto old_trig = channel_counters.triggers.exchange(trig);
125 :
126 0 : c_info.set_total_triggers(trig);
127 0 : if (trig < old_trig) {
128 0 : c_info.set_new_triggers(trig);
129 : } else {
130 0 : c_info.set_new_triggers(trig - old_trig);
131 : }
132 :
133 0 : auto pack_buf = m_interface->read_register(s_packets_counter_address + c * 8, 1);
134 0 : const auto& pack = pack_buf[0];
135 0 : auto old_pack = channel_counters.packets.exchange(pack);
136 :
137 0 : c_info.set_total_packets(pack);
138 0 : if (pack < old_pack) {
139 0 : c_info.set_new_packets(pack);
140 : } else {
141 0 : c_info.set_new_packets(pack - old_pack);
142 : }
143 :
144 0 : publish(std::move(c_info), { { "channel", fmt::format("{}", c) } });
145 :
146 0 : } catch (const ers::Issue& e) {
147 0 : ers::warning(MonitoringFailed(ERS_HERE, fmt::format("Channel {}", c), e));
148 0 : }
149 : }
150 :
151 : // gatehring the rest of the information
152 0 : static const std::regex volt_regex(
153 : ".* VBIAS0= ([^ ]+) VBIAS1= ([^ ]+) VBIAS2= ([^ ]+) VBIAS3= ([^ ]+) VBIAS4= ([^ ]+) POWER.-5v.= ([^ ]+) "
154 0 : "POWER..2.5v.= ([^ ]+) POWER..CE.= ([^ ]+) TEMP.Celsius.= ([^ ]+) .*");
155 :
156 0 : try {
157 :
158 0 : opmon::GeneralInfo v_info;
159 :
160 0 : auto cmd_res = m_interface->send_command("RD VM ALL");
161 :
162 0 : std::smatch string_values;
163 :
164 0 : if (!std::regex_match(cmd_res.result, string_values, volt_regex)) {
165 0 : ++m_error_counter;
166 0 : WrongMonitoringString temp_error(ERS_HERE, get_name(), m_error_counter, cmd_res.result);
167 0 : TLOG() << temp_error;
168 0 : if (m_error_counter >= 10) {
169 0 : ers::error(temp_error);
170 : return;
171 : }
172 0 : if (m_error_counter >= 5) {
173 0 : ers::warning(temp_error);
174 : return;
175 : }
176 : return;
177 0 : }
178 :
179 : // reset the error counter
180 0 : m_error_counter = 0;
181 :
182 0 : std::vector<double> values(string_values.size());
183 :
184 0 : for (size_t i = 1; i < string_values.size(); ++i) {
185 0 : try {
186 0 : values[i] = std::stod(string_values[i]);
187 0 : } catch (const std::logic_error& e) {
188 0 : ers::error(FailedStringConversion(ERS_HERE, string_values[i], e));
189 0 : return;
190 0 : }
191 : }
192 :
193 0 : v_info.set_v_bias_0(values[1]);
194 0 : v_info.set_v_bias_1(values[2]);
195 0 : v_info.set_v_bias_2(values[3]);
196 0 : v_info.set_v_bias_3(values[4]);
197 0 : v_info.set_v_bias_4(values[5]);
198 :
199 0 : v_info.set_power_minus5v(values[6]);
200 0 : v_info.set_power_plus2p5v(values[7]);
201 0 : v_info.set_power_ce(values[8]);
202 :
203 0 : v_info.set_temperature(values[9]);
204 :
205 0 : publish(std::move(v_info));
206 :
207 0 : } catch (const ers::Issue& e) {
208 0 : ers::warning(MonitoringFailed(ERS_HERE, "general info", e));
209 0 : }
210 :
211 : // //current monitor
212 : // for ( size_t ch = 0; ch < m_channel_confs.size() ; ++ch ) {
213 : // if ( m_channel_confs[ch].offset > 0 ) {
214 : // auto current_res = m_interface->send_command("RD CM CH " + std::to_string(ch) );
215 : // TLOG() << current_res.command << " -> " << current_res.result ;
216 : // }
217 :
218 : // // monitor of the ADC
219 : // m_interface->write_register(0x2000, {1234});
220 : // // this trigger the spy buffers
221 :
222 : // // read 100 values for each channel, register ch 8 of each afe, by looping on all the afe we use
223 : // for ( size_t afe = 0; afe < m_afe_confs.size() ; ++afe ) {
224 : // if ( m_afe_confs[afe].v_gain > 0 ) {
225 : // for ( size_t ch = 0; ch < m_channel_confs.size() ; ++ch ) {
226 : // if ( m_channel_confs[ch].offset > 0 ) {
227 : // auto data = m_interface->read_register(0x40000000 + (afe * 0x100000) + (ch * 0x10000), 50); // first 50
228 : // // data[0]
229 :
230 : // data = m_interface->read_register(0x40000000 + (afe * 0x100000) + (ch * 0x10000) +50, 50); // second 50
231 :
232 : // }
233 :
234 : // }
235 : // }
236 :
237 0 : } // NOLINT(readability/fn_size)
238 :
239 : void
240 0 : DaphneV2ControllerModule::do_conf(const CommandData_t&)
241 : {
242 0 : auto start_time = std::chrono::high_resolution_clock::now();
243 :
244 0 : auto board_conf = m_module_config->get_board_conf();
245 0 : auto slot = board_conf->get_slot_id();
246 0 : if (slot >= 16)
247 : // // the slot used laster in the code is a 4 bit register, so we need to check we are not overflowing
248 0 : throw InvalidSlot(ERS_HERE, slot, board_conf->get_address());
249 :
250 : // during configuration no other operations are allowed
251 0 : const std::lock_guard<std::mutex> lock(m_mutex);
252 :
253 0 : create_interface(board_conf->get_address(), m_module_config->get_daphne_conf()->get_timeout());
254 :
255 0 : validate_configuration(*m_module_config->get_board_conf());
256 :
257 0 : disable_links();
258 :
259 0 : configure_timing_endpoints();
260 :
261 0 : configure_analog_chain(true);
262 :
263 0 : align_DDR();
264 :
265 0 : configure_trigger_mode();
266 :
267 0 : reset_counters();
268 :
269 : // we get a list of
270 : // Let's say I want to see 0x5001
271 : // 0x5004 10
272 : // To be discussed - channel/link sorting
273 : // -----------------------------------------
274 : // thing.write_reg(0x2000, {1234});
275 : //
276 :
277 0 : m_scrap_called = false;
278 :
279 0 : auto end_time = std::chrono::high_resolution_clock::now();
280 :
281 0 : auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
282 0 : TLOG() << get_name() << ": board configured in " << duration.count() << " microseconds";
283 0 : }
284 :
285 : void
286 0 : DaphneV2ControllerModule::do_start(const CommandData_t&)
287 : {
288 :
289 0 : auto start_time = std::chrono::high_resolution_clock::now();
290 :
291 0 : reset_counters();
292 :
293 0 : auto end_time = std::chrono::high_resolution_clock::now();
294 :
295 0 : auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
296 0 : TLOG() << get_name() << ": board started in " << duration.count() << " microseconds";
297 0 : }
298 :
299 : void
300 0 : DaphneV2ControllerModule::do_scrap(const CommandData_t&)
301 : {
302 0 : auto start_time = std::chrono::high_resolution_clock::now();
303 :
304 0 : m_scrap_called = true;
305 :
306 : // during configuration no other operations are allowed
307 0 : const std::lock_guard<std::mutex> lock(m_mutex);
308 :
309 0 : disable_links();
310 :
311 0 : configure_analog_chain(false);
312 :
313 : // break the interface
314 0 : m_interface.reset(nullptr);
315 :
316 0 : auto end_time = std::chrono::high_resolution_clock::now();
317 :
318 0 : auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
319 0 : TLOG() << get_name() << ": board releasd in " << duration.count() << " microseconds";
320 0 : }
321 :
322 : void
323 0 : DaphneV2ControllerModule::create_interface(const std::string& ip, std::chrono::milliseconds timeout)
324 : {
325 :
326 0 : static std::regex ip_regex("[0-9]+.[0-9]+.[0-9]+.([0-9]+)");
327 :
328 0 : std::smatch matches;
329 :
330 0 : if (!std::regex_match(ip, matches, ip_regex)) {
331 0 : throw InvalidIPAddress(ERS_HERE, ip);
332 : }
333 :
334 0 : auto board = m_module_config->get_board_conf();
335 0 : TLOG() << get_name() << ": using daphne at " << ip << " with slot "
336 0 : << (int)board->get_slot_id(); // NOLINT(readability/casting)
337 :
338 0 : m_interface = std::make_unique<DaphneV2Interface>(ip.c_str(), 2001, timeout);
339 0 : }
340 :
341 : void
342 0 : DaphneV2ControllerModule::validate_configuration(const appmodel::DaphneV2BoardConf& c) const
343 : {
344 :
345 0 : const auto& channel_confs = c.get_active_channels();
346 :
347 0 : for (const auto& ch : channel_confs) {
348 0 : auto id = ch->get_channel_id();
349 :
350 : // CH OFFSET maximum is 2700 if GAIN is 1, 1500 if GAIN is 2
351 0 : auto gain = ch->get_gain();
352 0 : if (gain != 1 && gain != 2) {
353 0 : throw InvalidChannelConfiguration(ERS_HERE, id, ch->get_trim(), ch->get_offset(), gain);
354 : }
355 0 : auto offset = ch->get_offset();
356 0 : if (gain == 1) {
357 0 : if (offset > 2700)
358 0 : throw InvalidChannelConfiguration(ERS_HERE, id, ch->get_trim(), offset, gain);
359 0 : } else if (gain == 2) {
360 0 : if (offset > 1500)
361 0 : throw InvalidChannelConfiguration(ERS_HERE, id, ch->get_trim(), offset, gain);
362 : }
363 : } // loop over channels
364 :
365 0 : auto size = c.get_full_stream_channels().size();
366 0 : if (size > 16) {
367 : // we can only stream 16 channels at most
368 0 : throw TooManyChannels(ERS_HERE, size);
369 : }
370 0 : }
371 :
372 : void
373 0 : DaphneV2ControllerModule::configure_timing_endpoints()
374 : {
375 :
376 : // 0x00003000 Output record header parameters, read-write, bits 25 to 6, bits 5 to 0 are read only, 26 bits defined
377 : // as: bits 25..22 = slot_id(3..0), default "0010" bits 21..12 = crate_id(9..0), default is "0000000001" bits 11..6 =
378 : // detector_id(5..0), default is "000010" bits 5..0 = version_id(5..0), default is "000010"
379 :
380 0 : auto board = m_module_config->get_board_conf();
381 0 : std::bitset<26> config_value(board->get_slot_id());
382 0 : config_value <<= 10;
383 0 : config_value |= board->get_crate_id();
384 0 : config_value <<= 6;
385 0 : config_value |= board->get_detector_id();
386 0 : config_value <<= 6;
387 0 : config_value |= std::bitset<6>(fddetdataformats::DAPHNEFrame::version).to_ulong();
388 :
389 0 : TLOG() << get_name() << ": configuring timing endpoint with value " << config_value.to_string();
390 0 : m_interface->write_register(0x4001, { 0x1 });
391 0 : m_interface->write_register(0x3000, { config_value.to_ulong() });
392 0 : if (m_module_config->get_daphne_conf()->get_time_reset()) {
393 0 : m_interface->write_register(0x4003, { 1234 });
394 : }
395 :
396 : // waiting for the PLL to lock
397 0 : std::bitset<16> check;
398 0 : int counter = 0;
399 0 : do {
400 0 : ++counter;
401 0 : std::this_thread::sleep_for(std::chrono::milliseconds(5));
402 0 : auto register_check = m_interface->read_register(0x4000, 1);
403 0 : check = std::bitset<16>(register_check[0]);
404 0 : if (counter > 200)
405 : break;
406 0 : } while (!check[0]);
407 :
408 0 : if (!check[0]) {
409 0 : throw PLLNotLocked(ERS_HERE, board->get_slot_id(), "MMCM0");
410 : }
411 :
412 0 : m_interface->write_buffer(0x4002, { 1234 });
413 :
414 : // waiting for the PLL to lock
415 0 : counter = 0;
416 0 : do {
417 0 : ++counter;
418 0 : std::this_thread::sleep_for(std::chrono::milliseconds(5));
419 0 : auto register_check = m_interface->read_register(0x4000, 1);
420 0 : check = std::bitset<16>(register_check[0]);
421 0 : if (counter > 200)
422 : break;
423 0 : } while (!check[1]);
424 :
425 0 : if (!check[1]) {
426 0 : throw PLLNotLocked(ERS_HERE, board->get_slot_id(), "MMCM1");
427 : }
428 :
429 : // at this point everything that is in register 0x4000 is the status of the timing endpoint
430 : // we need to check bit 12 to check if the timing endpoint is valid
431 : // 0 = not ok
432 : // 1 = ok
433 : // should things fail, we can print a lot of messages from register 0x4000
434 :
435 : // there's a necessary delay to let DAPHNE receive and compare the timestamp
436 : // like previous cases, we are going to try to cut this by checking if the system is ready every 5 ms
437 : counter = 0;
438 0 : do {
439 0 : ++counter;
440 0 : std::this_thread::sleep_for(std::chrono::milliseconds(5));
441 0 : auto register_check = m_interface->read_register(0x4000, 1);
442 0 : check = std::bitset<16>(register_check[0]);
443 0 : if (counter > 500)
444 : break;
445 : // we ae happy to wait up to a second (5ms * 200) until calling an error
446 0 : } while (!check[12]);
447 :
448 0 : if (!check[12]) {
449 0 : throw TimingEndpointNotReady(ERS_HERE, board->get_slot_id(), check.to_string());
450 : }
451 :
452 0 : TLOG() << get_name() << ": done donfiguring timing endpoint";
453 0 : }
454 :
455 : void
456 0 : DaphneV2ControllerModule::configure_analog_chain(bool initial_config)
457 : {
458 :
459 0 : TLOG() << get_name() << ": configuring analog chain";
460 :
461 0 : if (initial_config) {
462 0 : auto result = m_interface->send_command("CFG AFE ALL INITIAL");
463 0 : TLOG() << get_name() << ": " << result.command << " -> " << result.result;
464 0 : }
465 :
466 0 : auto board_conf =
467 0 : initial_config ? m_module_config->get_board_conf() : m_module_config->get_daphne_conf()->get_default_v2_settings();
468 :
469 0 : auto result = m_interface->send_command(fmt::format("WR VBIASCTRL V {}", board_conf->get_bias_ctrl()));
470 0 : TLOG() << get_name() << ": " << result.command << " -> " << result.result;
471 :
472 0 : for (size_t ch = 0; ch < s_max_channels; ++ch) {
473 :
474 0 : const auto& channel_conf = board_conf->get_channel(ch);
475 : // get_channel returns the running value if the bool argument is true,
476 : // and the default values when the bool argument is false
477 : // hence, this loop does both the job of enabling and disebling
478 :
479 0 : result = m_interface->send_command(fmt::format("WR TRIM CH {} V {}", ch, channel_conf.get_trim()));
480 0 : TLOG() << get_name() << ": " << result.command << " -> " << result.result;
481 :
482 0 : result = m_interface->send_command(fmt::format("WR OFFSET CH {} V {}", ch, channel_conf.get_offset()));
483 0 : TLOG() << get_name() << ": " << result.command << " -> " << result.result;
484 :
485 0 : result = m_interface->send_command(fmt::format("CFG OFFSET CH {} GAIN {}", ch, channel_conf.get_gain()));
486 0 : TLOG() << get_name() << ": " << result.command << " -> " << result.result;
487 :
488 : } // channel loop
489 :
490 : // to check if the configuration went throguh we can
491 : // cmd (thing, "RD OFFSET CH " + std::to_string(ch), true);
492 : // But Manuel said that this is not necessary to be done all the time
493 :
494 0 : for (size_t afe = 0; afe < s_max_afes; ++afe) {
495 :
496 0 : const auto& afe_conf = board_conf->get_afe(afe);
497 :
498 0 : result = m_interface->send_command(fmt::format("WR AFE {} REG 52 V {}", afe, afe_conf.get_lna()->get_reg52()));
499 0 : TLOG() << get_name() << ": " << result.command << " -> " << result.result;
500 :
501 0 : result = m_interface->send_command(fmt::format("WR AFE {} REG 4 V {}", afe, afe_conf.get_adc()->get_reg4()));
502 0 : TLOG() << get_name() << ": " << result.command << " -> " << result.result;
503 :
504 0 : result = m_interface->send_command(fmt::format("WR AFE {} REG 51 V {}", afe, afe_conf.get_pga()->get_reg51()));
505 0 : TLOG() << get_name() << ": " << result.command << " -> " << result.result;
506 :
507 0 : result = m_interface->send_command(fmt::format("WR AFE {} VGAIN V {}", afe, afe_conf.get_attenuator()));
508 0 : TLOG() << get_name() << ": " << result.command << " -> " << result.result;
509 :
510 0 : result = m_interface->send_command(fmt::format("WR BIASSET AFE {} V {}", afe, afe_conf.get_v_bias()));
511 0 : TLOG() << get_name() << ": " << result.command << " -> " << result.result;
512 :
513 : } // afe loop
514 :
515 : // // To check these values we can do things like
516 : // // cmd (thing, "RD AFE " + std::to_string(AFE) + " REG 52", true);
517 : // // for all these registers and get the values from the replies
518 :
519 0 : TLOG() << get_name() << ": done donfiguring analog chain";
520 0 : }
521 :
522 : void
523 0 : DaphneV2ControllerModule::align_DDR()
524 : {
525 :
526 0 : TLOG() << get_name() << ": aligning DDR";
527 :
528 0 : m_interface->write_register(0x2001, { 1234 });
529 0 : m_interface->write_register(0x2001, { 1234 });
530 0 : m_interface->write_register(0x2001, { 1234 });
531 : // this is correct to be done 3 times
532 :
533 0 : std::this_thread::sleep_for(std::chrono::milliseconds(5));
534 : // this is necessary to give time to the board to align the AFE DDR
535 : // Otherwise further checks become pointless
536 :
537 : // --------------------------------------------
538 : // checking if the alignement is achieved
539 : // --------------------------------------------
540 0 : m_interface->write_register(0x2000, { 1234 });
541 : // this trigger the spy buffers
542 :
543 : // read register ch 8 of each afe, by looping on all the afe we use
544 0 : auto board_conf = m_module_config->get_board_conf();
545 0 : for (size_t afe = 0; afe < s_max_afes; ++afe) {
546 0 : if (board_conf->is_afe_used(afe)) {
547 0 : auto data = m_interface->read_register(0x40000000 + (afe * 0x100000) + (8 * 0x10000), 15); // ch = 8
548 :
549 : // things are ok when the data is 0x3f80
550 0 : if (data[0] != DaphneV2ControllerModule::s_frame_alignment_good)
551 0 : throw DDRNotAligned(ERS_HERE, board_conf->get_slot_id(), afe, data[0]);
552 0 : } // afe used
553 : }
554 :
555 0 : TLOG() << get_name() << ": done aligning DDR";
556 0 : }
557 :
558 : void
559 0 : DaphneV2ControllerModule::disable_links()
560 : {
561 0 : TLOG() << get_name() << ": disabling all links";
562 0 : m_interface->write_register(0x3001, { 0x0 });
563 0 : TLOG() << get_name() << ": all links disabled";
564 0 : }
565 :
566 : void
567 0 : DaphneV2ControllerModule::configure_trigger_mode()
568 : {
569 :
570 0 : TLOG() << get_name() << ": Setting trigger mode";
571 :
572 0 : auto c = m_module_config->get_board_conf();
573 :
574 0 : m_interface->write_register(0x6100, { c->get_self_trigger_xcorr() });
575 0 : m_interface->write_register(0x6002, { c->get_tp_conf() });
576 0 : m_interface->write_register(0x6003, { c->get_compensator() });
577 0 : m_interface->write_register(0x6004, { c->get_inverter() });
578 :
579 0 : auto threshold = c->get_self_trigger_threshold();
580 :
581 0 : if (threshold > 0) {
582 : // se are in self trigger mode
583 0 : m_interface->write_register(0x3001, { 0x3 }); // only link0 is enabled
584 0 : m_interface->write_register(0x6000, { threshold });
585 :
586 0 : std::bitset<DaphneV2ControllerModule::s_max_channels> mask;
587 0 : auto board_conf = m_module_config->get_board_conf();
588 : // we unmask all the channels that are enabled
589 0 : for (ChannelId ch = 0; ch < s_max_channels; ++ch) {
590 0 : if (board_conf->is_channel_used(ch))
591 0 : mask[ch] = true;
592 : }
593 0 : m_interface->write_register(0x6001, { (uint64_t)mask.to_ulong() }); // NOLINT
594 :
595 : // check
596 : // thing.read(0x3001, 1)
597 : // the result should be 0x3
598 :
599 : } else {
600 0 : m_interface->write_register(0x3001, { 0xaa });
601 0 : m_interface->write_register(0x6000, { 0 }); // for safety we mask everything
602 :
603 0 : size_t stream_id = 0;
604 0 : auto full_stream_channels = c->get_full_stream_channels();
605 0 : for (const auto& ch : full_stream_channels) {
606 :
607 : // The channles are not identified with an id from 0-39, they have a different identifier to represent the
608 : // cables in the fron of the board. They are grouped in 8
609 : // Conf ch -> DAQ ch
610 : // 0-7 -> 0-7
611 : // 8-15 -> 10-17
612 : // 16-23 -> 20-27
613 : // 24-31 -> 30-37
614 : // 32-39 -> 40-47
615 :
616 0 : auto reg = 0x5000 + stream_id; // stream is first come first served basis
617 0 : auto value = (ch / 8) * 10 + ch % 8;
618 :
619 0 : m_interface->write_register(reg, { (uint64_t)value }); // NOLINT
620 :
621 0 : ++stream_id;
622 : } // loop over full_stream channels
623 0 : }
624 :
625 0 : TLOG() << get_name() << ": trigger mode configured";
626 0 : }
627 :
628 : void
629 0 : DaphneV2ControllerModule::reset_counters()
630 : {
631 :
632 0 : m_last_package_counter = 0;
633 0 : m_last_unsent_counter = 0;
634 0 : for (auto& c : m_channel_counters) {
635 0 : c.triggers = 0;
636 0 : c.packets = 0;
637 : }
638 0 : m_interface->write_register(0x4004, { 1234 });
639 0 : }
640 :
641 : // void
642 : // DaphneV2ControllerModule::dump_buffers(const CommandData_t& conf_as_json)
643 : // {
644 : // auto start_time = std::chrono::high_resolution_clock::now();
645 :
646 : // auto conf_as_cpp = conf_as_json.get<DaphneV2ControllerModule::DumpBuffers>();
647 :
648 : // // during dumping no other operations are allowed
649 : // const std::lock_guard<std::mutex> lock(m_mutex);
650 :
651 : // std::string file_name(conf_as_cpp.directory);
652 : // if ( file_name.back() != '/' ) file_name += '/';
653 : // file_name += "spy_buffers_" + std::to_string(m_module_config->get_slot());
654 :
655 : // auto t = std::time(nullptr);
656 : // auto tm = *std::localtime(&t);
657 : // std::ostringstream oss;
658 : // oss << std::put_time(&tm, "%Y-%m-%dT%H-%M-%S");
659 : // file_name += '_' + oss.str() + ".txt";
660 :
661 : // size_t entries = std::min(conf_as_cpp.n_samples, (decltype(conf_as_cpp.n_samples)) 1024);
662 : // const size_t max_batch_size = 50;
663 :
664 : // m_interface->write_register(0x2000, {1234});
665 : // // this triggers the spy buffers
666 :
667 : // std::ofstream file(file_name);
668 :
669 : // for ( size_t ch = 0; ch < m_channel_confs.size(); ++ch) {
670 : // if ( ! channel_used(ch) ) continue;
671 :
672 : // auto afe = ch / 8;
673 : // auto ch_id = ch % 8;
674 :
675 : // file << "AFE "<< afe << " CH " << ch_id;
676 :
677 : // size_t counter = 0;
678 : // while ( counter < entries ) {
679 : // auto n_points = counter + max_batch_size > entries ? entries-counter : max_batch_size;
680 : // auto data = m_interface->read_register(0x40000000 + (afe * 0x100000) + (ch_id * 0x10000) + counter, n_points);
681 : // counter += n_points;
682 : // for ( const auto & e : data ) {
683 : // file << " " << e;
684 : // }
685 :
686 : // } // loop over the queries for the same channel
687 :
688 : // file << std::endl;
689 : // } // loop over the channels
690 :
691 : // auto end_time = std::chrono::high_resolution_clock::now();
692 :
693 : // auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
694 : // TLOG() << get_name() << ": buffers dumped in " << duration.count() << " microseconds";
695 :
696 : // }
697 :
698 : } // namespace dunedaq::daphnemodules
699 :
700 0 : DEFINE_DUNE_DAQ_MODULE(dunedaq::daphnemodules::DaphneV2ControllerModule)
|