LCOV - code coverage report
Current view: top level - daphnemodules/plugins - DaphneV2ControllerModule.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 0.0 % 313 0
Test Date: 2025-12-21 13:07:08 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/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)
        

Generated by: LCOV version 2.0-1