LCOV - code coverage report
Current view: top level - timing/src - I2CSFPNode.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 0.0 % 188 0
Test Date: 2025-12-21 13:07:08 Functions: 0.0 % 34 0

            Line data    Source code
       1              : /**
       2              :  * @file I2CSFPNode.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/I2CSFPNode.hpp"
      10              : 
      11              : #include "logging/Logging.hpp"
      12              : 
      13              : #include <string>
      14              : #include <utility>
      15              : #include <vector>
      16              : 
      17              : namespace dunedaq {
      18              : namespace timing {
      19              : 
      20              : //-----------------------------------------------------------------------------
      21            0 : I2CSFPSlave::I2CSFPSlave(const I2CMasterNode* i2c_master, uint8_t address) // NOLINT(build/unsigned)
      22              :   : I2CSlave(i2c_master, address)
      23            0 :   , m_calibration_parameter_start_addresses({ 0x4C, 0x50, 0x54, 0x58 }) // laser current, tx_pwr, temp, voltage
      24            0 : {}
      25              : //-----------------------------------------------------------------------------
      26              : 
      27              : //-----------------------------------------------------------------------------
      28            0 : I2CSFPSlave::~I2CSFPSlave() {}
      29              : //-----------------------------------------------------------------------------
      30              : 
      31              : //-----------------------------------------------------------------------------
      32              : void
      33            0 : I2CSFPSlave::sfp_reachable() const
      34              : {
      35            0 :   if (!this->ping(true)) {
      36            0 :     throw SFPUnreachable(ERS_HERE, get_master_id());
      37              :   }
      38            0 : }
      39              : //-----------------------------------------------------------------------------
      40              : 
      41              : //-----------------------------------------------------------------------------
      42              : void
      43            0 : I2CSFPSlave::ddm_available() const
      44              : {
      45            0 :   if (!read_ddm_support_bit()) {
      46            0 :     throw SFPDDMUnsupported(ERS_HERE, get_master_id());
      47              :   } else {
      48            0 :     if (read_i2c_reg_addressSwapBit()) {
      49            0 :       throw SFPDDMI2CAddressSwapUnsupported(ERS_HERE, get_master_id());
      50              :     }
      51              :   }
      52            0 : }
      53              : //-----------------------------------------------------------------------------
      54              : 
      55              : //-----------------------------------------------------------------------------
      56              : std::pair<double, double>
      57            0 : I2CSFPSlave::read_calibration_parameter_pair(uint32_t calib_parameter_id) const // NOLINT(build/unsigned)
      58              : {
      59              : 
      60            0 :   ddm_available();
      61              : 
      62            0 :   auto parameter_array = this->read_i2cArray(0x51, m_calibration_parameter_start_addresses.at(calib_parameter_id), 0x4);
      63              : 
      64              :   // slope
      65            0 :   double slope = parameter_array.at(0) + (parameter_array.at(1) / 256.0);
      66              : 
      67            0 :   uint32_t offset_raw = (parameter_array.at(2) & 0x7f) << 8 | parameter_array.at(3); // NOLINT(build/unsigned)
      68              : 
      69              :   // eighth bit corresponds to sign
      70            0 :   double offset = parameter_array.at(2) & (1UL << 7) ? offset_raw - 0x8000 : offset_raw;
      71              : 
      72            0 :   return std::make_pair(slope, offset);
      73            0 : }
      74              : //-----------------------------------------------------------------------------
      75              : 
      76              : //-----------------------------------------------------------------------------
      77              : double
      78            0 : I2CSFPSlave::read_temperature_raw() const
      79              : {
      80            0 :   ddm_available();
      81            0 :   auto temperature_array = this->read_i2cArray(0x51, 0x60, 0x2);
      82              : 
      83              :   // bit 7 corresponds to temperature sign, 0 for pos, 1 for neg
      84            0 :   double temperature = temperature_array.at(0) & (1UL << 7) ? (temperature_array.at(0) & 0x7f) - 0xff : temperature_array.at(0);
      85            0 :   return temperature + (temperature_array.at(1) / 256.0);
      86            0 : }
      87              : //-----------------------------------------------------------------------------
      88              : 
      89              : //-----------------------------------------------------------------------------
      90              : double
      91            0 : I2CSFPSlave::read_temperature() const
      92              : {
      93            0 :   auto temperature_raw = read_temperature_raw();
      94            0 :   auto temp_calib_pair = read_calibration_parameter_pair(0x2);
      95            0 :   return temperature_raw * temp_calib_pair.first + temp_calib_pair.second;
      96              : }
      97              : //-----------------------------------------------------------------------------
      98              : 
      99              : //-----------------------------------------------------------------------------
     100              : double
     101            0 : I2CSFPSlave::read_voltage_raw() const
     102              : {
     103            0 :   ddm_available();
     104            0 :   auto voltage_array = this->read_i2cArray(0x51, 0x62, 0x2);
     105            0 :   return (voltage_array.at(0) << 8) | voltage_array.at(1);
     106            0 : }
     107              : //-----------------------------------------------------------------------------
     108              : 
     109              : //-----------------------------------------------------------------------------
     110              : double
     111            0 : I2CSFPSlave::read_voltage() const
     112              : {
     113            0 :   auto voltage_raw = this->read_voltage_raw();
     114            0 :   auto temp_calib_pair = read_calibration_parameter_pair(0x3);
     115            0 :   return ((voltage_raw * temp_calib_pair.first) + temp_calib_pair.second) * 1e-4;
     116              : }
     117              : //-----------------------------------------------------------------------------
     118              : 
     119              : //-----------------------------------------------------------------------------
     120              : double
     121            0 : I2CSFPSlave::read_rx_power_raw() const
     122              : {
     123            0 :   ddm_available();
     124            0 :   auto rx_power_array = this->read_i2cArray(0x51, 0x68, 0x2);
     125            0 :   return (rx_power_array.at(0) << 8) | rx_power_array.at(1);
     126            0 : }
     127              : //-----------------------------------------------------------------------------
     128              : 
     129              : //-----------------------------------------------------------------------------
     130              : double
     131            0 : I2CSFPSlave::read_rx_ower() const
     132              : {
     133            0 :   auto rx_power_raw = this->read_rx_power_raw();
     134              :   // rx power calib constants, 5 4-byte parameters, IEEE 754 float encoding
     135            0 :   std::vector<uint32_t> rx_param_start_adr = { 0x48, 0x44, 0x40, 0x3C, 0x38 }; // NOLINT(build/unsigned)
     136            0 :   std::vector<double> rx_parameters;
     137            0 :   for (auto it = rx_param_start_adr.begin(); it != rx_param_start_adr.end(); ++it) {
     138            0 :     uint32_t parameter_bits = 0; // NOLINT(build/unsigned)
     139            0 :     auto parameter_array = this->read_i2cArray(0x51, *it, 0x4);
     140              : 
     141            0 :     for (auto jt = parameter_array.begin(); jt != parameter_array.end(); ++jt)
     142            0 :       parameter_bits = (parameter_bits << 8) | *jt;
     143              : 
     144              :     // convert the 32 bits to a float according IEEE 754
     145            0 :     double parameter = convert_bits_to_float(parameter_bits);
     146            0 :     rx_parameters.push_back(parameter);
     147            0 :   }
     148              : 
     149            0 :   double rx_power_calib = 0;
     150            0 :   for (uint32_t i = 0; i < rx_parameters.size(); ++i) { // NOLINT(build/unsigned)
     151            0 :     double parameter = rx_parameters.at(i);
     152            0 :     rx_power_calib = rx_power_calib + (parameter * pow(rx_power_raw, i));
     153              :   }
     154            0 :   return rx_power_calib * 0.1;
     155            0 : }
     156              : //-----------------------------------------------------------------------------
     157              : 
     158              : //-----------------------------------------------------------------------------
     159              : double
     160            0 : I2CSFPSlave::read_tx_power_raw() const
     161              : {
     162            0 :   ddm_available();
     163            0 :   auto tx_power_array = this->read_i2cArray(0x51, 0x66, 0x2);
     164            0 :   return (tx_power_array.at(0) << 8) | tx_power_array.at(1);
     165            0 : }
     166              : //-----------------------------------------------------------------------------
     167              : 
     168              : //-----------------------------------------------------------------------------
     169              : double
     170            0 : I2CSFPSlave::read_tx_power() const
     171              : {
     172            0 :   auto tx_power_raw = this->read_tx_power_raw();
     173            0 :   auto temp_calib_pair = read_calibration_parameter_pair(0x1);
     174            0 :   return ((tx_power_raw * temp_calib_pair.first) + temp_calib_pair.second) * 0.1;
     175              : }
     176              : //-----------------------------------------------------------------------------
     177              : 
     178              : //-----------------------------------------------------------------------------
     179              : double
     180            0 : I2CSFPSlave::read_current_raw() const
     181              : {
     182            0 :   ddm_available();
     183            0 :   auto current_array = this->read_i2cArray(0x51, 0x64, 0x2);
     184            0 :   return (current_array.at(0) << 8) | current_array.at(1);
     185            0 : }
     186              : //-----------------------------------------------------------------------------
     187              : 
     188              : //-----------------------------------------------------------------------------
     189              : double
     190            0 : I2CSFPSlave::read_current() const
     191              : {
     192            0 :   auto current_raw = this->read_current_raw();
     193            0 :   auto temp_calib_pair = read_calibration_parameter_pair(0x0);
     194            0 :   return ((current_raw * temp_calib_pair.first) + temp_calib_pair.second) * 0.002;
     195              : }
     196              : //-----------------------------------------------------------------------------
     197              : 
     198              : //-----------------------------------------------------------------------------
     199              : std::string
     200            0 : I2CSFPSlave::read_vendor_name() const
     201              : {
     202            0 :   sfp_reachable();
     203              : 
     204            0 :   std::stringstream vendor_name;
     205            0 :   auto vendor_name_characters = this->read_i2cArray(0x14, 0x10);
     206            0 :   for (auto it = vendor_name_characters.begin(); it != vendor_name_characters.end(); ++it) {
     207            0 :     vendor_name << *it;
     208              :   }
     209            0 :   return vendor_name.str();
     210            0 : }
     211              : //-----------------------------------------------------------------------------
     212              : 
     213              : //-----------------------------------------------------------------------------
     214              : std::string
     215            0 : I2CSFPSlave::read_vendor_part_number() const
     216              : {
     217            0 :   sfp_reachable();
     218              : 
     219            0 :   std::stringstream vendor_pn;
     220            0 :   auto vendor_pn_characters = this->read_i2cArray(0x28, 0x10);
     221            0 :   for (auto it = vendor_pn_characters.begin(); it != vendor_pn_characters.end(); ++it) {
     222            0 :     vendor_pn << *it;
     223              :   }
     224            0 :   return vendor_pn.str();
     225            0 : }
     226              : //-----------------------------------------------------------------------------
     227              : 
     228              : //-----------------------------------------------------------------------------
     229              : std::string
     230            0 : I2CSFPSlave::read_serial_number() const
     231              : {
     232            0 :   sfp_reachable();
     233              : 
     234            0 :   std::stringstream serial_number;
     235            0 :   auto serial_number_characters = this->read_i2cArray(0x44, 0x10);
     236            0 :   for (auto it = serial_number_characters.begin(); it != serial_number_characters.end(); ++it) {
     237            0 :     serial_number << *it;
     238              :   }
     239            0 :   return serial_number.str();
     240            0 : }
     241              : //-----------------------------------------------------------------------------
     242              : 
     243              : //-----------------------------------------------------------------------------
     244              : bool
     245            0 : I2CSFPSlave::read_ddm_support_bit() const
     246              : {
     247            0 :   sfp_reachable();
     248              : 
     249              :   // Bit 6 of reg 5C tells us whether the SFP supports digital diagnostic monitoring (DDM)
     250            0 :   auto ddm_info_byte = this->read_i2c(0x5C);
     251            0 :   return ddm_info_byte & 0x40;
     252              : }
     253              : //-----------------------------------------------------------------------------
     254              : 
     255              : //-----------------------------------------------------------------------------
     256              : bool
     257            0 : I2CSFPSlave::read_soft_tx_control_support_bit() const
     258              : {
     259            0 :   sfp_reachable();
     260              : 
     261              :   // Bit 6 of reg 5d tells us whether the soft tx control is implemented in this sfp
     262            0 :   auto enhanced_options_byte = this->read_i2c(0x5d);
     263            0 :   return enhanced_options_byte & 0x40;
     264              : }
     265              : //-----------------------------------------------------------------------------
     266              : 
     267              : //-----------------------------------------------------------------------------
     268              : bool
     269            0 : I2CSFPSlave::read_soft_tx_control_state() const
     270              : {
     271            0 :   ddm_available();
     272            0 :   auto opt_status_ctrl_byte = this->read_i2c(0x51, 0x6e);
     273              :   // Bit 6 tells us the state of the soft tx_disble register
     274            0 :   return opt_status_ctrl_byte & 0x40;
     275              : }
     276              : //-----------------------------------------------------------------------------
     277              : 
     278              : //-----------------------------------------------------------------------------
     279              : bool
     280            0 : I2CSFPSlave::read_tx_disable_pin_state() const
     281              : {
     282            0 :   ddm_available();
     283            0 :   auto opt_status_ctrl_byte = this->read_i2c(0x51, 0x6e);
     284              :   // Bit 7 tells us the state of tx_disble pin
     285            0 :   return opt_status_ctrl_byte & 0x80;
     286              : }
     287              : //-----------------------------------------------------------------------------
     288              : 
     289              : //-----------------------------------------------------------------------------
     290              : bool
     291            0 : I2CSFPSlave::read_i2c_reg_addressSwapBit() const
     292              : {
     293            0 :   sfp_reachable();
     294              : 
     295            0 :   auto ddm_info_byte = this->read_i2c(0x5C);
     296              :   // Bit 2 of byte 5C tells us whether special I2C address change operations are needed to access the DDM area
     297            0 :   return ddm_info_byte & 0x4;
     298              : }
     299              : //-----------------------------------------------------------------------------
     300              : 
     301              : //-----------------------------------------------------------------------------
     302              : void
     303            0 : I2CSFPSlave::switch_soft_tx_control_bit(bool turn_on) const
     304              : {
     305            0 :   ddm_available();
     306              : 
     307            0 :   if (!read_soft_tx_control_support_bit()) {
     308            0 :     throw SoftTxLaserControlUnsupported(ERS_HERE, get_master_id());
     309              :   }
     310              : 
     311              :   // Get optional status/control bits
     312            0 :   auto opt_status_ctrl_byte = this->read_i2c(0x51, 0x6e);
     313              : 
     314            0 :   uint8_t new_opt_status_ctrl_byte; // NOLINT(build/unsigned)
     315              :   // Bit 6 of byte 0x6e controls the soft tx_disable
     316            0 :   if (turn_on) {
     317            0 :     new_opt_status_ctrl_byte = opt_status_ctrl_byte & ~(1UL << 6);
     318              :   } else {
     319            0 :     new_opt_status_ctrl_byte = opt_status_ctrl_byte | (1UL << 6);
     320              :   }
     321            0 :   this->write_i2c(0x51, 0x6e, new_opt_status_ctrl_byte);
     322            0 : }
     323              : //-----------------------------------------------------------------------------
     324              : 
     325              : //-----------------------------------------------------------------------------
     326              : std::string
     327            0 : I2CSFPSlave::get_status(bool print_out) const
     328              : {
     329            0 :   sfp_reachable();
     330              : 
     331            0 :   std::stringstream status;
     332            0 :   std::vector<std::pair<std::string, std::string>> sfp_info;
     333              : 
     334              :   // Vendor name
     335            0 :   sfp_info.push_back(std::make_pair("Vendor", read_vendor_name()));
     336              : 
     337              :   // Vendor part number
     338            0 :   sfp_info.push_back(std::make_pair("Part number", read_vendor_part_number()));
     339              : 
     340              :   // Serial number
     341            0 :   sfp_info.push_back(std::make_pair("Serial number", read_serial_number()));
     342              : 
     343              :   // Does the SFP support DDM
     344            0 :   if (!read_ddm_support_bit()) {
     345            0 :     TLOG() << "DDM not available for SFP on I2C bus: " << get_master_id();
     346            0 :     status << format_reg_table(sfp_info, "SFP status", { "", "" });
     347            0 :     if (print_out)
     348            0 :       TLOG() << status.str();
     349            0 :     return status.str();
     350              :   } else {
     351            0 :     if (read_i2c_reg_addressSwapBit()) {
     352            0 :       TLOG() << "SFP DDM I2C address swap not supported. SFP on I2C bus: " << get_master_id();
     353            0 :       status << format_reg_table(sfp_info, "SFP status", { "", "" });
     354            0 :       if (print_out)
     355            0 :         TLOG() << status.str();
     356            0 :       return status.str();
     357              :     }
     358              :   }
     359              : 
     360            0 :   std::stringstream temperature_stream;
     361            0 :   temperature_stream << std::dec << std::fixed << std::setprecision(2) << read_temperature() << " C";
     362            0 :   sfp_info.push_back(std::make_pair("Temperature", temperature_stream.str()));
     363              : 
     364            0 :   std::stringstream voltage_stream;
     365            0 :   voltage_stream << std::dec << std::fixed << std::setprecision(2) << read_voltage() << " V";
     366            0 :   sfp_info.push_back(std::make_pair("Supply voltage", voltage_stream.str()));
     367              : 
     368            0 :   std::stringstream rx_power_stream;
     369            0 :   rx_power_stream << std::dec << std::fixed << std::setprecision(2) << read_rx_ower() << " uW";
     370            0 :   sfp_info.push_back(std::make_pair("Rx power", rx_power_stream.str()));
     371              : 
     372            0 :   std::stringstream tx_power_stream;
     373            0 :   tx_power_stream << std::dec << std::fixed << std::setprecision(2) << read_tx_power() << " uW";
     374            0 :   sfp_info.push_back(std::make_pair("Tx power", tx_power_stream.str()));
     375              : 
     376            0 :   std::stringstream current_stream;
     377            0 :   current_stream << std::dec << std::fixed << std::setprecision(2) << read_current() << " uA";
     378            0 :   sfp_info.push_back(std::make_pair("Tx current", current_stream.str()));
     379              : 
     380            0 :   if (read_soft_tx_control_support_bit()) {
     381              :     // sfp_info.push_back(std::make_pair("Soft Tx disbale supported",  "True"));
     382            0 :     sfp_info.push_back(std::make_pair("Tx disable bit", std::to_string(read_soft_tx_control_state())));
     383              :   } else {
     384            0 :     sfp_info.push_back(std::make_pair("Soft Tx disbale supported", "False"));
     385              :   }
     386              : 
     387            0 :   sfp_info.push_back(std::make_pair("Tx disable pin", std::to_string(read_tx_disable_pin_state())));
     388              : 
     389            0 :   status << format_reg_table(sfp_info, "SFP status", { "", "" });
     390            0 :   if (print_out)
     391            0 :     TLOG() << status.str();
     392            0 :   return status.str();
     393            0 : }
     394              : //-----------------------------------------------------------------------------
     395              : 
     396              : //-----------------------------------------------------------------------------
     397              : // void
     398              : // I2CSFPSlave::get_info(timinghardwareinfo::TimingSFPMonitorData& mon_data) const
     399              : // {
     400              : //   mon_data.data_valid = false;
     401              : 
     402              : //   sfp_reachable();
     403              : 
     404              : //   // Vendor name
     405              : //   mon_data.vendor_name = this->read_vendor_name();
     406              : 
     407              : //   // Vendor part number
     408              : //   mon_data.vendor_pn = this->read_vendor_part_number();
     409              : 
     410              : //   // Serial number TP DO?
     411              : //   // sfp_info.push_back(std::make_pair("Serial number", read_serial_number()));
     412              : 
     413              : //   // Does the SFP support DDM
     414              : //   if (!this->read_ddm_support_bit()) {
     415              : //     TLOG() << "DDM not available for SFP on I2C bus: " << get_master_id();
     416              : //     mon_data.ddm_supported = false;
     417              : //     return;
     418              : //   } else {
     419              : //     mon_data.ddm_supported = true;
     420              : //     if (this->read_i2c_reg_addressSwapBit()) {
     421              : //       TLOG() << "SFP DDM I2C address swap not supported. SFP on I2C bus: " << get_master_id();
     422              : //       return;
     423              : //     }
     424              : //   }
     425              : 
     426              : //   mon_data.temperature = this->read_temperature();
     427              : 
     428              : //   mon_data.supply_voltage = this->read_voltage();
     429              : 
     430              : //   mon_data.rx_power = this->read_rx_ower();
     431              : 
     432              : //   mon_data.tx_power = this->read_tx_power();
     433              : 
     434              : //   mon_data.laser_current = this->read_current();
     435              : 
     436              : //   mon_data.tx_disable_sw_supported = this->read_soft_tx_control_support_bit();
     437              : 
     438              : //   mon_data.tx_disable_sw = this->read_soft_tx_control_state();
     439              : 
     440              : //   mon_data.tx_disable_hw = this->read_tx_disable_pin_state();
     441              : 
     442              : //   mon_data.data_valid = true;
     443              : // }
     444              : 
     445              : // void
     446              : // I2CSFPSlave::get_info(opmonlib::InfoCollector& ci, int /*level*/) const
     447              : // {
     448              : //   timinghardwareinfo::TimingSFPMonitorData sfp_mon_data;
     449              : //   get_info(sfp_mon_data);
     450              : //   ci.add(sfp_mon_data);
     451              : // }
     452              : //-----------------------------------------------------------------------------
     453              : 
     454              : // uHAL Node registation
     455            0 : UHAL_REGISTER_DERIVED_NODE(I2CSFPNode)
     456              : 
     457              : //-----------------------------------------------------------------------------
     458            0 : I2CSFPNode::I2CSFPNode(const uhal::Node& node)
     459              :   : I2CMasterNode(node)
     460            0 :   , I2CSFPSlave(this, this->get_slave_address("SFP_EEProm"))
     461            0 : {}
     462              : //-----------------------------------------------------------------------------
     463              : 
     464              : //-----------------------------------------------------------------------------
     465            0 : I2CSFPNode::I2CSFPNode(const I2CSFPNode& node)
     466              :   : I2CMasterNode(node)
     467            0 :   , I2CSFPSlave(this, this->get_slave_address("SFP_EEProm"))
     468            0 : {}
     469              : //-----------------------------------------------------------------------------
     470              : 
     471              : //-----------------------------------------------------------------------------
     472            0 : I2CSFPNode::~I2CSFPNode() {}
     473              : //-----------------------------------------------------------------------------
     474              : 
     475              : } // namespace timing
     476              : } // namespace dunedaq
        

Generated by: LCOV version 2.0-1