60#include <boost/lexical_cast.hpp>
61#include <boost/date_time/posix_time/posix_time_types.hpp>
117#define MAP_SIZE (32*1024UL + 16UL)
118#define MAP_MASK (MAP_SIZE - 1)
126 mFd = ::open(mPath.c_str(), mFlags);
128 exception::MmapInitialisationError lExc;
129 log(lExc,
"Failed to open device file ",
Quote(mPath),
"; errno=",
Integer(errno),
", meaning ",
Quote (strerror(errno)));
133 const off_t lPageSize = sysconf(_SC_PAGESIZE);
134 const off_t lPageBaseAddr = (mOffset & ~(lPageSize-1));
136 mMmapPtr = mmap(0,
MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, mFd, lPageBaseAddr);
137 mMmapIOPtr =
static_cast<uint8_t*
>(mMmapPtr) + (mOffset - lPageBaseAddr);
139 if (mMmapPtr == (
void *)-1) {
140 exception::MmapInitialisationError lExc;
141 log(lExc,
"Error occurred when mapping device file '" + mPath +
"' to memory; errno=",
Integer(errno),
", meaning ",
Quote (strerror(errno)));
149 if (mMmapPtr != NULL) {
150 if (munmap(mMmapPtr,
MAP_SIZE) == -1)
151 log (
Error() ,
"mmap client for ",
Quote(mPath),
" encountered error when unmapping memory" );
159 int rc = ::close(mFd);
167void Mmap::File::read(
const uint32_t aAddr,
const uint32_t aNrWords, std::vector<uint32_t>& aValues)
172 if (4 * aAddr + 4 * aNrWords >
size_t(
MAP_SIZE)) {
173 exception::MmapInitialisationError lExc;
174 log(lExc,
"Attempted to read beyond the end of mapped memory for device file '" + mPath +
"' (reading ",
Integer(4 * aNrWords),
" bytes from address ",
Integer(4 * aAddr),
", i.e. ",
Integer(uint32_t(4 * aAddr + 4 * aNrWords -
MAP_SIZE)),
" bytes beyond end of ",
Integer(uint32_t(
MAP_SIZE)),
" mapped bytes.");
178 std::ostringstream lMessage;
179 lMessage <<
"SIGBUS received during " << 4*aNrWords <<
"-byte read @ 0x" << std::hex << 4*aAddr <<
" in " << mPath;
182 uint8_t* lVirtAddr =
static_cast<uint8_t*
>(mMmapIOPtr) + off_t(4*aAddr);
184 for (
size_t i=0; i<aNrWords; i++) {
185 aValues.push_back(*((uint32_t*) (lVirtAddr + 4 * i)));
191void Mmap::File::write(
const uint32_t aAddr,
const std::vector<std::pair<const uint8_t*, size_t> >& aData)
197 for (
size_t i = 0; i < aData.size(); i++)
198 lNrBytes += aData.at(i).second;
200 assert((lNrBytes % 4) == 0);
202 if (aAddr + lNrBytes >
size_t(
MAP_SIZE)) {
203 exception::MmapInitialisationError lExc;
204 log(lExc,
"Attempted to write beyond the end of mapped memory for device file '" + mPath +
"' (writing ",
Integer(lNrBytes),
" bytes at address ",
Integer(aAddr),
", i.e. ",
Integer(uint32_t(aAddr + lNrBytes -
MAP_SIZE)),
" bytes beyond end of ",
Integer(uint32_t(
MAP_SIZE)),
" mapped bytes.");
208 char *allocated = NULL;
209 posix_memalign((
void **)&allocated, 4096, lNrBytes + 4096);
210 if (allocated == NULL) {
211 exception::MmapCommunicationError lExc;
212 log(lExc,
"Failed to allocate ",
Integer(lNrBytes + 4096),
" bytes in File::write/2 function");
216 std::ostringstream lMessage;
217 lMessage <<
"SIGBUS received during " << lNrBytes <<
"-byte write @ 0x" << std::hex << aAddr <<
" in " << mPath;
222 size_t lNrBytesCopied = 0;
223 for (
size_t i = 0; i < aData.size(); i++) {
224 memcpy(
buffer + lNrBytesCopied, aData.at(i).first, aData.at(i).second);
225 lNrBytesCopied += aData.at(i).second;
229 while (lNrBytesCopied < lNrBytes) {
230 char* lSrcPtr =
buffer + lNrBytesCopied;
231 char* lVirtAddr =
static_cast<char*
>(mMmapIOPtr) + aAddr + lNrBytesCopied;
232 if ((lNrBytes - lNrBytesCopied) >= 8) {
233 *((uint64_t*) lVirtAddr) = *(uint64_t*) lSrcPtr;
236 else if ((lNrBytes - lNrBytesCopied) >= 4) {
237 *((uint64_t*) lVirtAddr) = uint64_t(*(uint32_t*) lSrcPtr);
255 if ( flock(mFd, LOCK_EX) == -1 ) {
256 detail::MutexError lExc;
257 log(lExc,
"Failed to lock device file ",
Quote(mPath),
"; errno=",
Integer(errno),
", meaning ",
Quote (strerror(errno)));
266 if ( flock(mFd, LOCK_UN) == -1 ) {
276 std::string lSanitizedPath(aPath);
277 std::replace(lSanitizedPath.begin(), lSanitizedPath.end(),
'/',
':');
279 return "/uhal::ipbusmmap-2.0::" + lSanitizedPath;
284 IPbus< 2 , 0 > ( aId , aUri ),
299 if (lArg.first ==
"sleep") {
300 mSleepDuration = std::chrono::microseconds(boost::lexical_cast<size_t>(lArg.second));
301 log (
Notice() ,
"mmap client with URI ",
Quote (uri()),
" : Inter-poll-/-interrupt sleep duration set to ", boost::lexical_cast<size_t>(lArg.second),
" us by URI 'sleep' attribute");
303 else if (lArg.first ==
"offset") {
304 const bool lIsHex = (lArg.second.find(
"0x") == 0) or (lArg.second.find(
"0X") == 0);
305 const size_t lOffset = (lIsHex ? boost::lexical_cast<HexTo<size_t> >(lArg.second) : boost::lexical_cast<size_t>(lArg.second));
309 else if (lArg.first ==
"max_in_flight") {
310 mMaxInFlight = boost::lexical_cast<size_t>(lArg.second);
311 log (
Notice() ,
"mmap 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");
313 else if (lArg.first ==
"max_packet_size") {
315 log (
Notice() ,
"mmap 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");
332 log(
Debug(),
"mmap client ",
Quote(
id()),
": implementDispatch method called");
345 log(
Debug(),
"mmap client ",
Quote(
id()),
": Flush method called");
358 log(
Info(),
"mmap client ",
Quote(
id()),
" : Closing device files since exception detected");
366 InnerProtocol::dispatchExceptionHandler();
403 std::vector<uint32_t> lValues;
405 log (
Debug(),
"mmap 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()));
410 mPageSize = std::min(uint32_t(4096), lValues.at(1));
417 if (lValues.at(1) > 0xFFFF) {
418 exception::MmapInitialisationError lExc;
424 exception::MmapInitialisationError lExc;
430 log (
Info() ,
"mmap client ",
Quote(
id()),
": Connected to device at ",
Quote(
mDeviceFile.
getPath()),
"; FPGA has ",
Integer(
mNumberOfPages),
" pages, each of size ",
Integer(
mPageSize),
" words, index ",
Integer(
mIndexNextPage),
" should be filled next (URI: ",
Quote(uri()),
")" );
459 const uint32_t lHeaderWord = (0x10000 | (((aBuffers->sendCounter() / 4) - 1) & 0xFFFF));
460 std::vector<std::pair<const uint8_t*, size_t> > lDataToWrite;
461 lDataToWrite.push_back( std::make_pair(
reinterpret_cast<const uint8_t*
>(&lHeaderWord),
sizeof lHeaderWord) );
462 lDataToWrite.push_back( std::make_pair(aBuffers->getSendBuffer(), aBuffers->sendCounter()) );
476 SteadyClock_t::time_point lStartTime = SteadyClock_t::now();
480 uint32_t lHwPublishedPageCount = 0x0;
482 std::vector<uint32_t> lValues;
487 lHwPublishedPageCount = lValues.at(3);
488 log (
Debug(),
"mmap 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()));
496 if (SteadyClock_t::now() - lStartTime > std::chrono::microseconds(getBoostTimeoutPeriod().total_microseconds())) {
497 exception::MmapTimeout lExc;
502 log(
Debug(),
"mmap 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");
508 log(
Info(),
"mmap client ",
Quote(
id()),
": Reading page ",
Integer(lPageIndexToRead),
" (published count ",
Integer(lHwPublishedPageCount),
", surpasses required, ",
Integer(
mReadReplyPageCount + 1),
")");
513 std::shared_ptr<Buffers> lBuffers =
mReplyQueue.front();
516 uint32_t lNrWordsToRead(lBuffers->replyCounter() >> 2);
519 std::vector<uint32_t> lPageContents;
526 const std::deque< std::pair< uint8_t* , uint32_t > >& lReplyBuffers ( lBuffers->getReplyBuffer() );
527 size_t lNrWordsInPacket = (lPageContents.at(0) >> 16) + (lPageContents.at(0) & 0xFFFF);
528 if (lNrWordsInPacket != (lBuffers->replyCounter() >> 2))
529 log (
Warning(),
"mmap client ",
Quote(
id()),
": Expected reply packet to contain ",
Integer(lBuffers->replyCounter() >> 2),
" words, but it actually contains ",
Integer(lNrWordsInPacket),
" words");
531 size_t lNrBytesCopied = 0;
532 for (
const auto& lBuffers: lReplyBuffers)
535 if ( lNrBytesCopied >= 4*lNrWordsInPacket)
538 size_t lNrBytesToCopy = std::min( lBuffers.second , uint32_t(4*lNrWordsInPacket - lNrBytesCopied) );
539 memcpy ( lBuffers.first, &lPageContents.at(1 + (lNrBytesCopied / 4)), lNrBytesToCopy );
540 lNrBytesCopied += lNrBytesToCopy;
552 exception::ValidationError lExc2;
553 log ( lExc2 ,
"Exception caught during reply validation for mmap 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 write(const uint32_t aAddr, const std::vector< std::pair< const uint8_t *, size_t > > &aData)
const std::string & getPath() const
void setOffset(size_t aOffset)
File(const std::string &aPath, int aFlags)
void setPath(const std::string &aPath)
void read(const uint32_t aAddr, const uint32_t aNrWords, std::vector< uint32_t > &aValues)
std::chrono::microseconds mSleepDuration
virtual void Flush()
Concrete implementation of the synchronization function to block until all buffers have been sent,...
void disconnect()
Close the connection to the device.
uint32_t mReadReplyPageCount
std::deque< std::shared_ptr< Buffers > > mReplyQueue
The list of buffers still awaiting a reply.
uint64_t mIPCSessionCount
static std::string getSharedMemName(const std::string &)
bool mIPCExternalSessionActive
void write(const std::shared_ptr< Buffers > &aBuffers)
Write request packet to next page in host-to-FPGA device file.
virtual ~Mmap()
Destructor.
void connect()
Set up the connection to the device.
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 ...
void read()
Read next pending reply packet from appropriate page of FPGA-to-host device file, and validate conten...
uint32_t getMaxSendSize()
Return the maximum size to be sent based on the buffer size in the target.
virtual void dispatchExceptionHandler()
Function which tidies up this protocol layer in the event of an exception.
uint32_t getMaxReplySize()
Return the maximum size of reply packet based on the buffer size in the target.
uint32_t mPublishedReplyPageCount
detail::SharedObject< detail::RobustSessionMutex > mIPCMutex
void protect(const std::function< void()> &, const std::string &)
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.
Empty struct which acts as a dummy variable for passing the formatting information around.
Struct to store a URI when parsed by boost spirit.
NameValuePairVectorType mArguments
The "key1=val1&key2=val2&key3=val3" part of a URI of the form "protocol://host:port/patha/pathb/blah....