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
|