LCOV - code coverage report
Current view: top level - daphnemodules/plugins - DaphneV2ControllerModule.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 0.0 % 305 0
Test Date: 2026-10-11 15:05:20 Functions: 0.0 % 40 0

            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)
        

Generated by: LCOV version 2.0-1