LCOV - code coverage report
Current view: top level - timing/src - I2CMasterNode.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 0.0 % 172 0
Test Date: 2026-02-16 10:18:04 Functions: 0.0 % 25 0

            Line data    Source code
       1              : /**
       2              :  * @file I2CMasterNode.cpp
       3              :  *
       4              :  * File:   OpenCoresI2CMasterNode.cpp
       5              :  * Author: ale
       6              :  *
       7              :  * Created on August 29, 2014, 4:47 PM
       8              :  *
       9              :  * This is part of the DUNE DAQ Software Suite, copyright 2020.
      10              :  * Licensing/copyright details are in the COPYING file that you should have
      11              :  * received with this code.
      12              :  */
      13              : 
      14              : #include "timing/I2CMasterNode.hpp"
      15              : 
      16              : #include "ers/ers.hpp"
      17              : #include "timing/I2CSlave.hpp"
      18              : #include "timing/TimingIssues.hpp"
      19              : #include "timing/toolbox.hpp"
      20              : 
      21              : #include <boost/lexical_cast.hpp>
      22              : #include <boost/range/adaptor/map.hpp>
      23              : #include <boost/range/algorithm/copy.hpp>
      24              : 
      25              : #include <algorithm>
      26              : #include <string>
      27              : #include <unordered_map>
      28              : #include <utility>
      29              : #include <vector>
      30              : 
      31              : namespace dunedaq {
      32              : namespace timing {
      33              : 
      34            0 : UHAL_REGISTER_DERIVED_NODE(I2CMasterNode)
      35              : 
      36              : // PRIVATE CONST definitions
      37              : const std::string I2CMasterNode::kPreHiNode = "ps_hi";
      38              : const std::string I2CMasterNode::kPreLoNode = "ps_lo";
      39              : const std::string I2CMasterNode::kCtrlNode = "ctrl";
      40              : const std::string I2CMasterNode::kTxNode = "data";
      41              : const std::string I2CMasterNode::kRxNode = "data";
      42              : const std::string I2CMasterNode::kCmdNode = "cmd_stat";
      43              : const std::string I2CMasterNode::kStatusNode = "cmd_stat";
      44              : 
      45              : const uint8_t I2CMasterNode::kStartCmd = 0x80;         // 1 << 7 // NOLINT(build/unsigned)
      46              : const uint8_t I2CMasterNode::kStopCmd = 0x40;          // 1 << 6 // NOLINT(build/unsigned)
      47              : const uint8_t I2CMasterNode::kReadFromSlaveCmd = 0x20; // 1 << 5 // NOLINT(build/unsigned)
      48              : const uint8_t I2CMasterNode::kWriteToSlaveCmd = 0x10;  // 1 << 4 // NOLINT(build/unsigned)
      49              : const uint8_t I2CMasterNode::kAckCmd = 0x08;           // 1 << 3 // NOLINT(build/unsigned)
      50              : const uint8_t I2CMasterNode::kInterruptAck = 0x01;     // 1      // NOLINT(build/unsigned)
      51              : 
      52              : const uint8_t I2CMasterNode::kReceivedAckBit = 0x80;     // recvdack = 0x1 << 7   // NOLINT(build/unsigned)
      53              : const uint8_t I2CMasterNode::kBusyBit = 0x40;            // busy = 0x1 << 6       // NOLINT(build/unsigned)
      54              : const uint8_t I2CMasterNode::kArbitrationLostBit = 0x20; // arblost = 0x1 << 5    // NOLINT(build/unsigned)
      55              : const uint8_t I2CMasterNode::kInProgressBit = 0x2;       // inprogress = 0x1 << 1 // NOLINT(build/unsigned)
      56              : const uint8_t I2CMasterNode::kInterruptBit = 0x1;        // interrupt = 0x1       // NOLINT(build/unsigned)
      57              : 
      58              : //-----------------------------------------------------------------------------
      59            0 : I2CMasterNode::I2CMasterNode(const uhal::Node& node)
      60            0 :   : uhal::Node(node)
      61              : {
      62            0 :   constructor();
      63            0 : }
      64              : //-----------------------------------------------------------------------------
      65              : 
      66              : //-----------------------------------------------------------------------------
      67            0 : I2CMasterNode::I2CMasterNode(const I2CMasterNode& node)
      68            0 :   : uhal::Node(node)
      69              : {
      70            0 :   constructor();
      71            0 : }
      72              : //-----------------------------------------------------------------------------
      73              : 
      74              : //-----------------------------------------------------------------------------
      75              : void
      76            0 : I2CMasterNode::constructor()
      77              : {
      78              :   // 16 bit clock prescale factor.
      79              :   // formula: m_clockPrescale = (input_frequency / 5 / desired_frequency) -1
      80              :   // for typical IPbus applications: input frequency = IPbus clock = 31.x MHz
      81              :   // target frequency 100 kHz to play it safe (first revision of i2c standard),
      82            0 :   m_clock_prescale = 0x40;
      83              :   // m_clock_prescale = 0x100;
      84              : 
      85              :   // Build the list of slaves
      86              :   // Loop over node parameters. Each parameter becomes a slave node.
      87            0 :   const std::unordered_map<std::string, std::string>& parameters = this->getParameters();
      88            0 :   std::unordered_map<std::string, std::string>::const_iterator it;
      89            0 :   for (it = parameters.begin(); it != parameters.end(); ++it) {
      90            0 :     uint32_t slave_addr = (boost::lexical_cast<timing::stoul<uint32_t>>(it->second) & 0x7f); // NOLINT(build/unsigned)
      91            0 :     m_i2c_device_addresses.insert(std::make_pair(it->first, slave_addr));
      92            0 :     m_i2c_devices.insert(std::make_pair(it->first, new I2CSlave(this, slave_addr)));
      93              :   }
      94            0 : }
      95              : //-----------------------------------------------------------------------------
      96              : 
      97              : //-----------------------------------------------------------------------------
      98            0 : I2CMasterNode::~I2CMasterNode()
      99              : {
     100            0 :   std::unordered_map<std::string, I2CSlave*>::iterator it;
     101            0 :   for (it = m_i2c_devices.begin(); it != m_i2c_devices.end(); ++it) {
     102              :     // Delete slaves
     103            0 :     delete it->second; // NOLINT
     104              :   }
     105            0 : }
     106              : //-----------------------------------------------------------------------------
     107              : 
     108              : //-----------------------------------------------------------------------------
     109              : std::vector<std::string>
     110            0 : I2CMasterNode::get_slaves() const
     111              : {
     112            0 :   std::vector<std::string> slaves;
     113              : 
     114            0 :   boost::copy(m_i2c_device_addresses | boost::adaptors::map_keys, std::back_inserter(slaves));
     115            0 :   return slaves;
     116            0 : }
     117              : //-----------------------------------------------------------------------------
     118              : 
     119              : //-----------------------------------------------------------------------------
     120              : uint8_t // NOLINT(build/unsigned)
     121            0 : I2CMasterNode::get_slave_address(const std::string& name) const
     122              : {
     123            0 :   std::unordered_map<std::string, uint8_t>::const_iterator it = // NOLINT(build/unsigned)
     124            0 :     m_i2c_device_addresses.find(name);
     125            0 :   if (it == m_i2c_device_addresses.end()) {
     126            0 :     throw I2CDeviceNotFound(ERS_HERE, getId(), name);
     127              :   }
     128            0 :   return it->second;
     129              : }
     130              : //-----------------------------------------------------------------------------
     131              : 
     132              : //-----------------------------------------------------------------------------
     133              : const I2CSlave&
     134            0 : I2CMasterNode::get_slave(const std::string& name) const
     135              : {
     136            0 :   std::unordered_map<std::string, I2CSlave*>::const_iterator it = m_i2c_devices.find(name);
     137            0 :   if (it == m_i2c_devices.end()) {
     138            0 :     throw I2CDeviceNotFound(ERS_HERE, getId(), name);
     139              :   }
     140            0 :   return *(it->second);
     141              : }
     142              : //-----------------------------------------------------------------------------
     143              : 
     144              : //-----------------------------------------------------------------------------
     145              : uint8_t                                                                             // NOLINT(build/unsigned)
     146            0 : I2CMasterNode::read_i2c(uint8_t i2c_device_address, uint32_t i2c_reg_address, bool atomic) const // NOLINT(build/unsigned)
     147              : {
     148              :   // // write one word containing the address
     149              :   // std::vector<uint8_t> array(1, i2c_reg_address & 0x7f);
     150              :   // this->write_block_i2c(i2c_device_address, array);
     151              :   // // request the content at the specific address
     152              :   // return this->read_block_i2c(i2c_device_address, 1) [0];
     153            0 :   return this->read_i2cArray(i2c_device_address, i2c_reg_address, 1, atomic)[0];
     154              : }
     155              : //-----------------------------------------------------------------------------
     156              : 
     157              : //-----------------------------------------------------------------------------
     158              : void
     159            0 : I2CMasterNode::write_i2c(uint8_t i2c_device_address, // NOLINT(build/unsigned)
     160              :                          uint32_t i2c_reg_address,   // NOLINT(build/unsigned)
     161              :                          uint8_t data,               // NOLINT(build/unsigned)
     162              :                          bool send_stop) const
     163              : {
     164              :   // std::vector<uint8_t> block(2);
     165              :   // block[0] = (i2c_reg_address & 0xff);
     166              :   // block[1] = (data & 0xff);
     167              :   // this->write_block_i2c(i2c_device_address, block);
     168              : 
     169            0 :   this->write_i2cArray(i2c_device_address, i2c_reg_address, { data }, send_stop);
     170            0 : }
     171              : //-----------------------------------------------------------------------------
     172              : 
     173              : //-----------------------------------------------------------------------------
     174              : std::vector<uint8_t>                                         // NOLINT(build/unsigned)
     175            0 : I2CMasterNode::read_i2cArray(uint8_t i2c_device_address,     // NOLINT(build/unsigned)
     176              :                              uint32_t i2c_reg_address,       // NOLINT(build/unsigned)
     177              :                              uint32_t number_of_words,       // NOLINT(build/unsigned)
     178              :                              bool atomic) const              // NOLINT(build/unsigned)
     179              : {
     180              :   // write one word containing the address
     181            0 :   std::vector<uint8_t> lArray{ (uint8_t)(i2c_reg_address & 0xff) }; // NOLINT(build/unsigned)
     182              : 
     183              :   // do not send stop if read transaction is atomic
     184            0 :   this->write_block_i2c(i2c_device_address, lArray, !atomic);
     185              : 
     186              :   // request the content at the specific address, 
     187              :   // and do not reset bus at begining of transaction (atomic)
     188            0 :   return this->read_block_i2c(i2c_device_address, number_of_words, !atomic);
     189            0 : }
     190              : //-----------------------------------------------------------------------------
     191              : 
     192              : //-----------------------------------------------------------------------------
     193              : void
     194            0 : I2CMasterNode::write_i2cArray(uint8_t i2c_device_address, // NOLINT(build/unsigned)
     195              :                               uint32_t i2c_reg_address,   // NOLINT(build/unsigned)
     196              :                               std::vector<uint8_t> data,  // NOLINT(build/unsigned)
     197              :                               bool send_stop) const
     198              : {
     199              :   // std::cout << "Writing " << data.size() << " from " << std::showbase <<  std::hex << i2c_reg_address << " on " <<
     200              :   // (uint32_t)i2c_device_address << std::endl; // HACK
     201            0 :   std::vector<uint8_t> block(data.size() + 1); // NOLINT(build/unsigned)
     202            0 :   block[0] = (i2c_reg_address & 0xff);
     203              : 
     204            0 :   for (size_t i(0); i < data.size(); ++i)
     205            0 :     block[i + 1] = data[i];
     206              : 
     207            0 :   this->write_block_i2c(i2c_device_address, block, send_stop);
     208            0 : }
     209              : //-----------------------------------------------------------------------------
     210              : 
     211              : //-----------------------------------------------------------------------------
     212              : std::vector<uint8_t>                                                                         // NOLINT(build/unsigned)
     213            0 : I2CMasterNode::read_i2cPrimitive(uint8_t i2c_device_address, uint32_t number_of_bytes) const // NOLINT(build/unsigned)
     214              : {
     215            0 :   return this->read_block_i2c(i2c_device_address, number_of_bytes);
     216              : }
     217              : //-----------------------------------------------------------------------------
     218              : 
     219              : //-----------------------------------------------------------------------------
     220              : void
     221            0 : I2CMasterNode::write_i2cPrimitive(uint8_t i2c_device_address,       // NOLINT(build/unsigned)
     222              :                                   const std::vector<uint8_t>& data, // NOLINT(build/unsigned)
     223              :                                   bool send_stop) const
     224              : {
     225            0 :   this->write_block_i2c(i2c_device_address, data, send_stop);
     226            0 : }
     227              : //-----------------------------------------------------------------------------
     228              : 
     229              : //-----------------------------------------------------------------------------
     230              : void
     231            0 : I2CMasterNode::write_block_i2c(uint8_t i2c_device_address,         // NOLINT(build/unsigned)
     232              :                                const std::vector<uint8_t>& data, // NOLINT(build/unsigned)
     233              :                                bool send_stop) const
     234              : {
     235              :   // transmit reg definitions
     236              :   // bits 7-1: 7-bit slave address during address transfer
     237              :   //           or first 7 bits of byte during data transfer
     238              :   // bit 0: RW flag during address transfer or LSB during data transfer.
     239              :   // '1' = reading from slave
     240              :   // '0' = writing to slave
     241              :   // command reg definitions
     242              :   // bit 7: Generate start condition
     243              :   // bit 6: Generate stop condition
     244              :   // bit 5: Read from slave
     245              :   // bit 4: Write to slave
     246              :   // bit 3: 0 when acknowledgement is received
     247              :   // bit 2:1: Reserved
     248              :   // bit 0: Interrupt acknowledge. When set, clears a pending interrupt
     249              : 
     250              :   // Reset bus before beginning
     251            0 :   reset();
     252              : 
     253              :   // Open the connection and send the slave address, bit 0 set to zero
     254            0 :   send_i2c_command_and_write_data(kStartCmd, (i2c_device_address << 1) & 0xfe);
     255              : 
     256            0 :   for (unsigned ibyte = 0; ibyte < data.size(); ibyte++) {
     257              : 
     258              :     // Send stop if last element of the array (and not vetoed)
     259            0 :     uint8_t cmd = (((ibyte == data.size() - 1) && send_stop) ? kStopCmd : 0x0); // NOLINT(build/unsigned)
     260              : 
     261              :     // Push the byte on the bus
     262            0 :     send_i2c_command_and_write_data(cmd, data[ibyte]);
     263              :   }
     264            0 : }
     265              : //-----------------------------------------------------------------------------
     266              : 
     267              : //-----------------------------------------------------------------------------
     268              : std::vector<uint8_t>                                                                // NOLINT(build/unsigned)
     269            0 : I2CMasterNode::read_block_i2c(uint8_t i2c_device_address, uint32_t number_of_bytes, bool send_reset) const // NOLINT(build/unsigned)
     270              : {
     271              :   // transmit reg definitions
     272              :   // bits 7-1: 7-bit slave address during address transfer
     273              :   //           or first 7 bits of byte during data transfer
     274              :   // bit 0: RW flag during address transfer or LSB during data transfer.
     275              :   //        '1' = reading from slave
     276              :   //        '0' = writing to slave
     277              :   // command reg definitions
     278              :   // bit 7:   Generate start condition
     279              :   // bit 6:   Generate stop condition
     280              :   // bit 5:   Read from slave
     281              :   // bit 4:   Write to slave
     282              :   // bit 3:   0 when acknowledgement is received
     283              :   // bit 2:1: Reserved
     284              :   // bit 0:   Interrupt acknowledge. When set, clears a pending interrupt
     285              : 
     286              :   // Reset bus before beginning
     287            0 :   if (send_reset)
     288              :   {
     289            0 :     reset();
     290              :   }
     291              : 
     292              :   // Open the connection & send the target i2c address. Bit 0 set to 1 (read)
     293            0 :   send_i2c_command_and_write_data(kStartCmd, (i2c_device_address << 1) | 0x01);
     294              : 
     295            0 :   std::vector<uint8_t> lArray; // NOLINT(build/unsigned)
     296            0 :   for (unsigned ibyte = 0; ibyte < number_of_bytes; ibyte++) {
     297              : 
     298            0 :     uint8_t cmd = ((ibyte == number_of_bytes - 1) ? (kStopCmd | kAckCmd) : 0x0); // NOLINT(build/unsigned)
     299              : 
     300              :     // Push the cmd on the bus, retrieve the result and put it in the arrary
     301            0 :     lArray.push_back(send_i2c_command_and_read_data(cmd));
     302              :   }
     303            0 :   return lArray;
     304            0 : }
     305              : //-----------------------------------------------------------------------------
     306              : 
     307              : //-----------------------------------------------------------------------------
     308              : bool
     309            0 : I2CMasterNode::ping(uint8_t i2c_device_address, bool throw_excp) const // NOLINT(build/unsigned)
     310              : {
     311              :   // Reset bus before beginning
     312            0 :   reset();
     313              : 
     314            0 :   try {
     315            0 :     send_i2c_command_and_write_data(kStartCmd, (i2c_device_address << 1) | 0x01);
     316            0 :     send_i2c_command_and_read_data(kStopCmd | kAckCmd);
     317              :     return true;
     318            0 :   } catch (const timing::I2CException& excp) {
     319            0 :     if (throw_excp) {
     320            0 :       throw excp;
     321              :     }
     322            0 :     return false;
     323            0 :   }
     324              : }
     325              : //-----------------------------------------------------------------------------
     326              : 
     327              : //-----------------------------------------------------------------------------
     328              : std::vector<uint8_t> // NOLINT(build/unsigned)
     329            0 : I2CMasterNode::scan() const
     330              : {
     331              : 
     332            0 :   std::vector<uint8_t> address_vector; // NOLINT(build/unsigned)
     333              : 
     334              :   // Reset bus before beginning
     335            0 :   reset();
     336              : 
     337            0 :   for (uint8_t iaddr(0); iaddr < 0x7f; ++iaddr) { // NOLINT(build/unsigned)
     338              :     // Open the connection & send the target i2c address. Bit 0 set to 1 (read)
     339            0 :     try {
     340            0 :       send_i2c_command_and_write_data(kStartCmd, (iaddr << 1) | 0x01);
     341            0 :       send_i2c_command_and_read_data(kStopCmd | kAckCmd);
     342            0 :     } catch (const timing::I2CException& excp) {
     343              :       // TIMING_LOG(kError) << std::showbase << std::hex << (uint32_t)iaddr << "  " << excp.what();
     344            0 :       continue;
     345            0 :     }
     346            0 :     address_vector.push_back(iaddr);
     347              :     // TIMING_LOG(kNotice) << std::showbase << std::hex << (uint32_t)iaddr << " found";
     348              :   }
     349              : 
     350            0 :   return address_vector;
     351            0 : }
     352              : //-----------------------------------------------------------------------------
     353              : 
     354              : //-----------------------------------------------------------------------------
     355              : void
     356            0 : I2CMasterNode::reset() const
     357              : {
     358              :   // Resets the I2C bus
     359              :   //
     360              :   // This function does the following:
     361              :   //        1) Disables the I2C core
     362              :   //        2) Sets the clock prescale registers
     363              :   //        3) Enables the I2C core
     364              :   //        4) Sets all writable bus-master registers to default values
     365              : 
     366            0 :   auto ctrl = getNode(kCtrlNode).read();
     367            0 :   auto pre_hi = getNode(kPreHiNode).read();
     368            0 :   auto pre_lo = getNode(kPreLoNode).read();
     369            0 :   getClient().dispatch();
     370              : 
     371            0 :   bool full_reset(false);
     372              : 
     373            0 :   full_reset = (m_clock_prescale != (pre_hi << 8) + pre_lo);
     374              : 
     375            0 :   if (full_reset) {
     376              :     // disable the I2C core
     377            0 :     getNode(kCtrlNode).write(0x00);
     378            0 :     getClient().dispatch();
     379              :     // set the clock prescale
     380            0 :     getNode(kPreHiNode).write((m_clock_prescale & 0xff00) >> 8);
     381              :     // getClient().dispatch();
     382            0 :     getNode(kPreLoNode).write(m_clock_prescale & 0xff);
     383              :     // getClient().dispatch();
     384              :     // set all writable bus-master registers to default values
     385            0 :     getNode(kTxNode).write(0x00);
     386            0 :     getNode(kCmdNode).write(0x00);
     387            0 :     getClient().dispatch();
     388              : 
     389              :     // enable the I2C core
     390            0 :     getNode(kCtrlNode).write(0x80);
     391            0 :     getClient().dispatch();
     392              :   } else {
     393              :     // set all writable bus-master registers to default values
     394            0 :     getNode(kTxNode).write(0x00);
     395            0 :     getNode(kCmdNode).write(0x00);
     396            0 :     getClient().dispatch();
     397              :   }
     398            0 : }
     399              : //-----------------------------------------------------------------------------
     400              : 
     401              : //-----------------------------------------------------------------------------
     402              : uint8_t                                                              // NOLINT(build/unsigned)
     403            0 : I2CMasterNode::send_i2c_command_and_read_data(uint8_t command) const // NOLINT(build/unsigned)
     404              : {
     405              : 
     406            0 :   assert(!(command & kWriteToSlaveCmd));
     407              : 
     408            0 :   uint8_t full_cmd = command | kReadFromSlaveCmd;                                     // NOLINT(build/unsigned)
     409            0 :   TLOG_DEBUG(10) << ">> sending read cmd  = " << format_reg_value((uint32_t)full_cmd); // NOLINT(build/unsigned)
     410              : 
     411              :   // Force the read bit high and set them cmd bits
     412            0 :   getNode(kCmdNode).write(full_cmd);
     413            0 :   getClient().dispatch();
     414              : 
     415              :   // Wait for transaction to finish. Require idle bus at the end if stop bit is high)
     416            0 :   wait_until_finished(/*req ack*/ false, command & kStopCmd);
     417              : 
     418              :   // Pull the data out of the rx register.
     419            0 :   uhal::ValWord<uint32_t> result = getNode(kRxNode).read(); // NOLINT(build/unsigned)
     420            0 :   getClient().dispatch();
     421              : 
     422            0 :   TLOG_DEBUG(10) << "<< receive data      = " << format_reg_value((uint32_t)result); // NOLINT(build/unsigned)v
     423              : 
     424            0 :   return (result & 0xff);
     425            0 : }
     426              : //-----------------------------------------------------------------------------
     427              : 
     428              : //-----------------------------------------------------------------------------
     429              : void
     430            0 : I2CMasterNode::send_i2c_command_and_write_data(uint8_t command, uint8_t data) const // NOLINT(build/unsigned)
     431              : {
     432              : 
     433              :   //
     434            0 :   assert(!(command & kReadFromSlaveCmd));
     435              : 
     436            0 :   uint8_t full_cmd = command | kWriteToSlaveCmd; // NOLINT(build/unsigned)
     437            0 :   std::stringstream debug_stream;
     438            0 :   debug_stream << ">> sending write cmd = " << std::showbase << std::hex << (uint32_t)full_cmd // NOLINT(build/unsigned)
     439            0 :                << " data = " << std::showbase << std::hex << (uint32_t)data;                   // NOLINT(build/unsigned)
     440            0 :   TLOG_DEBUG(10) << debug_stream.str();
     441              : 
     442              :   // write the payload
     443            0 :   getNode(kTxNode).write(data);
     444            0 :   getClient().dispatch();
     445              : 
     446              :   // Force the write bit high and set them cmd bits
     447            0 :   getNode(kCmdNode).write(full_cmd);
     448              : 
     449              :   // Run the commands and wait for transaction to finish
     450            0 :   getClient().dispatch();
     451              : 
     452              :   // Wait for transaction to finish. Require idle bus at the end if stop bit is high
     453              :   // wait_until_finished(req_hack, requ_idle)
     454            0 :   wait_until_finished( true, command & kStopCmd); // NOLINT
     455            0 : }
     456              : //-----------------------------------------------------------------------------
     457              : 
     458              : //-----------------------------------------------------------------------------
     459              : void
     460            0 : I2CMasterNode::wait_until_finished(bool require_acknowledgement, bool require_bus_idle_at_end) const
     461              : {
     462              :   // Ensures the current bus transaction has finished successfully
     463              :   // before allowing further I2C bus transactions
     464              :   // This method monitors the status register
     465              :   // and will not allow execution to continue until the
     466              :   // I2C bus has completed properly.  It will throw an exception
     467              :   // if it picks up bus problems or a bus timeout occurs.
     468            0 :   const unsigned max_retry = 20;
     469            0 :   unsigned attempt = 1;
     470            0 :   bool received_acknowledge, busy;
     471              : 
     472            0 :   const uhal::Node& status_node = getNode(kStatusNode);
     473              : 
     474            0 :   while (attempt <= max_retry) {
     475            0 :     usleep(10);
     476              :     // Get the status
     477            0 :     uhal::ValWord<uint32_t> i2c_status = status_node.read(); // NOLINT(build/unsigned)
     478            0 :     getClient().dispatch();
     479              : 
     480            0 :     received_acknowledge = !(i2c_status & kReceivedAckBit);
     481            0 :     busy = (i2c_status & kBusyBit);
     482            0 :     bool arbitration_lost = (i2c_status & kArbitrationLostBit);
     483            0 :     bool transfer_in_progress = (i2c_status & kInProgressBit);
     484              : 
     485            0 :     if (arbitration_lost) {
     486              :       // This is an instant error at any time
     487            0 :       throw I2CBusArbitrationLost(ERS_HERE, getId());
     488              :     }
     489              : 
     490            0 :     if (!transfer_in_progress) {
     491              :       // The transfer looks to have completed successfully,
     492              :       // pending further checks
     493              :       break;
     494              :     }
     495              : 
     496            0 :     attempt += 1;
     497            0 :   }
     498              : 
     499              :   // At this point, we've either had too many retries, or the
     500              :   // Transfer in Progress (TIP) bit went low.  If the TIP bit
     501              :   // did go low, then we do a couple of other checks to see if
     502              :   // the bus operated as expected:
     503              : 
     504            0 :   if (attempt > max_retry) {
     505            0 :     throw I2CTransactionTimeout(ERS_HERE, getId());
     506              :   }
     507              : 
     508            0 :   if (require_acknowledgement && !received_acknowledge) {
     509            0 :     throw I2CNoAcknowledgeReceived(ERS_HERE, getId());
     510              :   }
     511              : 
     512            0 :   if (require_bus_idle_at_end && busy) {
     513            0 :     throw I2CTransferFinishedBusStillBusy(ERS_HERE, getId());
     514              :   }
     515            0 : }
     516              : 
     517              : } // namespace timing
     518              : } // namespace dunedaq
        

Generated by: LCOV version 2.0-1