61#include <boost/lexical_cast.hpp>
62#include <boost/date_time/posix_time/posix_time_types.hpp>
111 mFd = ::open(mPath.c_str(), mFlags);
113 exception::PCIeInitialisationError lExc;
114 log(lExc,
"Failed to open device file ",
Quote(mPath),
"; errno=",
Integer(errno),
", meaning ",
Quote (strerror(errno)));
125 int rc = ::close(mFd);
136 if (mBuffer != NULL) {
137 if (mBufferSize >= aNrBytes)
146 posix_memalign((
void**)&mBuffer, 4096, aNrBytes + 4096);
147 if (mBuffer == NULL) {
148 exception::PCIeCommunicationError lExc;
149 log(lExc,
"Failed to allocate ",
Integer(aNrBytes + 4096),
" bytes in PCIe::File::createBuffer");
153 mBufferSize=aNrBytes+4096;
157void PCIe::File::read(
const uint32_t aAddr,
const uint32_t aNrWords, std::vector<uint32_t>& aValues)
162 createBuffer(4 * aNrWords);
165 off_t off = lseek(mFd, 4*aAddr, SEEK_SET);
166 if ( off != off_t(4 * aAddr)) {
167 exception::PCIeCommunicationError lExc;
168 log(lExc,
"Offset returned by lseek, ",
Integer(off),
", does not match that requested, ",
Integer(4*aAddr),
" (in preparation for read of ",
Integer(aNrWords),
" words)");
173 int rc =
::read(mFd, mBuffer, 4*aNrWords);
175 exception::PCIeCommunicationError lExc;
176 log(lExc,
"Read of ",
Integer(4*aNrWords),
" bytes at address ",
Integer(4 * aAddr),
" failed! errno=",
Integer(errno),
", meaning ",
Quote (strerror(errno)));
179 else if (
size_t(rc) < 4*aNrWords) {
180 exception::PCIeCommunicationError lExc;
181 log(lExc,
"Only ",
Integer(rc),
" bytes transferred in read of ",
Integer(4*aNrWords),
" bytes at address ",
Integer(4 * aAddr));
185 aValues.insert(aValues.end(),
reinterpret_cast<uint32_t*
>(mBuffer),
reinterpret_cast<uint32_t*
>(mBuffer)+ aNrWords);
191 write(4 * aAddr,
reinterpret_cast<const uint8_t*
>(aValues.data()), 4 * aValues.size());
200 assert((aNrBytes % 4) == 0);
202 createBuffer(aNrBytes);
205 memcpy(mBuffer, aPtr, aNrBytes);
208 off_t off = lseek(mFd, aAddr, SEEK_SET);
209 if ( off != off_t(aAddr)) {
211 if (fstat(mFd, &st) or (not S_ISFIFO(st.st_mode))) {
212 exception::PCIeCommunicationError lExc;
213 log(lExc,
"Offset returned by lseek, ",
Integer(off),
", does not match that requested, ",
Integer(aAddr),
" (in preparation for write of ",
Integer(aNrBytes),
" bytes)");
219 int rc =
::write(mFd, mBuffer, aNrBytes);
221 exception::PCIeCommunicationError lExc;
222 log(lExc,
"Write of ",
Integer(aNrBytes),
" bytes at address ",
Integer(aAddr),
" failed! errno=",
Integer(errno),
", meaning ",
Quote (strerror(errno)));
225 else if (
size_t(rc) < aNrBytes) {
226 exception::PCIeCommunicationError lExc;
227 log(lExc,
"Only ",
Integer(rc),
" bytes transferred in write of ",
Integer(aNrBytes),
" bytes at address ",
Integer(aAddr));
233void PCIe::File::write(
const uint32_t aAddr,
const std::vector<std::pair<const uint8_t*, size_t> >& aData)
239 for (
size_t i = 0; i < aData.size(); i++)
240 lNrBytes += aData.at(i).second;
242 assert((lNrBytes % 4) == 0);
244 createBuffer(lNrBytes);
247 size_t lNrBytesCopied = 0;
248 for (
size_t i = 0; i < aData.size(); i++) {
249 memcpy(mBuffer + lNrBytesCopied, aData.at(i).first, aData.at(i).second);
250 lNrBytesCopied += aData.at(i).second;
254 off_t off = lseek(mFd, aAddr, SEEK_SET);
255 if ( off != off_t(aAddr)) {
257 if (fstat(mFd, &st) or (not S_ISFIFO(st.st_mode))) {
258 exception::PCIeCommunicationError lExc;
259 log(lExc,
"Offset returned by lseek, ",
Integer(off),
", does not match that requested, ",
Integer(aAddr),
" (in preparation for write of ",
Integer(lNrBytes),
" bytes)");
265 int rc =
::write(mFd, mBuffer, lNrBytes);
267 exception::PCIeCommunicationError lExc;
268 log(lExc,
"Write of ",
Integer(lNrBytes),
" bytes at address ",
Integer(aAddr),
" failed! errno=",
Integer(errno),
", meaning ",
Quote (strerror(errno)));
271 else if (
size_t(rc) < lNrBytes) {
272 exception::PCIeCommunicationError lExc;
273 log(lExc,
"Only ",
Integer(rc),
" bytes transferred in write of ",
Integer(lNrBytes),
" bytes at address ",
Integer(aAddr));
287 if ( flock(mFd, LOCK_EX) == -1 ) {
288 detail::MutexError lExc;
289 log(lExc,
"Failed to lock device file ",
Quote(mPath),
"; errno=",
Integer(errno),
", meaning ",
Quote (strerror(errno)));
298 if ( flock(mFd, LOCK_UN) == -1 ) {
308 std::string lSanitizedPath(aPath);
309 std::replace(lSanitizedPath.begin(), lSanitizedPath.end(),
'/',
':');
311 return "/uhal::ipbuspcie-2.0::" + lSanitizedPath;
316 IPbus< 2 , 0 > ( aId , aUri ),
332 if ( aUri.
mHostname.find(
",") == std::string::npos ) {
333 exception::PCIeInitialisationError lExc;
334 log(lExc,
"No comma found in hostname of PCIe client URI '" + uri() +
"'; cannot construct 2 paths for device files");
338 exception::PCIeInitialisationError lExc;
339 log(lExc,
"Hostname of PCIe client URI '" + uri() +
"' starts/ends with a comma; cannot construct 2 paths for device files");
346 if (lArg.first ==
"events") {
348 exception::PCIeInitialisationError lExc;
349 log(lExc,
"PCIe client URI ",
Quote(uri()),
": 'events' attribute is specified multiple times");
355 log (
Info() ,
"PCIe client with URI ",
Quote (uri()),
" is configured to use interrupts");
357 else if (lArg.first ==
"sleep") {
358 mSleepDuration = std::chrono::microseconds(boost::lexical_cast<size_t>(lArg.second));
359 log (
Notice() ,
"PCIe client with URI ",
Quote (uri()),
" : Inter-poll-/-interrupt sleep duration set to ", boost::lexical_cast<size_t>(lArg.second),
" us by URI 'sleep' attribute");
361 else if (lArg.first ==
"max_in_flight") {
362 mMaxInFlight = boost::lexical_cast<size_t>(lArg.second);
363 log (
Notice() ,
"PCIe client with URI ",
Quote (uri()),
" : 'Maximum number of packets in flight' set to ", boost::lexical_cast<size_t>(lArg.second),
" by URI 'max_in_flight' attribute");
365 else if (lArg.first ==
"max_packet_size") {
367 log (
Notice() ,
"PCIe client with URI ",
Quote (uri()),
" : 'Maximum packet size (in 32-bit words) set to ", boost::lexical_cast<size_t>(lArg.second),
" by URI 'max_packet_size' attribute");
369 else if (lArg.first ==
"xdma_7series_workaround") {
371 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");
387 log(
Debug(),
"PCIe client ",
Quote(
id()),
": implementDispatch method called");
400 log(
Debug(),
"PCIe client ",
Quote(
id()),
": Flush method called");
413 log(
Info(),
"PCIe client ",
Quote(
id()),
": closing device files since exception detected");
421 InnerProtocol::dispatchExceptionHandler();
460 std::vector<uint32_t> lValues;
463 log (
Debug(),
"PCIe client ",
Quote(
id()),
": Read status info from addr 0 (",
Integer(lValues.at(0)),
", ",
Integer(lValues.at(1)),
", ",
Integer(lValues.at(2)),
", ",
Integer(lValues.at(3)),
"): ",
detail::PacketFmt((
const uint8_t*)lValues.data(), 4 * lValues.size()));
475 if (lValues.at(1) > 0xFFFF) {
476 exception::PCIeInitialisationError lExc;
482 exception::PCIeInitialisationError lExc;
497 log (
Info() ,
"PCIe client ",
Quote(
id()),
": 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" );
528 const uint32_t lHeaderWord = (0x10000 | (((aBuffers->sendCounter() / 4) - 1) & 0xFFFF));
529 std::vector<std::pair<const uint8_t*, size_t> > lDataToWrite;
530 lDataToWrite.push_back( std::make_pair(
reinterpret_cast<const uint8_t*
>(&lHeaderWord),
sizeof lHeaderWord) );
531 lDataToWrite.push_back( std::make_pair(aBuffers->getSendBuffer(), aBuffers->sendCounter()) );
545 SteadyClock_t::time_point lStartTime = SteadyClock_t::now();
551 std::vector<uint32_t> lRxEvent;
555 if (lRxEvent.at(0) == 1) {
560 if (SteadyClock_t::now() - lStartTime > std::chrono::microseconds(getBoostTimeoutPeriod().total_microseconds())) {
561 exception::PCIeTimeout lExc;
572 log(
Info(),
"PCIe client ",
Quote(
id()),
": Reading page ",
Integer(lPageIndexToRead),
" (interrupt received)");
576 uint32_t lHwPublishedPageCount = 0x0;
578 std::vector<uint32_t> lValues;
583 lHwPublishedPageCount = lValues.at(3);
584 log (
Debug(),
"Read status info from addr 0 (",
Integer(lValues.at(0)),
", ",
Integer(lValues.at(1)),
", ",
Integer(lValues.at(2)),
", ",
Integer(lValues.at(3)),
"): ",
detail::PacketFmt((
const uint8_t*)lValues.data(), 4 * lValues.size()));
592 if (SteadyClock_t::now() - lStartTime > std::chrono::microseconds(getBoostTimeoutPeriod().total_microseconds())) {
593 exception::PCIeTimeout lExc;
598 log(
Debug(),
"PCIe client ",
Quote(
id()),
": Trying to read page index ",
Integer(lPageIndexToRead),
" = count ",
Integer(
mReadReplyPageCount+1),
"; published page count is ",
Integer(lHwPublishedPageCount),
"; sleeping for ",
mSleepDuration.count(),
"us");
604 log(
Info(),
"PCIe client ",
Quote(
id()),
": Reading page ",
Integer(lPageIndexToRead),
" (published count ",
Integer(lHwPublishedPageCount),
", surpasses required, ",
Integer(
mReadReplyPageCount + 1),
")");
610 std::shared_ptr<Buffers> lBuffers =
mReplyQueue.front();
613 uint32_t lNrWordsToRead(lBuffers->replyCounter() >> 2);
614 if(
mXdma7seriesWorkaround and (lNrWordsToRead % 32 == 0 || lNrWordsToRead % 32 == 28 || lNrWordsToRead < 4))
618 std::vector<uint32_t> lPageContents;
625 const std::deque< std::pair< uint8_t* , uint32_t > >& lReplyBuffers ( lBuffers->getReplyBuffer() );
626 size_t lNrWordsInPacket = (lPageContents.at(0) >> 16) + (lPageContents.at(0) & 0xFFFF);
627 if (lNrWordsInPacket != (lBuffers->replyCounter() >> 2))
628 log (
Warning(),
"PCIe client ",
Quote(
id()),
": Expected reply packet to contain ",
Integer(lBuffers->replyCounter() >> 2),
" words, but it actually contains ",
Integer(lNrWordsInPacket),
" words");
630 size_t lNrBytesCopied = 0;
631 for (
const auto& lBuffer: lReplyBuffers)
634 if ( lNrBytesCopied >= 4*lNrWordsInPacket)
637 size_t lNrBytesToCopy = std::min( lBuffer.second , uint32_t(4*lNrWordsInPacket - lNrBytesCopied) );
638 memcpy ( lBuffer.first, &lPageContents.at(1 + (lNrBytesCopied / 4)), lNrBytesToCopy );
639 lNrBytesCopied += lNrBytesToCopy;
651 exception::ValidationError lExc2;
652 log ( lExc2 ,
"Exception caught during reply validation for PCIe device with URI " ,
Quote ( this->uri() ) ,
"; what returned: " ,
Quote ( aExc.
what() ) );
void returnBufferToPool(std::shared_ptr< Buffers > &aBuffers)
Function to return a buffer to the buffer pool.
virtual exception::exception * validate(std::shared_ptr< Buffers > aBuffers)
Function which dispatch calls when the reply is received to check that the headers are as expected.
A class which provides the version-specific functionality for IPbus.
void setPath(const std::string &aPath)
void createBuffer(const size_t aNrBytes)
void write(const uint32_t aAddr, const std::vector< uint32_t > &aValues)
const std::string & getPath() const
void read(const uint32_t aAddr, const uint32_t aNrWords, std::vector< uint32_t > &aValues)
File(const std::string &aPath, int aFlags)
void read()
Read next pending reply packet from appropriate page of FPGA-to-host device file, and validate conten...
File mDeviceFileFPGAToHost
FPGA-to-host device file.
uint64_t mIPCSessionCount
PCIe(const std::string &aId, const URI &aUri)
Constructor.
virtual void Flush()
Concrete implementation of the synchronization function to block until all buffers have been sent,...
uint32_t getMaxReplySize()
Return the maximum size of reply packet based on the buffer size in the target.
std::deque< std::shared_ptr< Buffers > > mReplyQueue
The list of buffers still awaiting a reply.
std::chrono::microseconds mSleepDuration
void connect()
Set up the connection to the device.
bool mXdma7seriesWorkaround
virtual void dispatchExceptionHandler()
Function which tidies up this protocol layer in the event of an exception.
bool mIPCExternalSessionActive
File mDeviceFileFPGAEvent
FPGA-to-host interrupt (event) file.
void disconnect()
Close the connection to the device.
uint32_t getMaxSendSize()
Return the maximum size to be sent based on the buffer size in the target.
static std::string getSharedMemName(const std::string &)
virtual ~PCIe()
Destructor.
detail::SharedObject< detail::RobustSessionMutex > mIPCMutex
uint32_t mPublishedReplyPageCount
void implementDispatch(std::shared_ptr< Buffers > aBuffers)
Send the IPbus buffer to the target, read back the response and call the packing-protocol's validate ...
uint32_t mReadReplyPageCount
void write(const std::shared_ptr< Buffers > &aBuffers)
Write request packet to next page in host-to-FPGA device file.
File mDeviceFileHostToFPGA
Host-to-FPGA device file.
Class used to display IPbus packet contents in human-readable format (e.g. in log messages)
An abstract base exception class, including an interface to throw as the derived type (for passing ex...
virtual const char * what() const
Function which returns the error message associated with an exception If no error message has previou...
virtual void throwAsDerivedType()=0
Function which casts a pointer from the base type of this object to a derived type of this object and...
std::unique_lock< RobustSessionMutex > ScopedSessionLock
_Quote< T > Quote(const T &aT)
_Integer< T, IntFmt<> > Integer(const T &aT)
Forward declare a function which creates an instance of the ultra-lightweight wrapper from an integer...
void log(FatalLevel &aFatal, const T0 &aArg0)
Function to add a log entry at Fatal level.
Struct to store a URI when parsed by boost spirit.
std::string mHostname
The "host" part of a URI of the form "protocol://host:port/patha/pathb/blah.ext?key1=val1&key2=val2&k...
NameValuePairVectorType mArguments
The "key1=val1&key2=val2&key3=val3" part of a URI of the form "protocol://host:port/patha/pathb/blah....