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