Line data Source code
1 : /**
2 : * @file MasterNode.cpp
3 : *
4 : * This is part of the DUNE DAQ Software Suite, copyright 2020.
5 : * Licensing/copyright details are in the COPYING file that you should have
6 : * received with this code.
7 : */
8 :
9 : #include "timing/MasterNode.hpp"
10 : #include "timing/MasterGlobalNode.hpp"
11 :
12 : #include "logging/Logging.hpp"
13 :
14 : #include <string>
15 :
16 : namespace dunedaq {
17 : namespace timing {
18 :
19 0 : UHAL_REGISTER_DERIVED_NODE(MasterNode)
20 :
21 : //-----------------------------------------------------------------------------
22 0 : MasterNode::MasterNode(const uhal::Node& node)
23 0 : : MasterNodeInterface(node)
24 0 : {}
25 : //-----------------------------------------------------------------------------
26 :
27 : //-----------------------------------------------------------------------------
28 0 : MasterNode::~MasterNode() {}
29 : //-----------------------------------------------------------------------------
30 :
31 : //-----------------------------------------------------------------------------
32 : std::string
33 0 : MasterNode::get_status_tables() const
34 : {
35 0 : std::stringstream status;
36 :
37 0 : status << getNode<TimestampGeneratorNode>("tstamp").get_status();
38 0 : status << std::endl;
39 :
40 0 : status << getNode<MasterGlobalNode>("global").get_status();
41 0 : status << std::endl;
42 :
43 0 : status << getNode<FLCmdGeneratorNode>("scmd_gen").get_cmd_counters_table();
44 0 : status << std::endl;
45 :
46 0 : getNode("cmd_ctrs.addr").write(0x0);
47 0 : auto counters = getNode("cmd_ctrs.data").readBlock(0xff);
48 0 : getClient().dispatch();
49 :
50 0 : std::vector<uint32_t> non_zero_counters;
51 0 : std::vector<std::string> counter_labels;
52 :
53 0 : for (uint i=0; i < counters.size(); ++i)
54 : {
55 0 : auto counter = counters.at(i);
56 0 : if (counter > 0)
57 : {
58 0 : counter_labels.push_back(format_reg_value(i));
59 0 : non_zero_counters.push_back(counter);
60 : }
61 : }
62 :
63 0 : std::vector<std::vector<uint32_t>> counters_container = { non_zero_counters }; // NOLINT(build/unsigned)
64 :
65 0 : status << format_counters_table(counters_container, { "Sent cmd counters" }, "Master cmd counters (>0)", counter_labels);
66 0 : status << std::endl;
67 :
68 0 : auto acmd_buf = read_sub_nodes(getNode("acmd_buf.stat"));
69 0 : status << format_reg_table(acmd_buf, "Master acmd buffer");
70 :
71 0 : return status.str();
72 0 : }
73 : //-----------------------------------------------------------------------------
74 :
75 : //-----------------------------------------------------------------------------
76 : std::string
77 0 : MasterNode::get_status(bool print_out) const
78 : {
79 0 : std::stringstream status;
80 0 : auto raw_timestamp = getNode<TimestampGeneratorNode>("tstamp").read_raw_timestamp();
81 0 : status << "Timestamp: 0x" << std::hex << tstamp2int(raw_timestamp) << std::endl << std::endl;
82 0 : status << get_status_tables();
83 :
84 0 : if (print_out)
85 0 : TLOG() << status.str();
86 0 : return status.str();
87 0 : }
88 : //-----------------------------------------------------------------------------
89 :
90 : //-----------------------------------------------------------------------------
91 : std::string
92 0 : MasterNode::get_status_with_date(uint32_t clock_frequency_hz, bool print_out) const // NOLINT(build/unsigned)
93 : {
94 0 : std::stringstream status;
95 0 : auto raw_timestamp = getNode<TimestampGeneratorNode>("tstamp").read_raw_timestamp();
96 0 : status << "Timestamp: 0x" << std::hex << tstamp2int(raw_timestamp) << " -> " << format_timestamp(raw_timestamp, clock_frequency_hz) << std::endl
97 0 : << std::endl;
98 0 : status << get_status_tables();
99 :
100 0 : if (print_out)
101 0 : TLOG() << status.str();
102 0 : return status.str();
103 0 : }
104 : //-----------------------------------------------------------------------------
105 :
106 : //-----------------------------------------------------------------------------
107 : void
108 0 : MasterNode::switch_endpoint_sfp(uint32_t address, bool turn_on) const // NOLINT(build/unsigned)
109 : {
110 0 : uint32_t sequence = 0xab;
111 0 : uint32_t address_mode = 1;
112 :
113 0 : std::vector<uint32_t> tx_packet = { address & 0xff,
114 0 : address >> 8UL,
115 : sequence,
116 :
117 : // packet to reset rx
118 : (0x1 << 7UL) | 0x70, // write transaction on 0x70
119 : (address_mode << 7UL) | 0x1, // transaction length of 0x1
120 0 : turn_on,
121 0 : };
122 0 : tx_packet.back() = tx_packet.back() | (0x1 << 8UL);
123 :
124 0 : auto result = transmit_async_packet(tx_packet, -1);
125 0 : }
126 : //-----------------------------------------------------------------------------
127 :
128 : //-----------------------------------------------------------------------------
129 : void
130 0 : MasterNode::enable_upstream_endpoint() const
131 : {
132 0 : auto global = getNode<MasterGlobalNode>("global");
133 0 : global.enable_upstream_endpoint();
134 0 : }
135 : //-----------------------------------------------------------------------------
136 :
137 : //-----------------------------------------------------------------------------
138 : void
139 0 : MasterNode::send_fl_cmd(uint32_t command,
140 : uint32_t channel, // NOLINT(build/unsigned)
141 : uint32_t number_of_commands) const // NOLINT(build/unsigned)
142 : {
143 0 : for (uint32_t i = 0; i < number_of_commands; i++) { // NOLINT(build/unsigned)
144 0 : getNode<FLCmdGeneratorNode>("scmd_gen").send_fl_cmd(command, channel);
145 :
146 0 : auto ts_l = getNode("cmd_log.tstamp_l").read();
147 0 : auto ts_h = getNode("cmd_log.tstamp_h").read();
148 0 : auto sent_cmd = getNode("cmd_log.cmd").read();
149 0 : getClient().dispatch();
150 :
151 0 : if (sent_cmd.value() != command)
152 : {
153 0 : TLOG() << "cmd in sent log: 0x" << std::hex << command << ", does not match requested 0x: " << sent_cmd.value();
154 : // TODO throw something
155 : }
156 0 : uint64_t timestamp = (uint64_t)ts_h.value() << 32 | ts_l.value();
157 0 : TLOG() << "Command sent " << "(" << format_reg_value(command) << ") from generator "
158 0 : << format_reg_value(channel) << " @time " << std::hex << std::showbase << timestamp;
159 0 : }
160 0 : }
161 : //-----------------------------------------------------------------------------
162 :
163 : //-----------------------------------------------------------------------------
164 : uint32_t // NOLINT(build/unsigned)
165 0 : MasterNode::measure_endpoint_rtt(uint32_t address, bool control_sfp) const // NOLINT(build/unsigned)
166 : {
167 :
168 0 : auto global = getNode<MasterGlobalNode>("global");
169 0 : auto echo = getNode<EchoMonitorNode>("echo_mon");
170 :
171 0 : if (control_sfp)
172 : {
173 : // Switch off all TX SFPs
174 : //switch_endpoint_sfp(0xffff, false);
175 :
176 : // Turn on the current target
177 0 : switch_endpoint_sfp(address, true);
178 :
179 0 : millisleep(100);
180 :
181 0 : try
182 : {
183 0 : global.enable_upstream_endpoint();
184 : }
185 0 : catch (const timing::ReceiverNotReady& e)
186 : {
187 0 : if (control_sfp) {
188 0 : switch_endpoint_sfp(address, false);
189 : }
190 0 : throw e;
191 0 : }
192 : }
193 :
194 0 : uint32_t endpoint_rtt = echo.send_echo_and_measure_delay(); // NOLINT(build/unsigned)
195 :
196 0 : if (control_sfp)
197 0 : switch_endpoint_sfp(address, false);
198 :
199 0 : return endpoint_rtt;
200 0 : }
201 : //-----------------------------------------------------------------------------
202 :
203 : //-----------------------------------------------------------------------------
204 : void
205 0 : MasterNode::apply_endpoint_delay(uint32_t address, // NOLINT(build/unsigned)
206 : uint32_t coarse_delay, // NOLINT(build/unsigned)
207 : uint32_t fine_delay, // NOLINT(build/unsigned)
208 : uint32_t /*phase_delay*/, // NOLINT(build/unsigned)
209 : bool measure_rtt,
210 : bool control_sfp) const
211 : {
212 :
213 0 : auto global = getNode<MasterGlobalNode>("global");
214 0 : auto echo = getNode<EchoMonitorNode>("echo_mon");
215 :
216 0 : if (measure_rtt) {
217 0 : if (control_sfp) {
218 : // Switch off all TX SFPs
219 : // switch_endpoint_sfp(0xffff, false);
220 :
221 : // Turn on the current target
222 0 : switch_endpoint_sfp(address, true);
223 :
224 0 : millisleep(100);
225 : }
226 :
227 0 : try
228 : {
229 0 : global.enable_upstream_endpoint();
230 : }
231 0 : catch (const timing::ReceiverNotReady& e)
232 : {
233 0 : if (control_sfp) {
234 0 : switch_endpoint_sfp(address, false);
235 : }
236 0 : throw e;
237 0 : }
238 :
239 0 : uint64_t endpoint_rtt = echo.send_echo_and_measure_delay(); // NOLINT(build/unsigned)
240 0 : TLOG() << "Pre delay adjustment RTT: " << format_reg_value(endpoint_rtt, 10);
241 : }
242 :
243 0 : uint32_t sequence = 0xab;
244 0 : uint32_t address_mode = 1;
245 :
246 0 : std::vector<uint32_t> tx_packet = { address & 0xff,
247 0 : address >> 8UL,
248 : sequence,
249 :
250 : // packet to write coarse delay
251 : (0x1 << 7UL) | 0x72, // write transaction on 0x72
252 : (address_mode << 7UL) | 0x1, // transaction length of 0x1
253 0 : ((fine_delay & 0xf) << 4UL) | (coarse_delay & 0xf),
254 :
255 : // packet to write fine delay
256 : (0x1 << 7UL) | 0x73, // write transaction on 0x73
257 : (address_mode << 7UL) | 0x1, // transaction length of 0x1
258 0 : (fine_delay >> 4UL) & 0xff,
259 :
260 : // packet to set skew done
261 : (0x1 << 7UL) | 0x70, // write transaction on 0x70
262 : (address_mode << 7UL) | 0x1, // transaction length of 0x1
263 : 0x3, // deskew done
264 :
265 : // packet to resync
266 : (0x1 << 7UL) | 0x70, // write transaction on 0x70
267 : (address_mode << 7UL) | 0x1, // transaction length of 0x1
268 : 0x4, // resync
269 0 : };
270 :
271 0 : tx_packet.back() = tx_packet.back() | (0x1 << 8UL);
272 :
273 0 : transmit_async_packet(tx_packet, -1);
274 :
275 0 : if (measure_rtt) {
276 0 : try
277 : {
278 0 : global.enable_upstream_endpoint();
279 : }
280 0 : catch (const timing::ReceiverNotReady& e)
281 : {
282 0 : if (control_sfp)
283 : {
284 0 : switch_endpoint_sfp(address, false);
285 : }
286 0 : throw e;
287 0 : }
288 :
289 0 : uint64_t endpoint_rtt = echo.send_echo_and_measure_delay(); // NOLINT(build/unsigned)
290 0 : TLOG() << "Post delay adjustment RTT: " << format_reg_value(endpoint_rtt, 10);
291 :
292 0 : if (control_sfp)
293 0 : switch_endpoint_sfp(address, false);
294 : }
295 0 : }
296 : //-----------------------------------------------------------------------------
297 :
298 : //-----------------------------------------------------------------------------
299 : void
300 0 : MasterNode::sync_timestamp(TimestampSource source) const // NOLINT(build/unsigned)
301 : {
302 0 : set_timestamp(source);
303 :
304 0 : enable_timestamp_broadcast();
305 0 : TLOG() << "Timestamp broadcast enabled";
306 0 : }
307 : //-----------------------------------------------------------------------------
308 :
309 : //-----------------------------------------------------------------------------
310 : uint64_t // NOLINT(build/unsigned)
311 0 : MasterNode::read_timestamp() const
312 : {
313 0 : return getNode<TimestampGeneratorNode>("tstamp").read_timestamp();
314 : }
315 : //-----------------------------------------------------------------------------
316 :
317 : //-----------------------------------------------------------------------------
318 : void
319 0 : MasterNode::set_timestamp(TimestampSource source) const // NOLINT(build/unsigned)
320 : {
321 0 : getNode<TimestampGeneratorNode>("tstamp").set_timestamp(source);
322 0 : }
323 : //-----------------------------------------------------------------------------
324 :
325 : //-----------------------------------------------------------------------------
326 : void
327 0 : MasterNode::get_info(timingfirmwareinfo::MasterMonitorData& mon_data) const
328 : {
329 0 : mon_data.timestamp = read_timestamp();
330 :
331 0 : auto control = read_sub_nodes(getNode("global.csr.ctrl"), false);
332 0 : auto state = read_sub_nodes(getNode("global.csr.stat"), false);
333 0 : getClient().dispatch();
334 :
335 0 : mon_data.ts_bcast_enable = control.at("ts_en").value();
336 0 : mon_data.ts_valid = state.at("ts_valid").value();
337 0 : mon_data.ts_tx_err = state.at("ts_tx_err").value();
338 0 : mon_data.tx_err = state.at("tx_err").value();
339 0 : mon_data.ctrs_rdy = state.at("ctrs_rdy").value();
340 :
341 : // ic.add(mon_data);
342 :
343 : // uint number_of_commands = 0xff;
344 :
345 : // getNode("cmd_ctrs.addr").write(0x0);
346 : // auto counters = getNode("cmd_ctrs.data").readBlock(number_of_commands);
347 : // getClient().dispatch();
348 :
349 : // for (uint i = 0; i < number_of_commands; ++i) { // NOLINT(build/unsigned)
350 :
351 : // timingfirmwareinfo::SentCommandCounter cmd_counter;
352 : // opmonlib::InfoCollector cmd_counter_ic;
353 :
354 : // cmd_counter.counts = counters.at(i);
355 :
356 : // std::stringstream channel;
357 : // channel << "cmd_0x" << std::hex << i;
358 :
359 : // cmd_counter_ic.add(cmd_counter);
360 : // ic.add(channel.str(), cmd_counter_ic);
361 : // }
362 :
363 : // getNode<FLCmdGeneratorNode>("scmd_gen").get_info(ic, level);
364 0 : }
365 : //-----------------------------------------------------------------------------
366 :
367 : //-----------------------------------------------------------------------------
368 0 : void MasterNode::reset_command_counters() const
369 : {
370 0 : auto global = getNode<MasterGlobalNode>("global");
371 0 : global.reset_command_counters();
372 0 : }
373 : //-----------------------------------------------------------------------------
374 :
375 : //-----------------------------------------------------------------------------
376 : std::vector<uint32_t>
377 0 : MasterNode::transmit_async_packet(const std::vector<uint32_t>& packet, int timeout) const
378 : {
379 : // TODO: check for valid packet
380 :
381 0 : reset_sub_nodes(getNode("acmd_buf.txbuf"));
382 :
383 0 : TLOG_DEBUG(11) << "tx packet: ";
384 0 : for (auto t : packet)
385 0 : TLOG_DEBUG(11) << std::hex << "0x" << t;
386 :
387 0 : getNode("acmd_buf.txbuf").writeBlock(packet);
388 0 : getClient().dispatch();
389 :
390 : // we do not expect a reply
391 0 : if (timeout < 0)
392 : {
393 0 : std::vector<uint32_t> empty_vector;
394 0 : return empty_vector;
395 0 : }
396 :
397 0 : uhal::ValWord<uint32_t> buffer_ready; // NOLINT(build/unsigned)
398 0 : uhal::ValWord<uint32_t> buffer_timeout; // NOLINT(build/unsigned)
399 :
400 : // start time counting
401 0 : auto start = std::chrono::high_resolution_clock::now();
402 :
403 : // Wait for the buffer to be happy
404 0 : while (true) {
405 :
406 0 : buffer_ready = getNode("acmd_buf.stat.ready").read();
407 0 : buffer_timeout = getNode("acmd_buf.stat.timeout").read();
408 0 : getClient().dispatch();
409 :
410 0 : TLOG_DEBUG(10) << "async buffer ready: 0x" << buffer_ready.value() << ", timeout: " << buffer_timeout.value();
411 :
412 0 : if (buffer_timeout)
413 0 : throw VLCommandReplyTimeout(ERS_HERE);
414 :
415 0 : if (buffer_ready)
416 : break;
417 :
418 0 : auto now = std::chrono::high_resolution_clock::now();
419 0 : auto us_since_start = std::chrono::duration_cast<std::chrono::microseconds>(now - start);
420 :
421 0 : if (us_since_start.count() > timeout)
422 0 : throw VLCommandReplyBufferFlagTimeout(ERS_HERE, timeout);
423 :
424 0 : std::this_thread::sleep_for(std::chrono::microseconds(50));
425 0 : }
426 :
427 0 : auto rx_packet = getNode("acmd_buf.rxbuf").readBlock(0x20);
428 0 : getClient().dispatch();
429 :
430 0 : if (rx_packet.at(0) != 0xff || rx_packet.at(1) != 0xff || rx_packet.at(2) != packet.at(2))
431 : {
432 0 : ers::warning(InvalidVLCommandReplyPacket(ERS_HERE, rx_packet.at(0), rx_packet.at(1), rx_packet.at(2)));
433 : }
434 :
435 0 : TLOG_DEBUG(11) << "async result: ";
436 0 : for (auto r : rx_packet)
437 0 : TLOG_DEBUG(11) << std::hex << "0x" << r;
438 :
439 0 : return rx_packet.value();
440 0 : }
441 : //-----------------------------------------------------------------------------
442 :
443 : //-----------------------------------------------------------------------------
444 : void
445 0 : MasterNode::write_endpoint_data(uint16_t endpoint_address, uint8_t reg_address, std::vector<uint8_t> data, bool address_mode) const
446 : {
447 0 : auto data_length = data.size();
448 0 : if (data_length > 0x3f || data_length == 0)
449 : {
450 0 : TLOG() << "invalid data length";
451 : }
452 :
453 : // TODO make sequence a function argument?
454 0 : uint32_t sequence = 0xab;
455 :
456 0 : std::vector<uint32_t> tx_packet = { static_cast<uint32_t>(endpoint_address & 0xff),
457 0 : static_cast<uint32_t>(endpoint_address >> 8UL),
458 : sequence,
459 : // bit 7 = 1 -> write
460 0 : static_cast<uint32_t>((0x1 << 7UL) | reg_address),
461 0 : static_cast<uint32_t>((address_mode << 7UL) | (0x3f & data_length))
462 0 : };
463 0 : tx_packet.insert(tx_packet.end(), data.begin(), data.end());
464 0 : tx_packet.back() = tx_packet.back() | (0x1 << 8UL);
465 :
466 0 : auto result = transmit_async_packet(tx_packet);
467 0 : }
468 : //-----------------------------------------------------------------------------
469 :
470 : //-----------------------------------------------------------------------------
471 : std::vector<uint32_t>
472 0 : MasterNode::read_endpoint_data(uint16_t endpoint_address, uint8_t reg_address, uint8_t data_length, bool address_mode) const
473 : {
474 0 : if (data_length > 0x3f || data_length == 0)
475 : {
476 0 : TLOG() << "invalid data length";
477 : // TODO throw something
478 : }
479 :
480 : // TODO make sequence a function argument?
481 0 : uint32_t sequence = 0xab;
482 0 : std::vector<uint32_t> tx_packet = { static_cast<uint32_t>(endpoint_address & 0xff),
483 0 : static_cast<uint32_t>(endpoint_address >> 8UL),
484 : sequence,
485 : // bit 7 = 0 -> read
486 0 : reg_address,
487 0 : static_cast<uint32_t>((0x1 << 8UL) | (address_mode << 7UL) | (0x3f & data_length))
488 0 : };
489 :
490 0 : auto result = transmit_async_packet(tx_packet);
491 :
492 : // get parts we actually want
493 0 : std::vector<uint32_t> result_data (result.begin()+3, result.begin()+3+data_length);
494 :
495 : // strip off the bit 8 which is high for last byte
496 0 : result_data.back() = result_data.back() & 0xff;
497 :
498 0 : return result_data;
499 0 : }
500 : //-----------------------------------------------------------------------------
501 :
502 : //-----------------------------------------------------------------------------
503 0 : void MasterNode::disable_timestamp_broadcast() const
504 : {
505 0 : getNode("global.csr.ctrl.ts_en").write(0x0);
506 0 : getClient().dispatch();
507 0 : }
508 : //-----------------------------------------------------------------------------
509 :
510 : //-----------------------------------------------------------------------------
511 0 : void MasterNode::enable_timestamp_broadcast() const
512 : {
513 0 : getNode("global.csr.ctrl.ts_en").write(0x1);
514 0 : getClient().dispatch();
515 0 : }
516 : //-----------------------------------------------------------------------------
517 :
518 : //-----------------------------------------------------------------------------
519 : timingfirmware::EndpointCheckResult
520 0 : MasterNode::scan_endpoint(uint16_t endpoint_address, bool control_sfp) const
521 : {
522 0 : timingfirmware::EndpointCheckResult result;
523 0 : auto global = getNode<MasterGlobalNode>("global");
524 0 : auto echo = getNode<EchoMonitorNode>("echo_mon");
525 :
526 0 : timingfirmware::EndpointCheckResult endpoint_result;
527 0 : endpoint_result.address = endpoint_address;
528 :
529 : // is endpoint sfp switched on?
530 : // are any relevant muxes set to correct channel?
531 0 : if (control_sfp)
532 : {
533 0 : switch_endpoint_sfp(endpoint_address, true);
534 :
535 0 : millisleep(100);
536 : }
537 :
538 0 : try
539 : {
540 0 : global.enable_upstream_endpoint();
541 : }
542 0 : catch (const timing::ReceiverNotReady& e)
543 : {
544 0 : switch_endpoint_sfp(endpoint_address, false);
545 :
546 : //ers::error(MonitoredEndpointDead(ERS_HERE, endpoint_address));
547 :
548 0 : return endpoint_result;
549 0 : }
550 :
551 0 : endpoint_result.alive = true;
552 0 : endpoint_result.round_trip_time = echo.send_echo_and_measure_delay();
553 0 : TLOG_DEBUG(5) << "Endpoint at address " << endpoint_address << " alive. RTT: " << endpoint_result.round_trip_time;
554 :
555 0 : auto ept_state = read_endpoint_data(endpoint_address, 0x71, 0x1, 0x1).at(0) & 0xf;
556 0 : TLOG_DEBUG(5) << "Endpoint at address " << endpoint_address << " state: 0x" << std::hex << ept_state;
557 0 : endpoint_result.state = ept_state;
558 :
559 0 : if (ept_state == 0x6)
560 : {
561 0 : TLOG_DEBUG(5) << "Endpoint at address " << endpoint_address << ", applying delays of: " << 0x0;
562 0 : ers::info(MonitoredEndpointDelaySet(ERS_HERE, 0x0, endpoint_address, ept_state));
563 0 : apply_endpoint_delay(endpoint_address, 0x0, 0x0, 0x0, false, false);
564 :
565 0 : endpoint_result.applied_delay = 0x0;
566 :
567 0 : auto ept_state_after_delays = read_endpoint_data(endpoint_address, 0x71, 0x1, 0x1).at(0) & 0xf;
568 0 : TLOG_DEBUG(5) << "Endpoint at address " << endpoint_address << ", state after delays apply: " << ept_state_after_delays;
569 0 : endpoint_result.state_after_delay_apply = ept_state_after_delays;
570 :
571 0 : endpoint_result.round_trip_time_after_delay_apply = echo.send_echo_and_measure_delay();
572 0 : TLOG_DEBUG(5) << "Endpoint at address " << endpoint_address << ", RTT after delays apply: " << endpoint_result.round_trip_time_after_delay_apply;
573 : }
574 0 : else if (ept_state == 0x7 || ept_state == 0x8)
575 : {
576 0 : TLOG_DEBUG(5) << "Endpoint at address " << endpoint_address << ", delays not needed";
577 : }
578 : else
579 : {
580 0 : ers::error(MonitoredEndpointUnexpectedState(ERS_HERE, endpoint_address, ept_state));
581 : }
582 :
583 0 : if (control_sfp)
584 : {
585 0 : switch_endpoint_sfp(endpoint_address, false);
586 : }
587 :
588 : return endpoint_result;
589 0 : }
590 : //-----------------------------------------------------------------------------
591 :
592 : //-----------------------------------------------------------------------------
593 0 : void MasterNode::configure_endpoint_command_decoder(uint16_t endpoint_address, uint8_t slot, uint8_t command) const
594 : {
595 0 : write_endpoint_data(endpoint_address, 0x60+slot, {command}, true);
596 0 : }
597 : //-----------------------------------------------------------------------------
598 : } // namespace timing
599 : } // namespace dunedaq
|