μHAL (v2.7.9)
Part of the IPbus software repository
ProtocolPCIe.cpp
Go to the documentation of this file.
1 /*
2 ---------------------------------------------------------------------------
3 
4  This file is part of uHAL.
5 
6  uHAL is a hardware access library and programming framework
7  originally developed for upgrades of the Level-1 trigger of the CMS
8  experiment at CERN.
9 
10  uHAL is free software: you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation, either version 3 of the License, or
13  (at your option) any later version.
14 
15  uHAL is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU General Public License for more details.
19 
20  You should have received a copy of the GNU General Public License
21  along with uHAL. If not, see <http://www.gnu.org/licenses/>.
22 
23 
24  Andrew Rose, Imperial College, London
25  email: awr01 <AT> imperial.ac.uk
26 
27  Marc Magrans de Abril, CERN
28  email: marc.magrans.de.abril <AT> cern.ch
29 
30  Tom Williams, Rutherford Appleton Laboratory, Oxfordshire
31  email: tom.williams <AT> cern.ch
32 
33 ---------------------------------------------------------------------------
34 */
35 
42 #include "uhal/ProtocolPCIe.hpp"
43 
44 
45 #include <algorithm> // for min
46 #include <assert.h>
47 #include <cstdlib>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <iomanip> // for operator<<
51 #include <iostream> // for operator<<
52 #include <sys/file.h>
53 #include <sys/stat.h>
54 #include <stdlib.h> // for size_t, free
55 #include <stdio.h>
56 #include <string.h> // for memcpy
57 #include <unistd.h>
58 
59 #include <boost/chrono/duration.hpp> // for operator>
60 #include <boost/chrono/time_point.hpp> // for operator-
61 #include <boost/lexical_cast.hpp>
62 #include <boost/thread/thread.hpp> // for sleep_for
63 #include <boost/date_time/posix_time/posix_time_types.hpp> // for time_dura...
64 #include <boost/interprocess/sync/scoped_lock.hpp>
65 
66 #include "uhal/grammars/URI.hpp" // for URI
67 #include "uhal/log/LogLevels.hpp" // for BaseLogLevel
68 #include "uhal/log/log_inserters.integer.hpp" // for Integer
69 #include "uhal/log/log_inserters.quote.hpp" // for Quote
70 #include "uhal/log/log.hpp"
71 #include "uhal/Buffers.hpp"
72 #include "uhal/ClientFactory.hpp"
73 
74 
75 namespace uhal {
76 
77 
78 PCIe::PacketFmt::PacketFmt(const uint8_t* const aPtr, const size_t aNrBytes) :
79  mData(1, std::pair<const uint8_t*, size_t>(aPtr, aNrBytes))
80 {}
81 
82 
83 PCIe::PacketFmt::PacketFmt(const std::vector< std::pair<const uint8_t*, size_t> >& aData) :
84  mData(aData)
85 {}
86 
87 
89 {}
90 
91 
92 std::ostream& operator<<(std::ostream& aStream, const PCIe::PacketFmt& aPacket)
93 {
94  std::ios::fmtflags lOrigFlags( aStream.flags() );
95 
96  size_t lNrBytesWritten = 0;
97  for (size_t i = 0; i < aPacket.mData.size(); i++) {
98  for (const uint8_t* lPtr = aPacket.mData.at(i).first; lPtr != (aPacket.mData.at(i).first + aPacket.mData.at(i).second); lPtr++, lNrBytesWritten++) {
99  if ((lNrBytesWritten & 3) == 0)
100  aStream << std::endl << " @ " << std::setw(3) << std::dec << (lNrBytesWritten >> 2) << " : x";
101  aStream << std::setw(2) << std::hex << uint16_t(*lPtr) << " ";
102  }
103  }
104 
105  aStream.flags( lOrigFlags );
106  return aStream;
107 }
108 
109 
110 
111 
112 PCIe::File::File(const std::string& aPath, int aFlags) :
113  mPath(aPath),
114  mFd(-1),
115  mFlags(aFlags),
116  mLocked(false),
117  mBufferSize(0),
118  mBuffer(NULL)
119 {
120 }
121 
122 
124 {
125  free(mBuffer);
126  close();
127 }
128 
129 
130 const std::string& PCIe::File::getPath() const
131 {
132  return mPath;
133 }
134 
135 
136 void PCIe::File::setPath(const std::string& aPath)
137 {
138  mPath = aPath;
139 }
140 
141 
143 {
144  if (mFd != -1)
145  return;
146 
147  mFd = ::open(mPath.c_str(), mFlags);
148  if ( mFd == -1 ) {
149  exception::PCIeInitialisationError lExc;
150  log(lExc, "Failed to open device file ", Quote(mPath), "; errno=", Integer(errno), ", meaning ", Quote (strerror(errno)));
151  throw lExc;
152  }
153 }
154 
155 
157 {
158  if (mFd != -1) {
159  if (haveLock())
160  unlock();
161  int rc = ::close(mFd);
162  mFd = -1;
163  if (rc == -1)
164  log (Error(), "Failed to close file ", Quote(mPath), "; errno=", Integer(errno), ", meaning ", Quote (strerror(errno)));
165  }
166 }
167 
168 
169 
170 void PCIe::File::createBuffer(const size_t aNrBytes)
171 {
172  if (mBuffer != NULL) {
173  if (mBufferSize >= aNrBytes)
174  return;
175  else {
176  free(mBuffer);
177  mBuffer = NULL;
178  mBufferSize = 0;
179  }
180  }
181 
182  posix_memalign((void**)&mBuffer, 4096/*alignment*/, aNrBytes + 4096);
183  if (mBuffer == NULL) {
184  exception::PCIeCommunicationError lExc;
185  log(lExc, "Failed to allocate ", Integer(aNrBytes + 4096), " bytes in PCIe::File::createBuffer");
186  throw lExc;
187  }
188 
189  mBufferSize=aNrBytes+4096;
190 }
191 
192 
193 void PCIe::File::read(const uint32_t aAddr, const uint32_t aNrWords, std::vector<uint32_t>& aValues)
194 {
195  if (mFd == -1)
196  open();
197 
198  createBuffer(4 * aNrWords);
199 
200  /* select AXI MM address */
201  off_t off = lseek(mFd, 4*aAddr, SEEK_SET);
202  if ( off != off_t(4 * aAddr)) {
203  exception::PCIeCommunicationError lExc;
204  log(lExc, "Offset returned by lseek, ", Integer(off), ", does not match that requested, ", Integer(4*aAddr), " (in preparation for read of ", Integer(aNrWords), " words)");
205  throw lExc;
206  }
207 
208  /* read data from AXI MM into buffer using SGDMA */
209  int rc = ::read(mFd, mBuffer, 4*aNrWords);
210  if (rc == -1) {
211  exception::PCIeCommunicationError lExc;
212  log(lExc, "Read of ", Integer(4*aNrWords), " bytes at address ", Integer(4 * aAddr), " failed! errno=", Integer(errno), ", meaning ", Quote (strerror(errno)));
213  throw lExc;
214  }
215  else if (size_t(rc) < 4*aNrWords) {
216  exception::PCIeCommunicationError lExc;
217  log(lExc, "Only ", Integer(rc), " bytes transferred in read of ", Integer(4*aNrWords), " bytes at address ", Integer(4 * aAddr));
218  throw lExc;
219  }
220 
221  aValues.insert(aValues.end(), reinterpret_cast<uint32_t*>(mBuffer), reinterpret_cast<uint32_t*>(mBuffer)+ aNrWords);
222 }
223 
224 
225 void PCIe::File::write(const uint32_t aAddr, const std::vector<uint32_t>& aValues)
226 {
227  write(4 * aAddr, reinterpret_cast<const uint8_t* const>(aValues.data()), 4 * aValues.size());
228 }
229 
230 
231 void PCIe::File::write(const uint32_t aAddr, const uint8_t* const aPtr, const size_t aNrBytes)
232 {
233  if (mFd == -1)
234  open();
235 
236  assert((aNrBytes % 4) == 0);
237 
238  createBuffer(aNrBytes);
239 
240  // data to write to register address
241  memcpy(mBuffer, aPtr, aNrBytes);
242 
243  /* select AXI MM address */
244  off_t off = lseek(mFd, aAddr, SEEK_SET);
245  if ( off != off_t(aAddr)) {
246  struct stat st;
247  if (fstat(mFd, &st) or (not S_ISFIFO(st.st_mode))) {
248  exception::PCIeCommunicationError lExc;
249  log(lExc, "Offset returned by lseek, ", Integer(off), ", does not match that requested, ", Integer(aAddr), " (in preparation for write of ", Integer(aNrBytes), " bytes)");
250  throw lExc;
251  }
252  }
253 
254  /* write buffer to AXI MM address using SGDMA */
255  int rc = ::write(mFd, mBuffer, aNrBytes);
256  if (rc == -1) {
257  exception::PCIeCommunicationError lExc;
258  log(lExc, "Write of ", Integer(aNrBytes), " bytes at address ", Integer(aAddr), " failed! errno=", Integer(errno), ", meaning ", Quote (strerror(errno)));
259  throw lExc;
260  }
261  else if (size_t(rc) < aNrBytes) {
262  exception::PCIeCommunicationError lExc;
263  log(lExc, "Only ", Integer(rc), " bytes transferred in write of ", Integer(aNrBytes), " bytes at address ", Integer(aAddr));
264  throw lExc;
265  }
266 }
267 
268 
269 void PCIe::File::write(const uint32_t aAddr, const std::vector<std::pair<const uint8_t*, size_t> >& aData)
270 {
271  if (mFd == -1)
272  open();
273 
274  size_t lNrBytes = 0;
275  for (size_t i = 0; i < aData.size(); i++)
276  lNrBytes += aData.at(i).second;
277 
278  assert((lNrBytes % 4) == 0);
279 
280  createBuffer(lNrBytes);
281 
282  // data to write to register address
283  size_t lNrBytesCopied = 0;
284  for (size_t i = 0; i < aData.size(); i++) {
285  memcpy(mBuffer + lNrBytesCopied, aData.at(i).first, aData.at(i).second);
286  lNrBytesCopied += aData.at(i).second;
287  }
288 
289  /* select AXI MM address */
290  off_t off = lseek(mFd, aAddr, SEEK_SET);
291  if ( off != off_t(aAddr)) {
292  struct stat st;
293  if (fstat(mFd, &st) or (not S_ISFIFO(st.st_mode))) {
294  exception::PCIeCommunicationError lExc;
295  log(lExc, "Offset returned by lseek, ", Integer(off), ", does not match that requested, ", Integer(aAddr), " (in preparation for write of ", Integer(lNrBytes), " bytes)");
296  throw lExc;
297  }
298  }
299 
300  /* write buffer to AXI MM address using SGDMA */
301  int rc = ::write(mFd, mBuffer, lNrBytes);
302  if (rc == -1) {
303  exception::PCIeCommunicationError lExc;
304  log(lExc, "Write of ", Integer(lNrBytes), " bytes at address ", Integer(aAddr), " failed! errno=", Integer(errno), ", meaning ", Quote (strerror(errno)));
305  throw lExc;
306  }
307  else if (size_t(rc) < lNrBytes) {
308  exception::PCIeCommunicationError lExc;
309  log(lExc, "Only ", Integer(rc), " bytes transferred in write of ", Integer(lNrBytes), " bytes at address ", Integer(aAddr));
310  throw lExc;
311  }
312 }
313 
314 
316 {
317  return mLocked;
318 }
319 
320 
322 {
323  if ( flock(mFd, LOCK_EX) == -1 ) {
324  exception::MutexError lExc;
325  log(lExc, "Failed to lock device file ", Quote(mPath), "; errno=", Integer(errno), ", meaning ", Quote (strerror(errno)));
326  throw lExc;
327  }
328  mLocked = true;
329 }
330 
331 
333 {
334  if ( flock(mFd, LOCK_UN) == -1 ) {
335  log(Warning(), "Failed to unlock device file ", Quote(mPath), "; errno=", Integer(errno), ", meaning ", Quote (strerror(errno)));
336  }
337  else
338  mLocked = false;
339 }
340 
341 
342 
343 
345  mCount(0),
346  mSessionActive(false)
347 {
348  pthread_mutexattr_t lAttr;
349 
350  int s = pthread_mutexattr_init(&lAttr);
351  if (s != 0) {
352  exception::MutexError lExc;
353  log(lExc, "Error code ", Integer(s), " (", strerror(s), ") returned in mutex attr initialisation");
354  throw lExc;
355  }
356 
357  s = pthread_mutexattr_setpshared(&lAttr, PTHREAD_PROCESS_SHARED);
358  if (s != 0) {
359  exception::MutexError lExc;
360  log(lExc, "Error code ", Integer(s), " (", strerror(s), ") returned by pthread_mutexattr_setpshared");
361  throw lExc;
362  }
363 
364  s = pthread_mutexattr_setrobust(&lAttr, PTHREAD_MUTEX_ROBUST);
365  if (s != 0) {
366  exception::MutexError lExc;
367  log(lExc, "Error code ", Integer(s), " (", strerror(s), ") returned by pthread_mutexattr_setrobust");
368  throw lExc;
369  }
370 
371  s = pthread_mutex_init(&mMutex, &lAttr);
372  if (s != 0) {
373  exception::MutexError lExc;
374  log(lExc, "Error code ", Integer(s), " (", strerror(s), ") returned in mutex initialisation");
375  throw lExc;
376  }
377 }
378 
379 
381 {
382 }
383 
384 
386 {
387  int s = pthread_mutex_lock(&mMutex);
388  bool lLastOwnerDied = (s == EOWNERDEAD);
389  if (lLastOwnerDied)
390  s = pthread_mutex_consistent(&mMutex);
391 
392  if (s != 0) {
393  exception::MutexError lExc;
394  log(lExc, "Error code ", Integer(s), " (", strerror(s), ") returned when ", lLastOwnerDied ? "making mutex state consistent" : "locking mutex");
395  throw lExc;
396  }
397 }
398 
399 
401 {
402  int s = pthread_mutex_unlock(&mMutex);
403  if (s != 0)
404  log(Error(), "Error code ", Integer(s), " (", strerror(s), ") returned when unlocking mutex");
405 }
406 
407 
409 {
410  return mCount;
411 }
412 
413 
415 {
416  return mSessionActive;
417 }
418 
419 
421 {
422  mCount++;
423  mSessionActive = true;
424 }
425 
427 {
428  mSessionActive = false;
429 }
430 
431 
432 
433 
434 template <class T>
435 PCIe::SharedObject<T>::SharedObject(const std::string& aName) :
436  mName(aName),
437  mSharedMem(boost::interprocess::open_or_create, aName.c_str(), 1024, 0x0, boost::interprocess::permissions(0666)),
438  mObj(mSharedMem.find_or_construct<T>(boost::interprocess::unique_instance)())
439 {
440 }
441 
442 template <class T>
444 {
445  // boost::interprocess::shared_memory_object::remove(mName.c_str());
446 }
447 
448 template <class T>
450 {
451  return mObj;
452 }
453 
454 template <class T>
456 {
457  return *mObj;
458 }
459 
460 
461 
462 
463 std::string PCIe::getSharedMemName(const std::string& aPath)
464 {
465  std::string lSanitizedPath(aPath);
466  std::replace(lSanitizedPath.begin(), lSanitizedPath.end(), '/', ':');
467 
468  return "/uhal::ipbuspcie-2.0::" + lSanitizedPath;
469 }
470 
471 
472 PCIe::PCIe ( const std::string& aId, const URI& aUri ) :
473  IPbus< 2 , 0 > ( aId , aUri ),
474  mConnected(false),
475  mDeviceFileHostToFPGA(aUri.mHostname.substr(0, aUri.mHostname.find(",")), O_RDWR ),
476  mDeviceFileFPGAToHost(aUri.mHostname.substr(aUri.mHostname.find(",")+1), O_RDWR | O_NONBLOCK /* for read might need O_RDWR | O_NONBLOCK */),
477  mDeviceFileFPGAEvent("", O_RDONLY),
479  mXdma7seriesWorkaround(false),
480  mUseInterrupt(false),
481  mNumberOfPages(0),
482  mMaxInFlight(0),
483  mPageSize(0),
484  mMaxPacketSize(0),
485  mIndexNextPage(0),
488 {
489  if ( aUri.mHostname.find(",") == std::string::npos ) {
490  exception::PCIeInitialisationError lExc;
491  log(lExc, "No comma found in hostname of PCIe client URI '" + uri() + "'; cannot construct 2 paths for device files");
492  throw lExc;
493  }
494  else if ( aUri.mHostname.find(",") == 0 || aUri.mHostname.find(",") == aUri.mHostname.size()-1) {
495  exception::PCIeInitialisationError lExc;
496  log(lExc, "Hostname of PCIe client URI '" + uri() + "' starts/ends with a comma; cannot construct 2 paths for device files");
497  throw lExc;
498  }
499 
500  mSleepDuration = boost::chrono::microseconds(mUseInterrupt ? 0 : 50);
501 
502  for (NameValuePairVectorType::const_iterator lIt = aUri.mArguments.begin(); lIt != aUri.mArguments.end(); lIt++) {
503  if (lIt->first == "events") {
504  if (mUseInterrupt) {
505  exception::PCIeInitialisationError lExc;
506  log(lExc, "PCIe client URI ", Quote(uri()), ": 'events' attribute is specified multiple times");
507  throw lExc;
508  }
509 
510  mUseInterrupt = true;
511  mDeviceFileFPGAEvent.setPath(lIt->second);
512  log (Info() , "PCIe client with URI ", Quote (uri()), " is configured to use interrupts");
513  }
514  else if (lIt->first == "sleep") {
515  mSleepDuration = boost::chrono::microseconds(boost::lexical_cast<size_t>(lIt->second));
516  log (Notice() , "PCIe client with URI ", Quote (uri()), " : Inter-poll-/-interrupt sleep duration set to ", boost::lexical_cast<size_t>(lIt->second), " us by URI 'sleep' attribute");
517  }
518  else if (lIt->first == "max_in_flight") {
519  mMaxInFlight = boost::lexical_cast<size_t>(lIt->second);
520  log (Notice() , "PCIe client with URI ", Quote (uri()), " : 'Maximum number of packets in flight' set to ", boost::lexical_cast<size_t>(lIt->second), " by URI 'max_in_flight' attribute");
521  }
522  else if (lIt->first == "max_packet_size") {
523  mMaxPacketSize = boost::lexical_cast<size_t>(lIt->second);
524  log (Notice() , "PCIe client with URI ", Quote (uri()), " : 'Maximum packet size (in 32-bit words) set to ", boost::lexical_cast<size_t>(lIt->second), " by URI 'max_packet_size' attribute");
525  }
526  else if (lIt->first == "xdma_7series_workaround") {
527  mXdma7seriesWorkaround = true;
528  log (Notice() , "PCIe client with URI ", Quote (uri()), " : Adjusting size of PCIe reads to a few fixed sizes as workaround for 7-series xdma firmware bug");
529  }
530  else
531  log (Warning() , "Unknown attribute ", Quote (lIt->first), " used in URI ", Quote(uri()));
532  }
533 }
534 
535 
537 {
538  disconnect();
539 }
540 
541 
543 {
544  log(Debug(), "PCIe client (URI: ", Quote(uri()), ") : implementDispatch method called");
545 
546  if ( ! mConnected )
547  connect();
548 
549  if ( mReplyQueue.size() == mMaxInFlight )
550  read();
551  write(aBuffers);
552 }
553 
554 
555 void PCIe::Flush( )
556 {
557  log(Debug(), "PCIe client (URI: ", Quote(uri()), ") : Flush method called");
558  while ( !mReplyQueue.empty() )
559  read();
560 
562 
563  IPCScopedLock_t lLockGuard(*mIPCMutex);
564  mIPCMutex->endSession();
565 }
566 
567 
569 {
570  log(Notice(), "PCIe client ", Quote(id()), " (URI: ", Quote(uri()), ") : closing device files since exception detected");
571 
573 
575 
576  disconnect();
577 
578  InnerProtocol::dispatchExceptionHandler();
579 }
580 
581 
583 {
584  if ( ! mConnected )
585  connect();
586 
587  return mMaxPacketSize * 4;
588 }
589 
590 
592 {
593  if ( ! mConnected )
594  connect();
595 
596  return mMaxPacketSize * 4;
597 }
598 
599 
601 {
602  IPCScopedLock_t lLockGuard(*mIPCMutex);
603  connect(lLockGuard);
604 }
605 
606 
608 {
609  // Read current value of session counter when reading status info from FPGA
610  // (So that can check whether this info is up-to-date later on, when sending next request packet)
612  mIPCSessionCount = mIPCMutex->getCounter();
613 
614  log ( Debug() , "PCIe client is opening device file " , Quote ( mDeviceFileFPGAToHost.getPath() ) , " (device-to-client)" );
616 
617  std::vector<uint32_t> lValues;
618  mDeviceFileFPGAToHost.read(0x0, 4, lValues);
619  aGuard.unlock();
620  log ( Debug(), "Read status info (", Integer(lValues.at(0)), ", ", Integer(lValues.at(1)), ", ", Integer(lValues.at(2)), ", ", Integer(lValues.at(3)), "): ", PacketFmt((const uint8_t*)lValues.data(), 4 * lValues.size()));
621 
622  mNumberOfPages = lValues.at(0);
623  if ( (mMaxInFlight == 0) or (mMaxInFlight > mNumberOfPages) )
625  mPageSize = lValues.at(1);
626  if ( (mMaxPacketSize == 0) or (mMaxPacketSize >= mPageSize) )
628  mIndexNextPage = lValues.at(2);
629  mPublishedReplyPageCount = lValues.at(3);
631 
632  if (lValues.at(1) > 0xFFFF) {
633  exception::PCIeInitialisationError lExc;
634  log (lExc, "Invalid page size, ", Integer(lValues.at(1)), ", reported in device file ", Quote(mDeviceFileFPGAToHost.getPath()));
635  throw lExc;
636  }
637 
639  exception::PCIeInitialisationError lExc;
640  log (lExc, "Next page index, ", Integer(mIndexNextPage), ", reported in device file ", Quote(mDeviceFileFPGAToHost.getPath()), " is inconsistent with number of pages, ", Integer(mNumberOfPages));
641  throw lExc;
642  }
643 
644  log ( Debug() , "PCIe client is opening device file " , Quote ( mDeviceFileHostToFPGA.getPath() ) , " (client-to-device)" );
646 
649 
650  if (mUseInterrupt)
652 
653  mConnected = true;
654  log ( Info() , "PCIe client connected to device at ", Quote(mDeviceFileHostToFPGA.getPath()), ", ", Quote(mDeviceFileFPGAToHost.getPath()), "; FPGA has ", Integer(mNumberOfPages), " pages, each of size ", Integer(mPageSize), " words, index ", Integer(mIndexNextPage), " should be filled next" );
655 }
656 
657 
659 {
663  mConnected = false;
664 }
665 
666 
668 {
669  if (not mDeviceFileHostToFPGA.haveLock()) {
671 
672  IPCScopedLock_t lGuard(*mIPCMutex);
673  mIPCMutex->startSession();
675 
676  // If these two numbers don't match, another client/process has sent packets
677  // more recently than this client has, so must re-read status info
678  if (mIPCExternalSessionActive or (mIPCMutex->getCounter() != mIPCSessionCount)) {
679  connect(lGuard);
680  }
681  }
682 
683  log (Info(), "PCIe client ", Quote(id()), " (URI: ", Quote(uri()), ") : writing ", Integer(aBuffers->sendCounter() / 4), "-word packet to page ", Integer(mIndexNextPage), " in ", Quote(mDeviceFileHostToFPGA.getPath()));
684 
685  const uint32_t lHeaderWord = (0x10000 | (((aBuffers->sendCounter() / 4) - 1) & 0xFFFF));
686  std::vector<std::pair<const uint8_t*, size_t> > lDataToWrite;
687  lDataToWrite.push_back( std::make_pair(reinterpret_cast<const uint8_t*>(&lHeaderWord), sizeof lHeaderWord) );
688  lDataToWrite.push_back( std::make_pair(aBuffers->getSendBuffer(), aBuffers->sendCounter()) );
689 
690  IPCScopedLock_t lGuard(*mIPCMutex);
692  log (Debug(), "Wrote " , Integer((aBuffers->sendCounter() / 4) + 1), " 32-bit words at address " , Integer(mIndexNextPage * 4 * mPageSize), " ... ", PacketFmt(lDataToWrite));
693 
695  mReplyQueue.push_back(aBuffers);
696 }
697 
698 
700 {
701  const size_t lPageIndexToRead = (mIndexNextPage - mReplyQueue.size() + mNumberOfPages) % mNumberOfPages;
702  SteadyClock_t::time_point lStartTime = SteadyClock_t::now();
703 
705  {
706  if (mUseInterrupt)
707  {
708  std::vector<uint32_t> lRxEvent;
709  // wait for interrupt; read events file node to see if user interrupt has come
710  while (true) {
711  mDeviceFileFPGAEvent.read(0, 1, lRxEvent);
712  if (lRxEvent.at(0) == 1) {
713  break;
714  }
715  lRxEvent.clear();
716 
717  if (SteadyClock_t::now() - lStartTime > boost::chrono::microseconds(getBoostTimeoutPeriod().total_microseconds())) {
718  exception::PCIeTimeout lExc;
719  log(lExc, "Next page (index ", Integer(lPageIndexToRead), " count ", Integer(mPublishedReplyPageCount+1), ") of PCIe device '" + mDeviceFileHostToFPGA.getPath() + "' is not ready after timeout period");
720  throw lExc;
721  }
722 
723  log(Debug(), "PCIe client ", Quote(id()), " (URI: ", Quote(uri()), ") : Waiting for interrupt; sleeping for ", mSleepDuration.count(), "us");
724  if (mSleepDuration > boost::chrono::microseconds(0))
725  boost::this_thread::sleep_for( mSleepDuration );
726 
727  } // end of while (true)
728 
729  log(Info(), "PCIe client ", Quote(id()), " (URI: ", Quote(uri()), ") : Reading page ", Integer(lPageIndexToRead), " (interrupt received)");
730  }
731  else
732  {
733  uint32_t lHwPublishedPageCount = 0x0;
734 
735  std::vector<uint32_t> lValues;
736  while ( true ) {
737  // FIXME : Improve by simply adding fileWrite method that takes uint32_t ref as argument (or returns uint32_t)
738  IPCScopedLock_t lGuard(*mIPCMutex);
739  mDeviceFileFPGAToHost.read(0, (mXdma7seriesWorkaround ? 8 : 4), lValues);
740  lHwPublishedPageCount = lValues.at(3);
741  log (Debug(), "Read status info from addr 0 (", Integer(lValues.at(0)), ", ", Integer(lValues.at(1)), ", ", Integer(lValues.at(2)), ", ", Integer(lValues.at(3)), "): ", PacketFmt((const uint8_t*)lValues.data(), 4 * lValues.size()));
742 
743  if (lHwPublishedPageCount != mPublishedReplyPageCount) {
744  mPublishedReplyPageCount = lHwPublishedPageCount;
745  break;
746  }
747  // FIXME: Throw if published page count is invalid number
748 
749  if (SteadyClock_t::now() - lStartTime > boost::chrono::microseconds(getBoostTimeoutPeriod().total_microseconds())) {
750  exception::PCIeTimeout lExc;
751  log(lExc, "Next page (index ", Integer(lPageIndexToRead), " count ", Integer(mPublishedReplyPageCount+1), ") of PCIe device '" + mDeviceFileHostToFPGA.getPath() + "' is not ready after timeout period");
752  throw lExc;
753  }
754 
755  log(Debug(), "PCIe client ", Quote(id()), " (URI: ", Quote(uri()), ") : Trying to read page index ", Integer(lPageIndexToRead), " = count ", Integer(mReadReplyPageCount+1), "; published page count is ", Integer(lHwPublishedPageCount), "; sleeping for ", mSleepDuration.count(), "us");
756  if (mSleepDuration > boost::chrono::microseconds(0))
757  boost::this_thread::sleep_for( mSleepDuration );
758  lValues.clear();
759  }
760 
761  log(Info(), "PCIe client ", Quote(id()), " (URI: ", Quote(uri()), ") : Reading page ", Integer(lPageIndexToRead), " (published count ", Integer(lHwPublishedPageCount), ", surpasses required, ", Integer(mReadReplyPageCount + 1), ")");
762  }
763  }
765 
766  // PART 1 : Read the page
767  boost::shared_ptr<Buffers> lBuffers = mReplyQueue.front();
768  mReplyQueue.pop_front();
769 
770  uint32_t lNrWordsToRead(lBuffers->replyCounter() >> 2);
771  if(mXdma7seriesWorkaround and (lNrWordsToRead % 32 == 0 || lNrWordsToRead % 32 == 28 || lNrWordsToRead < 4))
772  lNrWordsToRead += 4;
773  lNrWordsToRead += 1;
774 
775  std::vector<uint32_t> lPageContents;
776  IPCScopedLock_t lGuard(*mIPCMutex);
777  mDeviceFileFPGAToHost.read(4 + lPageIndexToRead * mPageSize, lNrWordsToRead , lPageContents);
778  lGuard.unlock();
779  log (Debug(), "Read " , Integer(lNrWordsToRead), " 32-bit words from address " , Integer(4 + lPageIndexToRead * 4 * mPageSize), " ... ", PacketFmt((const uint8_t*)lPageContents.data(), 4 * lPageContents.size()));
780 
781  // PART 2 : Transfer to reply buffer
782  const std::deque< std::pair< uint8_t* , uint32_t > >& lReplyBuffers ( lBuffers->getReplyBuffer() );
783  size_t lNrWordsInPacket = (lPageContents.at(0) >> 16) + (lPageContents.at(0) & 0xFFFF);
784  if (lNrWordsInPacket != (lBuffers->replyCounter() >> 2))
785  log (Warning(), "Expected reply packet to contain ", Integer(lBuffers->replyCounter() >> 2), " words, but it actually contains ", Integer(lNrWordsInPacket), " words");
786 
787  size_t lNrBytesCopied = 0;
788  for ( std::deque< std::pair< uint8_t* , uint32_t > >::const_iterator lIt = lReplyBuffers.begin() ; lIt != lReplyBuffers.end() ; ++lIt )
789  {
790  // Don't copy more of page than was written to, for cases when less data received than expected
791  if ( lNrBytesCopied >= 4*lNrWordsInPacket)
792  break;
793 
794  size_t lNrBytesToCopy = std::min( lIt->second , uint32_t(4*lNrWordsInPacket - lNrBytesCopied) );
795  memcpy ( lIt->first, &lPageContents.at(1 + (lNrBytesCopied / 4)), lNrBytesToCopy );
796  lNrBytesCopied += lNrBytesToCopy;
797  }
798 
799 
800  // PART 3 : Validate the packet contents
801  uhal::exception::exception* lExc = NULL;
802  try
803  {
804  lExc = ClientInterface::validate ( lBuffers );
805  }
806  catch ( exception::exception& aExc )
807  {
808  exception::ValidationError lExc2;
809  log ( lExc2 , "Exception caught during reply validation for PCIe device with URI " , Quote ( this->uri() ) , "; what returned: " , Quote ( aExc.what() ) );
810  throw lExc2;
811  }
812 
813  if (lExc != NULL)
814  lExc->throwAsDerivedType();
815 }
816 
817 
818 } // end ns uhal
uhal::PCIe::File::close
void close()
Definition: ProtocolPCIe.cpp:156
uhal::PCIe::IPCScopedLock_t
boost::unique_lock< IPCMutex_t > IPCScopedLock_t
Definition: ProtocolPCIe.hpp:182
uhal::PCIe::mIPCMutex
SharedObject< IPCMutex_t > mIPCMutex
Definition: ProtocolPCIe.hpp:254
uhal::PCIe::read
void read()
Read next pending reply packet from appropriate page of FPGA-to-host device file, and validate conten...
Definition: ProtocolPCIe.cpp:699
uhal::PCIe::mIPCExternalSessionActive
bool mIPCExternalSessionActive
Definition: ProtocolPCIe.hpp:255
uhal::operator<<
std::ostream & operator<<(std::ostream &aStr, const uhal::HttpResponseType &aHttpResponse)
Definition: HttpResponseGrammar.cpp:41
uhal::PCIe::write
void write(const boost::shared_ptr< Buffers > &aBuffers)
Write request packet to next page in host-to-FPGA device file.
Definition: ProtocolPCIe.cpp:667
uhal::PCIe::SharedObject::operator->
T * operator->()
Definition: ProtocolPCIe.cpp:449
uhal::PCIe::mUseInterrupt
bool mUseInterrupt
Definition: ProtocolPCIe.hpp:260
uhal::PCIe::RobustMutex::getCounter
uint64_t getCounter() const
Definition: ProtocolPCIe.cpp:408
uhal::PCIe::mMaxPacketSize
uint32_t mMaxPacketSize
Definition: ProtocolPCIe.hpp:264
uhal::exception::exception::what
virtual const char * what() const
Function which returns the error message associated with an exception If no error message has previou...
Definition: exception.cpp:87
uhal::dec
@ dec
Decimal.
Definition: log_inserters.integer.hpp:50
uhal::URI::mHostname
std::string mHostname
The "host" part of a URI of the form "protocol://host:port/patha/pathb/blah.ext?key1=val1&key2=val2&k...
Definition: URI.hpp:54
ProtocolPCIe.hpp
boost::shared_ptr
Definition: DerivedNodeFactory.hpp:52
uhal::PCIe::mSleepDuration
boost::chrono::microseconds mSleepDuration
Definition: ProtocolPCIe.hpp:262
uhal::PCIe::getSharedMemName
static std::string getSharedMemName(const std::string &)
Definition: ProtocolPCIe.cpp:463
uhal::PCIe::RobustMutex::mMutex
pthread_mutex_t mMutex
Definition: ProtocolPCIe.hpp:158
uhal::PCIe::PacketFmt::mData
const std::vector< std::pair< const uint8_t *, size_t > > mData
Definition: ProtocolPCIe.hpp:97
uhal::PCIe::mMaxInFlight
uint32_t mMaxInFlight
Definition: ProtocolPCIe.hpp:264
uhal::PCIe::mDeviceFileHostToFPGA
File mDeviceFileHostToFPGA
Host-to-FPGA device file.
Definition: ProtocolPCIe.hpp:248
uhal::PCIe::Flush
virtual void Flush()
Concrete implementation of the synchronization function to block until all buffers have been sent,...
Definition: ProtocolPCIe.cpp:555
uhal::PCIe::File::open
void open()
Definition: ProtocolPCIe.cpp:142
uhal::URI::mArguments
NameValuePairVectorType mArguments
The "key1=val1&key2=val2&key3=val3" part of a URI of the form "protocol://host:port/patha/pathb/blah....
Definition: URI.hpp:62
uhal::IPbus
A class which provides the version-specific functionality for IPbus.
Definition: ProtocolIPbus.hpp:69
uhal::PCIe::PacketFmt::~PacketFmt
~PacketFmt()
Definition: ProtocolPCIe.cpp:88
boost
Definition: log.hpp:13
uhal::PCIe::PacketFmt::PacketFmt
PacketFmt(const uint8_t *const, const size_t)
Definition: ProtocolPCIe.cpp:78
uhal::PCIe::connect
void connect()
Set up the connection to the device.
Definition: ProtocolPCIe.cpp:600
uhal::PCIe::File::setPath
void setPath(const std::string &aPath)
Definition: ProtocolPCIe.cpp:136
uhal::PCIe::File::createBuffer
void createBuffer(const size_t aNrBytes)
Definition: ProtocolPCIe.cpp:170
uhal::min
@ min
minutes past the hour formatted as two digits e.g.
Definition: log_inserters.time.hpp:63
uhal::PCIe::File::unlock
void unlock()
Definition: ProtocolPCIe.cpp:332
uhal::PCIe::RobustMutex::RobustMutex
RobustMutex()
Definition: ProtocolPCIe.cpp:344
uhal::exception::exception
An abstract base exception class, including an interface to throw as the derived type (for passing ex...
Definition: exception.hpp:71
uhal::PCIe::mConnected
bool mConnected
Definition: ProtocolPCIe.hpp:245
uhal::PCIe::File::write
void write(const uint32_t aAddr, const std::vector< uint32_t > &aValues)
Definition: ProtocolPCIe.cpp:225
uhal::PCIe::PacketFmt
Definition: ProtocolPCIe.hpp:91
uhal::PCIe::RobustMutex::~RobustMutex
~RobustMutex()
Definition: ProtocolPCIe.cpp:380
uhal::PCIe::SharedObject::SharedObject
SharedObject(const std::string &aName)
Definition: ProtocolPCIe.cpp:435
uhal::PCIe::SharedObject::operator*
T & operator*()
Definition: ProtocolPCIe.cpp:455
uhal::PCIe::RobustMutex::startSession
void startSession()
Definition: ProtocolPCIe.cpp:420
uhal
Definition: HttpResponseGrammar.hpp:49
uhal::PCIe::File::haveLock
bool haveLock() const
Definition: ProtocolPCIe.cpp:315
log_inserters.quote.hpp
uhal::PCIe::File::File
File(const std::string &aPath, int aFlags)
Definition: ProtocolPCIe.cpp:112
uhal::Info
InfoLevel Info
Definition: LogLevels.cpp:115
uhal::PCIe::mXdma7seriesWorkaround
bool mXdma7seriesWorkaround
Definition: ProtocolPCIe.hpp:258
uhal::PCIe::dispatchExceptionHandler
virtual void dispatchExceptionHandler()
Function which tidies up this protocol layer in the event of an exception.
Definition: ProtocolPCIe.cpp:568
uhal::log
void log(FatalLevel &aFatal, const T0 &aArg0)
Function to add a log entry at Fatal level.
Definition: log.hxx:20
log_inserters.integer.hpp
uhal::PCIe::mDeviceFileFPGAEvent
File mDeviceFileFPGAEvent
FPGA-to-host interrupt (event) file.
Definition: ProtocolPCIe.hpp:252
uhal::Integer
_Integer< T, IntFmt<> > Integer(const T &aT)
Forward declare a function which creates an instance of the ultra-lightweight wrapper from an integer...
Definition: log_inserters.integer.hxx:43
uhal::hex
@ hex
Hexadecimal.
Definition: log_inserters.integer.hpp:51
uhal::PCIe::RobustMutex::isActive
bool isActive() const
Definition: ProtocolPCIe.cpp:414
uhal::PCIe::getMaxSendSize
uint32_t getMaxSendSize()
Return the maximum size to be sent based on the buffer size in the target.
Definition: ProtocolPCIe.cpp:582
uhal::PCIe::getMaxReplySize
uint32_t getMaxReplySize()
Return the maximum size of reply packet based on the buffer size in the target.
Definition: ProtocolPCIe.cpp:591
uhal::Warning
WarningLevel Warning
Definition: LogLevels.cpp:79
uhal::PCIe::RobustMutex::lock
void lock()
Definition: ProtocolPCIe.cpp:385
uhal::exception::exception::throwAsDerivedType
virtual void throwAsDerivedType()=0
Function which casts a pointer from the base type of this object to a derived type of this object and...
uhal::PCIe::mDeviceFileFPGAToHost
File mDeviceFileFPGAToHost
FPGA-to-host device file.
Definition: ProtocolPCIe.hpp:250
uhal::PCIe::implementDispatch
void implementDispatch(boost::shared_ptr< Buffers > aBuffers)
Send the IPbus buffer to the target, read back the response and call the packing-protocol's validate ...
Definition: ProtocolPCIe.cpp:542
uhal::Error
ErrorLevel Error
Definition: LogLevels.cpp:61
uhal::PCIe::mIndexNextPage
uint32_t mIndexNextPage
Definition: ProtocolPCIe.hpp:264
uhal::Quote
_Quote< T > Quote(const T &aT)
Definition: log_inserters.quote.hxx:49
uhal::PCIe::disconnect
void disconnect()
Close the connection to the device.
Definition: ProtocolPCIe.cpp:658
uhal::PCIe::File::~File
~File()
Definition: ProtocolPCIe.cpp:123
uhal::PCIe::~PCIe
virtual ~PCIe()
Destructor.
Definition: ProtocolPCIe.cpp:536
uhal::tests::uri
std::string uri
Definition: test_single.cpp:89
uhal::PCIe::mReplyQueue
std::deque< boost::shared_ptr< Buffers > > mReplyQueue
The list of buffers still awaiting a reply.
Definition: ProtocolPCIe.hpp:267
uhal::PCIe::File::lock
void lock()
Definition: ProtocolPCIe.cpp:321
uhal::PCIe::mNumberOfPages
uint32_t mNumberOfPages
Definition: ProtocolPCIe.hpp:264
uhal::Debug
DebugLevel Debug
Definition: LogLevels.cpp:133
uhal::Notice
NoticeLevel Notice
Definition: LogLevels.cpp:97
log.hpp
uhal::PCIe::mReadReplyPageCount
uint32_t mReadReplyPageCount
Definition: ProtocolPCIe.hpp:264
uhal::PCIe::mPublishedReplyPageCount
uint32_t mPublishedReplyPageCount
Definition: ProtocolPCIe.hpp:264
uhal::PCIe::RobustMutex::endSession
void endSession()
Definition: ProtocolPCIe.cpp:426
uhal::PCIe::File::read
void read(const uint32_t aAddr, const uint32_t aNrWords, std::vector< uint32_t > &aValues)
Definition: ProtocolPCIe.cpp:193
uhal::ClientInterface::returnBufferToPool
void returnBufferToPool(boost::shared_ptr< Buffers > &aBuffers)
Function to return a buffer to the buffer pool.
Definition: ClientInterface.cpp:221
uhal::PCIe::File::getPath
const std::string & getPath() const
Definition: ProtocolPCIe.cpp:130
uhal::PCIe::SharedObject::~SharedObject
~SharedObject()
Definition: ProtocolPCIe.cpp:443
LogLevels.hpp
URI.hpp
uhal::PCIe::RobustMutex::unlock
void unlock()
Definition: ProtocolPCIe.cpp:400
uhal::URI
Struct to store a URI when parsed by boost spirit.
Definition: URI.hpp:50
uhal::PCIe::mPageSize
uint32_t mPageSize
Definition: ProtocolPCIe.hpp:264
uhal::ClientInterface::validate
virtual exception::exception * validate(boost::shared_ptr< Buffers > aBuffers)
Function which dispatch calls when the reply is received to check that the headers are as expected.
Definition: ClientInterface.cpp:188
Buffers.hpp
uhal::PCIe::mIPCSessionCount
uint64_t mIPCSessionCount
Definition: ProtocolPCIe.hpp:256
uhal::PCIe::PCIe
PCIe(const std::string &aId, const URI &aUri)
Constructor.
Definition: ProtocolPCIe.cpp:472
ClientFactory.hpp