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
|