μHAL (v2.6.5)
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/stat.h>
53 #include <stdlib.h> // for size_t, free
54 #include <string.h> // for memcpy
55 #include <unistd.h>
56 
57 #include <boost/chrono/duration.hpp> // for operator>
58 #include <boost/chrono/time_point.hpp> // for operator-
59 #include <boost/lexical_cast.hpp>
60 #include <boost/thread/thread.hpp> // for sleep_for
61 #include <boost/date_time/posix_time/posix_time_types.hpp> // for time_dura...
62 
63 #include "uhal/grammars/URI.hpp" // for URI
64 #include "uhal/log/LogLevels.hpp" // for BaseLogLevel
65 #include "uhal/log/log_inserters.integer.hpp" // for Integer
66 #include "uhal/log/log_inserters.quote.hpp" // for Quote
67 #include "uhal/log/log.hpp"
68 #include "uhal/Buffers.hpp"
69 #include "uhal/ClientFactory.hpp"
70 
71 
72 namespace uhal {
73 
74 
75 PCIe::PacketFmt::PacketFmt(const uint8_t* const aPtr, const size_t aNrBytes) :
76  mData(1, std::pair<const uint8_t*, size_t>(aPtr, aNrBytes))
77 {
78 }
79 
80 PCIe::PacketFmt::PacketFmt(const std::vector< std::pair<const uint8_t*, size_t> >& aData) :
81  mData(aData)
82 {}
83 
85 {}
86 
87 std::ostream& operator<<(std::ostream& aStream, const PCIe::PacketFmt& aPacket)
88 {
89  std::ios::fmtflags lOrigFlags( aStream.flags() );
90 
91  size_t lNrBytesWritten = 0;
92  for (size_t i = 0; i < aPacket.mData.size(); i++) {
93  for (const uint8_t* lPtr = aPacket.mData.at(i).first; lPtr != (aPacket.mData.at(i).first + aPacket.mData.at(i).second); lPtr++, lNrBytesWritten++) {
94  if ((lNrBytesWritten & 3) == 0)
95  aStream << std::endl << " @ " << std::setw(3) << std::dec << (lNrBytesWritten >> 2) << " : x";
96  aStream << std::setw(2) << std::hex << uint16_t(*lPtr) << " ";
97  }
98  }
99 
100  aStream.flags( lOrigFlags );
101  return aStream;
102 }
103 
104 
105 
106 
107 PCIe::File::File(const std::string& aPath, int aFlags) :
108  mPath(aPath),
109  mFd(-1),
110  mFlags(aFlags),
111  mBufferSize(0),
112  mBuffer(NULL)
113 {
114 }
115 
117 {
118  free(mBuffer);
119  close();
120 }
121 
122 const std::string& PCIe::File::getPath() const
123 {
124  return mPath;
125 }
126 
127 void PCIe::File::setPath(const std::string& aPath)
128 {
129  mPath = aPath;
130 }
131 
133 {
134  if (mFd != -1)
135  return;
136 
137  mFd = ::open(mPath.c_str(), mFlags);
138  if ( mFd == -1 ) {
139  exception::PCIeInitialisationError lExc;
140  log(lExc, "Failed to open device file ", Quote(mPath), "; errno=", Integer(errno), ", meaning ", Quote (strerror(errno)));
141  throw lExc;
142  }
143 }
144 
146 {
147  if (mFd != -1) {
148  int rc = ::close(mFd);
149  mFd = -1;
150  if (rc == -1)
151  log (Error(), "Failed to close file ", Quote(mPath), "; errno=", Integer(errno), ", meaning ", Quote (strerror(errno)));
152  }
153 }
154 
155 
156 
157 void PCIe::File::createBuffer(const size_t aNrBytes)
158 {
159  if (mBuffer != NULL) {
160  if (mBufferSize >= aNrBytes)
161  return;
162  else {
163  delete mBuffer;
164  mBuffer = NULL;
165  }
166  }
167 
168  posix_memalign((void**)&mBuffer, 4096/*alignment*/, aNrBytes + 4096);
169  if (mBuffer == NULL) {
170  exception::PCIeCommunicationError lExc;
171  log(lExc, "Failed to allocate ", Integer(aNrBytes + 4096), " bytes in PCIe::File::createBuffer");
172  throw lExc;
173  }
174 }
175 
176 
177 void PCIe::File::read(const uint32_t aAddr, const uint32_t aNrWords, std::vector<uint32_t>& aValues)
178 {
179  if (mFd == -1)
180  open();
181 
182  createBuffer(4 * aNrWords);
183 
184  /* select AXI MM address */
185  off_t off = lseek(mFd, 4*aAddr, SEEK_SET);
186  if ( off != (4 * aAddr)) {
187  exception::PCIeCommunicationError lExc;
188  log(lExc, "Offset returned by lseek, ", Integer(off), ", does not match that requested, ", Integer(4*aAddr), " (in preparation for read of ", Integer(aNrWords), " words)");
189  throw lExc;
190  }
191 
192  /* read data from AXI MM into buffer using SGDMA */
193  int rc = ::read(mFd, mBuffer, 4*aNrWords);
194  if (rc == -1) {
195  exception::PCIeCommunicationError lExc;
196  log(lExc, "Read of ", Integer(4*aNrWords), " bytes at address ", Integer(4 * aAddr), " failed! errno=", Integer(errno), ", meaning ", Quote (strerror(errno)));
197  throw lExc;
198  }
199  else if (size_t(rc) < 4*aNrWords) {
200  exception::PCIeCommunicationError lExc;
201  log(lExc, "Only ", Integer(rc), " bytes transferred in read of ", Integer(4*aNrWords), " bytes at address ", Integer(4 * aAddr));
202  throw lExc;
203  }
204 
205  aValues.insert(aValues.end(), reinterpret_cast<uint32_t*>(mBuffer), reinterpret_cast<uint32_t*>(mBuffer)+ aNrWords);
206 }
207 
208 
209 void PCIe::File::write(const uint32_t aAddr, const std::vector<uint32_t>& aValues)
210 {
211  write(4 * aAddr, reinterpret_cast<const uint8_t* const>(aValues.data()), 4 * aValues.size());
212 }
213 
214 
215 void PCIe::File::write(const uint32_t aAddr, const uint8_t* const aPtr, const size_t aNrBytes)
216 {
217  if (mFd == -1)
218  open();
219 
220  assert((aNrBytes % 4) == 0);
221 
222  createBuffer(aNrBytes);
223 
224  // data to write to register address
225  memcpy(mBuffer, aPtr, aNrBytes);
226 
227  /* select AXI MM address */
228  off_t off = lseek(mFd, aAddr, SEEK_SET);
229  if ( off != aAddr) {
230  struct stat st;
231  if (fstat(mFd, &st) or (not S_ISFIFO(st.st_mode))) {
232  exception::PCIeCommunicationError lExc;
233  log(lExc, "Offset returned by lseek, ", Integer(off), ", does not match that requested, ", Integer(aAddr), " (in preparation for write of ", Integer(aNrBytes), " bytes)");
234  throw lExc;
235  }
236  }
237 
238  /* write buffer to AXI MM address using SGDMA */
239  int rc = ::write(mFd, mBuffer, aNrBytes);
240  if (rc == -1) {
241  exception::PCIeCommunicationError lExc;
242  log(lExc, "Write of ", Integer(aNrBytes), " bytes at address ", Integer(aAddr), " failed! errno=", Integer(errno), ", meaning ", Quote (strerror(errno)));
243  throw lExc;
244  }
245  else if (size_t(rc) < aNrBytes) {
246  exception::PCIeCommunicationError lExc;
247  log(lExc, "Only ", Integer(rc), " bytes transferred in write of ", Integer(aNrBytes), " bytes at address ", Integer(aAddr));
248  throw lExc;
249  }
250 }
251 
252 
253 void PCIe::File::write(const uint32_t aAddr, const std::vector<std::pair<const uint8_t*, size_t> >& aData)
254 {
255  if (mFd == -1)
256  open();
257 
258  size_t lNrBytes = 0;
259  for (size_t i = 0; i < aData.size(); i++)
260  lNrBytes += aData.at(i).second;
261 
262  assert((lNrBytes % 4) == 0);
263 
264  createBuffer(lNrBytes);
265 
266  // data to write to register address
267  size_t lNrBytesCopied = 0;
268  for (size_t i = 0; i < aData.size(); i++) {
269  memcpy(mBuffer + lNrBytesCopied, aData.at(i).first, aData.at(i).second);
270  lNrBytesCopied += aData.at(i).second;
271  }
272 
273  /* select AXI MM address */
274  off_t off = lseek(mFd, aAddr, SEEK_SET);
275  if ( off != aAddr) {
276  struct stat st;
277  if (fstat(mFd, &st) or (not S_ISFIFO(st.st_mode))) {
278  exception::PCIeCommunicationError lExc;
279  log(lExc, "Offset returned by lseek, ", Integer(off), ", does not match that requested, ", Integer(aAddr), " (in preparation for write of ", Integer(lNrBytes), " bytes)");
280  throw lExc;
281  }
282  }
283 
284  /* write buffer to AXI MM address using SGDMA */
285  int rc = ::write(mFd, mBuffer, lNrBytes);
286  if (rc == -1) {
287  exception::PCIeCommunicationError lExc;
288  log(lExc, "Write of ", Integer(lNrBytes), " bytes at address ", Integer(aAddr), " failed! errno=", Integer(errno), ", meaning ", Quote (strerror(errno)));
289  throw lExc;
290  }
291  else if (size_t(rc) < lNrBytes) {
292  exception::PCIeCommunicationError lExc;
293  log(lExc, "Only ", Integer(rc), " bytes transferred in write of ", Integer(lNrBytes), " bytes at address ", Integer(aAddr));
294  throw lExc;
295  }
296 }
297 
298 
299 
300 
301 PCIe::PCIe ( const std::string& aId, const URI& aUri ) :
302  IPbus< 2 , 0 > ( aId , aUri ),
303  mConnected(false),
304  mDeviceFileHostToFPGA(aUri.mHostname.substr(0, aUri.mHostname.find(",")), O_RDWR ),
305  mDeviceFileFPGAToHost(aUri.mHostname.substr(aUri.mHostname.find(",")+1), O_RDWR | O_NONBLOCK /* for read might need O_RDWR | O_NONBLOCK */),
306  mDeviceFileFPGAEvent("", O_RDONLY),
307  mXdma7seriesWorkaround(false),
308  mUseInterrupt(false),
309  mNumberOfPages(0),
310  mMaxInFlight(0),
311  mPageSize(0),
312  mMaxPacketSize(0),
313  mIndexNextPage(0),
316  mAsynchronousException ( NULL )
317 {
318  if ( aUri.mHostname.find(",") == std::string::npos ) {
319  exception::PCIeInitialisationError lExc;
320  log(lExc, "No comma found in hostname of PCIe client URI '" + uri() + "'; cannot construct 2 paths for device files");
321  throw lExc;
322  }
323  else if ( aUri.mHostname.find(",") == 0 || aUri.mHostname.find(",") == aUri.mHostname.size()-1) {
324  exception::PCIeInitialisationError lExc;
325  log(lExc, "Hostname of PCIe client URI '" + uri() + "' starts/ends with a comma; cannot construct 2 paths for device files");
326  throw lExc;
327  }
328 
329  mSleepDuration = boost::chrono::microseconds(mUseInterrupt ? 0 : 50);
330 
331  for (NameValuePairVectorType::const_iterator lIt = aUri.mArguments.begin(); lIt != aUri.mArguments.end(); lIt++) {
332  if (lIt->first == "events") {
333  if (mUseInterrupt) {
334  exception::PCIeInitialisationError lExc;
335  log(lExc, "PCIe client URI ", Quote(uri()), ": 'events' attribute is specified multiple times");
336  throw lExc;
337  }
338 
339  mUseInterrupt = true;
340  mDeviceFileFPGAEvent.setPath(lIt->second);
341  log (Info() , "PCIe client with URI ", Quote (uri()), " is configured to use interrupts");
342  }
343  else if (lIt->first == "sleep") {
344  mSleepDuration = boost::chrono::microseconds(boost::lexical_cast<size_t>(lIt->second));
345  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");
346  }
347  else if (lIt->first == "max_in_flight") {
348  mMaxInFlight = boost::lexical_cast<size_t>(lIt->second);
349  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");
350  }
351  else if (lIt->first == "max_packet_size") {
352  mMaxPacketSize = boost::lexical_cast<size_t>(lIt->second);
353  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");
354  }
355  else if (lIt->first == "xdma_7series_workaround") {
356  mXdma7seriesWorkaround = true;
357  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");
358  }
359  else
360  log (Warning() , "Unknown attribute ", Quote (lIt->first), " used in URI ", Quote(uri()));
361  }
362 }
363 
364 
365 PCIe::~PCIe()
366 {
367  disconnect();
368 }
369 
370 
372 {
373  log(Debug(), "PCIe client (URI: ", Quote(uri()), ") : implementDispatch method called");
374 
375  if ( ! mConnected )
376  connect();
377 
378  if ( mReplyQueue.size() == mMaxInFlight )
379  read();
380  write(aBuffers);
381 }
382 
383 
384 void PCIe::Flush( )
385 {
386  log(Debug(), "PCIe client (URI: ", Quote(uri()), ") : Flush method called");
387  while ( !mReplyQueue.empty() )
388  read();
389 
390 }
391 
392 
394 {
395  log(Notice(), "PCIe client ", Quote(id()), " (URI: ", Quote(uri()), ") : closing device files since exception detected");
396 
398  disconnect();
399 
400  InnerProtocol::dispatchExceptionHandler();
401 }
402 
403 
405 {
406  if ( ! mConnected )
407  connect();
408 
409  return mMaxPacketSize * 4;
410 }
411 
412 
414 {
415  if ( ! mConnected )
416  connect();
417 
418  return mMaxPacketSize * 4;
419 }
420 
421 
423 {
424  log ( Debug() , "PCIe client is opening device file " , Quote ( mDeviceFileHostToFPGA.getPath() ) , " (client-to-device)" );
426 
427  log ( Debug() , "PCIe client is opening device file " , Quote ( mDeviceFileFPGAToHost.getPath() ) , " (device-to-client)" );
428  std::vector<uint32_t> lValues;
429  mDeviceFileFPGAToHost.read(0x0, 4, lValues);
430  log (Info(), "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()));
431 
432  mNumberOfPages = lValues.at(0);
433  if ( (mMaxInFlight == 0) or (mMaxInFlight > mNumberOfPages) )
435  mPageSize = lValues.at(1);
436  if ( (mMaxPacketSize == 0) or (mMaxPacketSize >= mPageSize) )
438  mIndexNextPage = lValues.at(2);
439  mPublishedReplyPageCount = lValues.at(3);
441 
444 
445  if (lValues.at(1) > 0xFFFF) {
446  exception::PCIeInitialisationError lExc;
447  log (lExc, "Invalid page size, ", Integer(lValues.at(1)), ", reported in device file ", Quote(mDeviceFileFPGAToHost.getPath()));
448  throw lExc;
449  }
450 
452  exception::PCIeInitialisationError lExc;
453  log (lExc, "Next page index, ", Integer(mIndexNextPage), ", reported in device file ", Quote(mDeviceFileFPGAToHost.getPath()), " is inconsistent with number of pages, ", Integer(mNumberOfPages));
454  throw lExc;
455  }
456 
457  if (mUseInterrupt)
459 
460  mConnected = true;
461  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" );
462 }
463 
464 
466 {
470  mConnected = false;
471 }
472 
473 
474 
475 void PCIe::write(const boost::shared_ptr<Buffers>& aBuffers)
476 {
477  log (Info(), "PCIe client ", Quote(id()), " (URI: ", Quote(uri()), ") : writing ", Integer(aBuffers->sendCounter() / 4), "-word packet to page ", Integer(mIndexNextPage), " in ", Quote(mDeviceFileHostToFPGA.getPath()));
478 
479  const uint32_t lHeaderWord = (0x10000 | (((aBuffers->sendCounter() / 4) - 1) & 0xFFFF));
480  std::vector<std::pair<const uint8_t*, size_t> > lDataToWrite;
481  lDataToWrite.push_back( std::make_pair(reinterpret_cast<const uint8_t*>(&lHeaderWord), sizeof lHeaderWord) );
482  lDataToWrite.push_back( std::make_pair(aBuffers->getSendBuffer(), aBuffers->sendCounter()) );
484 
485  log (Debug(), "Wrote " , Integer((aBuffers->sendCounter() / 4) + 1), " 32-bit words at address " , Integer(mIndexNextPage * 4 * mPageSize), " ... ", PacketFmt(lDataToWrite));
486 
487  mIndexNextPage = (mIndexNextPage + 1) % mNumberOfPages;
488  mReplyQueue.push_back(aBuffers);
489 }
490 
491 
492 // -------------------------------------------------------------------------------
493 
494 void PCIe::read()
495 {
496  const size_t lPageIndexToRead = (mIndexNextPage - mReplyQueue.size() + mNumberOfPages) % mNumberOfPages;
497  SteadyClock_t::time_point lStartTime = SteadyClock_t::now();
498 
500  {
501  if (mUseInterrupt)
502  {
503  std::vector<uint32_t> lRxEvent;
504  // wait for interrupt; read events file node to see if user interrupt has come
505  while (true) {
506  mDeviceFileFPGAEvent.read(0, 1, lRxEvent);
507  if (lRxEvent.at(0) == 1) {
508  break;
509  }
510  lRxEvent.clear();
511 
512  if (SteadyClock_t::now() - lStartTime > boost::chrono::microseconds(getBoostTimeoutPeriod().total_microseconds())) {
513  exception::PCIeTimeout lExc;
514  log(lExc, "Next page (index ", Integer(lPageIndexToRead), " count ", Integer(mPublishedReplyPageCount+1), ") of PCIe device '" + mDeviceFileHostToFPGA.getPath() + "' is not ready after timeout period");
515  throw lExc;
516  }
517 
518  log(Debug(), "PCIe client ", Quote(id()), " (URI: ", Quote(uri()), ") : Waiting for interrupt; sleeping for ", mSleepDuration.count(), "us");
519  if (mSleepDuration > boost::chrono::microseconds(0))
520  boost::this_thread::sleep_for( mSleepDuration );
521 
522  } // end of while (true)
523 
524  log(Info(), "PCIe client ", Quote(id()), " (URI: ", Quote(uri()), ") : Reading page ", Integer(lPageIndexToRead), " (interrupt received)");
525  }
526  else
527  {
528  uint32_t lHwPublishedPageCount = 0x0;
529 
530  std::vector<uint32_t> lValues;
531  while ( true ) {
532  // FIXME : Improve by simply adding fileWrite method that takes uint32_t ref as argument (or returns uint32_t)
533  mDeviceFileFPGAToHost.read(0, (mXdma7seriesWorkaround ? 8 : 4), lValues);
534  lHwPublishedPageCount = lValues.at(3);
535  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()));
536 
537  if (lHwPublishedPageCount != mPublishedReplyPageCount) {
538  mPublishedReplyPageCount = lHwPublishedPageCount;
539  break;
540  }
541  // FIXME: Throw if published page count is invalid number
542 
543  if (SteadyClock_t::now() - lStartTime > boost::chrono::microseconds(getBoostTimeoutPeriod().total_microseconds())) {
544  exception::PCIeTimeout lExc;
545  log(lExc, "Next page (index ", Integer(lPageIndexToRead), " count ", Integer(mPublishedReplyPageCount+1), ") of PCIe device '" + mDeviceFileHostToFPGA.getPath() + "' is not ready after timeout period");
546  throw lExc;
547  }
548 
549  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");
550  if (mSleepDuration > boost::chrono::microseconds(0))
551  boost::this_thread::sleep_for( mSleepDuration );
552  lValues.clear();
553  }
554 
555  log(Info(), "PCIe client ", Quote(id()), " (URI: ", Quote(uri()), ") : Reading page ", Integer(lPageIndexToRead), " (published count ", Integer(lHwPublishedPageCount), ", surpasses required, ", Integer(mReadReplyPageCount + 1), ")");
556  }
557  }
559 
560  // PART 1 : Read the page
561  boost::shared_ptr<Buffers> lBuffers = mReplyQueue.front();
562  mReplyQueue.pop_front();
563 
564  uint32_t lNrWordsToRead(lBuffers->replyCounter() >> 2);
565  if(mXdma7seriesWorkaround and (lNrWordsToRead % 32 == 0 || lNrWordsToRead % 32 == 28 || lNrWordsToRead < 4))
566  lNrWordsToRead += 4;
567  lNrWordsToRead += 1;
568 
569  std::vector<uint32_t> lPageContents;
570  mDeviceFileFPGAToHost.read(4 + lPageIndexToRead * mPageSize, lNrWordsToRead , lPageContents);
571  log (Debug(), "Read " , Integer(lNrWordsToRead), " 32-bit words from address " , Integer(4 + lPageIndexToRead * 4 * mPageSize), " ... ", PacketFmt((const uint8_t*)lPageContents.data(), 4 * lPageContents.size()));
572 
573  // PART 2 : Transfer to reply buffer
574  const std::deque< std::pair< uint8_t* , uint32_t > >& lReplyBuffers ( lBuffers->getReplyBuffer() );
575  size_t lNrWordsInPacket = (lPageContents.at(0) >> 16) + (lPageContents.at(0) & 0xFFFF);
576  if (lNrWordsInPacket != (lBuffers->replyCounter() >> 2))
577  log (Warning(), "Expected reply packet to contain ", Integer(lBuffers->replyCounter() >> 2), " words, but it actually contains ", Integer(lNrWordsInPacket), " words");
578 
579  size_t lNrBytesCopied = 0;
580  for ( std::deque< std::pair< uint8_t* , uint32_t > >::const_iterator lIt = lReplyBuffers.begin() ; lIt != lReplyBuffers.end() ; ++lIt )
581  {
582  // Don't copy more of page than was written to, for cases when less data received than expected
583  if ( lNrBytesCopied >= 4*lNrWordsInPacket)
584  break;
585 
586  size_t lNrBytesToCopy = std::min( lIt->second , uint32_t(4*lNrWordsInPacket - lNrBytesCopied) );
587  memcpy ( lIt->first, &lPageContents.at(1 + (lNrBytesCopied / 4)), lNrBytesToCopy );
588  lNrBytesCopied += lNrBytesToCopy;
589  }
590 
591 
592  // PART 3 : Validate the packet contents
593  try
594  {
595  if ( uhal::exception::exception* lExc = ClientInterface::validate ( lBuffers ) ) //Control of the pointer has been passed back to the client interface
596  {
597  mAsynchronousException = lExc;
598  }
599  }
600  catch ( exception::exception& aExc )
601  {
602  mAsynchronousException = new exception::ValidationError ();
603  log ( *mAsynchronousException , "Exception caught during reply validation for PCIe device with URI " , Quote ( this->uri() ) , "; what returned: " , Quote ( aExc.what() ) );
604  }
605 
607  {
608  mAsynchronousException->ThrowAsDerivedType();
609  }
610 }
611 
612 
613 } // end ns uhal
uint32_t mNumberOfPages
NoticeLevel Notice
Definition: LogLevels.cpp:96
uint32_t mPageSize
void returnBufferToPool(boost::shared_ptr< Buffers > &aBuffers)
Function to return a buffer to the buffer pool.
void connect()
Set up the connection to the device.
PacketFmt(const uint8_t *const, const size_t)
A class which provides the version-specific functionality for IPbus.
uint32_t getMaxSendSize()
Return the maximum size to be sent based on the buffer size in the target.
std::string mPath
void read()
Read next pending reply packet from appropriate page of FPGA-to-host device file, and validate conten...
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
minutes past the hour formatted as two digits e.g.
void setPath(const std::string &aPath)
uint32_t mReadReplyPageCount
const std::vector< std::pair< const uint8_t *, size_t > > mData
uint32_t mIndexNextPage
uint32_t mMaxPacketSize
WarningLevel Warning
Definition: LogLevels.cpp:79
File mDeviceFileFPGAToHost
FPGA-to-host device file.
An abstract base exception class providing an interface to a throw/ThrowAsDerivedType mechanism which...
Definition: exception.hpp:89
uint32_t mPublishedReplyPageCount
uhal::exception::exception * mAsynchronousException
A pointer to an exception object for passing exceptions from the worker thread to the main thread...
virtual void dispatchExceptionHandler()
Function which tidies up this protocol layer in the event of an exception.
void write(const uint32_t aAddr, const std::vector< uint32_t > &aValues)
ErrorLevel Error
Definition: LogLevels.cpp:61
virtual void Flush()
Concrete implementation of the synchronization function to block until all buffers have been sent...
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
bool mUseInterrupt
_Quote< T > Quote(const T &aT)
void createBuffer(const size_t aNrBytes)
virtual const char * what() const
Function which returns the error message associated with an exception If no error message has previou...
Definition: exception.cpp:116
File(const std::string &aPath, int aFlags)
File mDeviceFileHostToFPGA
Host-to-FPGA device file.
bool mXdma7seriesWorkaround
void write(const boost::shared_ptr< Buffers > &aBuffers)
Write request packet to next page in host-to-FPGA device file.
std::string uri
Definition: test_single.cpp:89
Hexadecimal.
uint32_t mMaxInFlight
File mDeviceFileFPGAEvent
FPGA-to-host interrupt (event) file.
DebugLevel Debug
Definition: LogLevels.cpp:133
void implementDispatch(boost::shared_ptr< Buffers > aBuffers)
Send the IPbus buffer to the target, read back the response and call the packing-protocol&#39;s 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...
uint32_t getMaxReplySize()
Return the maximum size of reply packet based on the buffer size in the target.
InfoLevel Info
Definition: LogLevels.cpp:114
void disconnect()
Close the connection to the device.
PCIe(const PCIe &aPCIe)
std::ostream & operator<<(std::ostream &aStr, const uhal::HttpResponseType &aHttpResponse)
const std::string & getPath() const
_Integer< T, IntFmt<> > Integer(const T &aT)
Forward declare a function which creates an instance of the ultra-lightweight wrapper from an integer...
Struct to store a URI when parsed by boost spirit.
Definition: URI.hpp:49
void read(const uint32_t aAddr, const uint32_t aNrWords, std::vector< uint32_t > &aValues)
boost::chrono::microseconds mSleepDuration
virtual ~PCIe()
Destructor.
std::deque< boost::shared_ptr< Buffers > > mReplyQueue
The list of buffers still awaiting a reply.