LCOV - code coverage report
Current view: top level - uhallibs/src - ProtocolAxi4Lite.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 0.0 % 263 0
Test Date: 2025-12-21 13:07:08 Functions: 0.0 % 30 0

            Line data    Source code
       1              : #include "uhallibs/ProtocolAxi4Lite.hpp"
       2              : 
       3              : #include <fcntl.h>
       4              : #include <sys/file.h>
       5              : #include <sys/mman.h>
       6              : #include <unistd.h>
       7              : #include <thread>
       8              : 
       9              : #include "uhallibs/formatters.hpp"
      10              : 
      11              : #include "uhal/ClientFactory.hpp"
      12              : #include "uhal/Buffers.hpp"
      13              : 
      14              : using uhal::Integer;
      15              : 
      16              : UHAL_REGISTER_EXTERNAL_CLIENT(uhallibs::Axi4Lite, "ipbusaxi4lite-2.0", "AXI4 Lite IPBus client")
      17              : // Syntax: ipbus-qdma-axi4l-2.0:///sys/bus/pci/devices/0000:<bus>:<dev>.<func>/resource<bar#>
      18              : // Syntax: ipbusaxi4lite-2.0://<bus>:<dev>.<func>/resource<bar#>
      19              : // 
      20              : namespace uhallibs {
      21              : 
      22              : 
      23            0 : Axi4Lite::MappedFile::MappedFile(const std::string& aPath, size_t aLength, int aProtFlags)
      24            0 :     : mPath(aPath),
      25            0 :       mFd(-1),
      26            0 :       mBar(nullptr),
      27            0 :       mLength(aLength),
      28            0 :       mProtFlags(aProtFlags),
      29            0 :       mLocked(false),
      30            0 :       mBufferSize(0),
      31            0 :       mBuffer(nullptr) {}
      32              : 
      33            0 : Axi4Lite::MappedFile::~MappedFile() {
      34            0 :   if (mBuffer != nullptr) free(mBuffer);
      35            0 :   close();
      36            0 : }
      37              : 
      38            0 : const std::string& Axi4Lite::MappedFile::getPath() const { return mPath; }
      39              : 
      40            0 : void Axi4Lite::MappedFile::setPath(const std::string& aPath) { mPath = aPath; }
      41            0 : void Axi4Lite::MappedFile::setLength(size_t aLength) { mLength = aLength; }
      42              : 
      43            0 : void Axi4Lite::MappedFile::open() {
      44            0 :   if (mBar != nullptr) return;
      45              : 
      46            0 :   mFd = ::open(mPath.c_str(), (mProtFlags & PROT_WRITE) ? O_RDWR : O_RDONLY);
      47            0 :   if (mFd < 0) {
      48              :     return;
      49              :   }
      50              : 
      51            0 :   void* lBar = mmap(nullptr, 4*mLength, mProtFlags, MAP_SHARED, mFd, 0);
      52            0 :   mBar = (lBar == MAP_FAILED ? nullptr : (uint32_t*)lBar);
      53              : }
      54              : 
      55            0 : void Axi4Lite::MappedFile::close() {
      56            0 :   if (mBar != nullptr) munmap(mBar, mLength);
      57            0 :   mBar = nullptr;
      58              : 
      59            0 :   if (mFd != -1) {
      60            0 :     if (haveLock())
      61            0 :       unlock();
      62            0 :     int rc = ::close(mFd);
      63            0 :     mFd = -1;
      64            0 :     if (rc == -1)
      65            0 :       log (uhal::Error(), "Failed to close file ", uhal::Quote(mPath), "; errno=", uhal::Integer(errno), ", meaning ", uhal::Quote (strerror(errno)));
      66              :   }
      67            0 : }
      68              : 
      69              : 
      70            0 : void Axi4Lite::MappedFile::createBuffer(const size_t aNrBytes) {
      71            0 :   if (mBuffer != NULL) {
      72            0 :     if (mBufferSize >= aNrBytes)
      73              :       return;
      74              :     else {
      75            0 :       free(mBuffer);
      76            0 :       mBuffer = NULL;
      77            0 :       mBufferSize = 0;
      78              :     }
      79              :   }
      80              : 
      81            0 :   posix_memalign((void**)&mBuffer, 4096 /*alignment*/, aNrBytes + 4096);
      82            0 :   if (mBuffer == NULL) {
      83            0 :     exception::Axi4LiteCommunicationError lExc;
      84            0 :     log(lExc, "Failed to allocate ", Integer(aNrBytes + 4096),
      85              :         " bytes in Axi4Lite::MappedFile::createBuffer");
      86            0 :     throw lExc;
      87            0 :   }
      88              : 
      89            0 :   mBufferSize = aNrBytes + 4096;
      90              : }
      91              : 
      92            0 : void Axi4Lite::MappedFile::read(const uint32_t aAddr, const uint32_t aNrWords,
      93              :                 std::vector<uint32_t>& aValues) {
      94            0 :   if (mBar == nullptr) open();
      95              : 
      96            0 :   for (size_t i(0); i < aNrWords; ++i) {
      97            0 :     aValues.push_back(le32toh(mBar[aAddr + i]));
      98              :   }
      99            0 : }
     100              : 
     101            0 : void Axi4Lite::MappedFile::write(const uint32_t aAddr, const std::vector<uint32_t>& aValues) {
     102            0 :   write(aAddr, reinterpret_cast<const uint8_t*>(aValues.data()),
     103            0 :         4 * aValues.size());
     104            0 : }
     105              : 
     106            0 : void Axi4Lite::MappedFile::write(const uint32_t aAddr, const uint8_t* const aPtr,
     107              :                  const size_t aNrBytes) {
     108            0 :   if (mBar == nullptr) open();
     109              : 
     110            0 :   assert((aNrBytes % 4) == 0);
     111            0 :   uint32_t lNrWordsData = aNrBytes / 4;
     112              : 
     113            0 :   auto lPtr32 = reinterpret_cast<const uint32_t*>(aPtr);
     114            0 :   for (size_t i(0); i < lNrWordsData; ++i) {
     115            0 :     mBar[aAddr + i] = lPtr32[i];
     116              :   }
     117            0 : }
     118              : 
     119            0 : void Axi4Lite::MappedFile::write(const uint32_t aAddr,
     120              :                  const std::vector<std::pair<const uint8_t*, size_t> >& aData) {
     121            0 :   if (mBar == nullptr) open();
     122              : 
     123              :   size_t lNrBytes = 0;
     124            0 :   for (size_t i = 0; i < aData.size(); i++) lNrBytes += aData.at(i).second;
     125              : 
     126            0 :   assert((lNrBytes % 4) == 0);
     127            0 :   size_t lNrWords = lNrBytes/4;
     128              : 
     129            0 :   createBuffer(lNrBytes);
     130              : 
     131            0 :   size_t k(0);
     132            0 :   for (size_t i = 0; i < aData.size(); ++i) {
     133            0 :     for (size_t j = 0; j < aData.at(i).second; ++j) {
     134            0 :       mBuffer[k] = aData.at(i).first[j];
     135            0 :       ++k;
     136              :     }
     137              :   }
     138              : 
     139            0 :   auto mBuffer32b = reinterpret_cast<const uint32_t*>(mBuffer);
     140              : 
     141            0 :   for (size_t i(0); i<lNrWords; ++i) {
     142            0 :     mBar[aAddr + i] = htole32(mBuffer32b[i]);
     143              :   }
     144              :   // std::memcpy(mBar + aAddr, mBuffer, lNrBytes);
     145              : 
     146            0 : }
     147              : 
     148            0 : bool Axi4Lite::MappedFile::haveLock() const { return mLocked; }
     149              : 
     150            0 : void Axi4Lite::MappedFile::lock() {
     151            0 :   if (flock(mFd, LOCK_EX) == -1) {
     152            0 :     ipc::exception::MutexError lExc;
     153            0 :     log(lExc, "Failed to lock device file ", uhal::Quote(mPath),
     154            0 :         "; errno=", Integer(errno), ", meaning ", uhal::Quote(strerror(errno)));
     155            0 :     throw lExc;
     156            0 :   }
     157            0 :   mLocked = true;
     158            0 : }
     159              : 
     160            0 : void Axi4Lite::MappedFile::unlock() {
     161            0 :   if (flock(mFd, LOCK_UN) == -1) {
     162            0 :     log(uhal::Warning(), "Failed to unlock device file ", uhal::Quote(mPath),
     163            0 :         "; errno=", Integer(errno), ", meaning ", uhal::Quote(strerror(errno)));
     164              :   } else
     165            0 :     mLocked = false;
     166            0 : }
     167              : 
     168              : // Axi4Lite Transport
     169            0 : std::string Axi4Lite::getSharedMemName(const std::string& aPath)
     170              : {
     171            0 :   std::string lSanitizedPath(aPath);
     172            0 :   std::replace(lSanitizedPath.begin(), lSanitizedPath.end(), '/', ':');
     173              : 
     174            0 :   return "/uhal::ipbusaxi4lite-2.0::" + lSanitizedPath;
     175            0 : }
     176              : 
     177            0 : std::string Axi4Lite::getDevicePath(const uhal::URI& aUri) {
     178              : 
     179            0 :   std::string lPath = aUri.mHostname;
     180              : 
     181            0 :   auto it = std::find_if( aUri.mArguments.begin(), aUri.mArguments.end(),
     182            0 :     [](const std::pair<std::string, std::string>& element){ return element.first == "dev";} );
     183              : 
     184            0 :   if ( it != aUri.mArguments.end() ) {
     185            0 :     lPath += "/"+it->second;
     186              :   }
     187              : 
     188            0 :   return lPath;
     189            0 : }
     190              : 
     191            0 : Axi4Lite::Axi4Lite(const std::string& aId, const uhal::URI& aUri)
     192              :     : IPbus<2, 0>(aId, aUri),
     193            0 :       mConnected(false),
     194            0 :       mMappedFile(getDevicePath(aUri), 64, PROT_WRITE),
     195            0 :       mIPCMutex(getSharedMemName(mMappedFile.getPath())),
     196            0 :       mNumberOfPages(0),
     197            0 :       mMaxInFlight(0),
     198            0 :       mPageSize(0),
     199            0 :       mMaxPacketSize(0),
     200            0 :       mIndexNextPage(0),
     201            0 :       mPublishedReplyPageCount(0),
     202            0 :       mReadReplyPageCount(0) {
     203              : 
     204            0 :         mSleepDuration = std::chrono::microseconds(50);
     205              : 
     206            0 :       }
     207              : 
     208            0 : Axi4Lite::~Axi4Lite() {
     209              : 
     210            0 : }
     211              : 
     212              : 
     213              : 
     214            0 : void Axi4Lite::implementDispatch ( std::shared_ptr< uhal::Buffers > aBuffers )
     215              : {
     216            0 :   log(uhal::Debug(), "Axi4Lite client (URI: ", uhal::Quote(uri()), ") : implementDispatch method called");
     217              : 
     218            0 :   if ( ! mConnected )
     219            0 :     connect();
     220              : 
     221            0 :   if ( mReplyQueue.size() == mMaxInFlight )
     222            0 :     read();
     223            0 :   write(aBuffers);
     224            0 : }
     225              : 
     226              : 
     227            0 : void Axi4Lite::Flush( )
     228              : {
     229            0 :   log(uhal::Debug(), "Axi4Lite client (URI: ", uhal::Quote(uri()), ") : Flush method called");
     230            0 :   while ( !mReplyQueue.empty() )
     231            0 :     read();
     232              : 
     233            0 :   mMappedFile.unlock();
     234              : 
     235            0 :   IPCScopedLock_t lLockGuard(*mIPCMutex);
     236            0 :   mIPCMutex->endSession();
     237            0 : }
     238              : 
     239              : 
     240            0 : void Axi4Lite::dispatchExceptionHandler()
     241              : {
     242            0 :   log(uhal::Notice(), "Axi4Lite client ", uhal::Quote(id()), " (URI: ", uhal::Quote(uri()), ") : closing device files since exception detected");
     243              : 
     244            0 :   ClientInterface::returnBufferToPool ( mReplyQueue );
     245              : 
     246            0 :   mMappedFile.unlock();
     247              : 
     248            0 :   disconnect();
     249              : 
     250            0 :   InnerProtocol::dispatchExceptionHandler();
     251            0 : }
     252              : 
     253              : 
     254            0 : uint32_t Axi4Lite::getMaxSendSize()
     255              : {
     256            0 :   if ( ! mConnected )
     257            0 :     connect();
     258              : 
     259            0 :   return mMaxPacketSize * 4;
     260              : }
     261              : 
     262              : 
     263            0 : uint32_t Axi4Lite::getMaxReplySize()
     264              : {
     265            0 :   if ( ! mConnected )
     266            0 :     connect();
     267              : 
     268            0 :   return mMaxPacketSize * 4;
     269              : }
     270              : 
     271              : 
     272            0 : void Axi4Lite::connect()
     273              : {
     274            0 :   IPCScopedLock_t lLockGuard(*mIPCMutex);
     275            0 :   connect(lLockGuard);
     276            0 : }
     277              : 
     278            0 : void Axi4Lite::connect(IPCScopedLock_t& aGuard)
     279              : {
     280              :   // Read current value of session counter when reading status info from FPGA
     281              :   // (So that can check whether this info is up-to-date later on, when sending next request packet)
     282            0 :   mIPCExternalSessionActive = mIPCMutex->isActive() and (not mMappedFile.haveLock());
     283            0 :   mIPCSessionCount = mIPCMutex->getCounter();
     284              : 
     285            0 :   log ( uhal::Debug() , "Axi4Lite client is opening device file " , uhal::Quote ( mMappedFile.getPath() ) , " (device-to-client)" );
     286              : 
     287              :   // Minimal mapping to read the 
     288            0 :   mMappedFile.setLength(0x10);
     289            0 :   mMappedFile.open();
     290            0 :   std::vector<uint32_t> lStats;
     291            0 :   mMappedFile.read(0, 4, lStats);
     292            0 :   mMappedFile.close();
     293            0 :   aGuard.unlock();
     294              : 
     295            0 :   mNumberOfPages = lStats.at(0);
     296            0 :   if ( (mMaxInFlight == 0) or (mMaxInFlight > mNumberOfPages) )
     297            0 :     mMaxInFlight = mNumberOfPages;
     298            0 :   mPageSize = lStats.at(1);
     299            0 :   if ( (mMaxPacketSize == 0) or (mMaxPacketSize >= mPageSize) )
     300            0 :     mMaxPacketSize = mPageSize - 1;
     301            0 :   mIndexNextPage = lStats.at(2);
     302            0 :   mPublishedReplyPageCount = lStats.at(3);
     303            0 :   mReadReplyPageCount = mPublishedReplyPageCount;
     304              : 
     305              :   // 
     306            0 :   constexpr uint32_t lSafetyMargin(4096);
     307              :   // Set the memory mapping range to a value commensurate to the memory available in firmware
     308            0 :   mMappedFile.setLength(mNumberOfPages*mPageSize+4+lSafetyMargin);
     309            0 :   mMappedFile.open();
     310              : 
     311            0 :   mConnected=true;
     312              : 
     313            0 :   log ( uhal::Info() , "Axi4Lite client connected to device at ", uhal::Quote(mMappedFile.getPath()), ", FPGA has ", Integer(mNumberOfPages), " pages, each of size ", Integer(mPageSize), " words, index ", Integer(mIndexNextPage), " should be filled next" );
     314              : 
     315            0 : }
     316              : 
     317            0 : void Axi4Lite::disconnect()
     318              : {
     319            0 :   mMappedFile.close();
     320            0 :   mConnected = false;
     321            0 : }
     322              : 
     323            0 : void Axi4Lite::write(const std::shared_ptr<uhal::Buffers>& aBuffers)
     324              : {
     325            0 :   if (not mMappedFile.haveLock()) {
     326            0 :     mMappedFile.lock();
     327              : 
     328            0 :     IPCScopedLock_t lGuard(*mIPCMutex);
     329            0 :     mIPCMutex->startSession();
     330            0 :     mIPCSessionCount++;
     331              : 
     332              :     // If these two numbers don't match, another client/process has sent packets
     333              :     // more recently than this client has, so must re-read status info
     334            0 :     if (mIPCExternalSessionActive or (mIPCMutex->getCounter() != mIPCSessionCount)) {
     335            0 :       connect(lGuard);
     336              :     }
     337            0 :   }
     338              : 
     339            0 :   log (uhal::Info(), "Axi4Lite client ", uhal::Quote(id()), " (URI: ", uhal::Quote(uri()), ") : writing ", Integer(aBuffers->sendCounter() / 4), "-word packet to page ", Integer(mIndexNextPage), " in ", uhal::Quote(mMappedFile.getPath()));
     340              : 
     341            0 :   const uint32_t lHeaderWord = (0x10000 | (((aBuffers->sendCounter() / 4) - 1) & 0xFFFF));
     342            0 :   std::vector<std::pair<const uint8_t*, size_t> > lDataToWrite;
     343            0 :   lDataToWrite.push_back( std::make_pair(reinterpret_cast<const uint8_t*>(&lHeaderWord), sizeof lHeaderWord) );
     344            0 :   lDataToWrite.push_back( std::make_pair(aBuffers->getSendBuffer(), aBuffers->sendCounter()) );
     345              : 
     346            0 :   IPCScopedLock_t lGuard(*mIPCMutex);
     347            0 :   mMappedFile.write(mIndexNextPage * mPageSize, lDataToWrite);
     348            0 :   log (uhal::Debug(), "Wrote " , Integer((aBuffers->sendCounter() / 4) + 1), " 32-bit words at address " , Integer(mIndexNextPage * mPageSize), " ... ", PacketFmt(lDataToWrite));
     349              : 
     350            0 :   mIndexNextPage = (mIndexNextPage + 1) % mNumberOfPages;
     351            0 :   mReplyQueue.push_back(aBuffers);
     352            0 : }
     353              : 
     354              : 
     355            0 : void Axi4Lite::read()
     356              : {
     357            0 :   const size_t lPageIndexToRead = (mIndexNextPage - mReplyQueue.size() + mNumberOfPages) % mNumberOfPages;
     358            0 :   SteadyClock_t::time_point lStartTime = SteadyClock_t::now();
     359              : 
     360            0 :   if (mReadReplyPageCount == mPublishedReplyPageCount)
     361              :   {
     362            0 :     uint32_t lHwPublishedPageCount = 0x0;
     363              : 
     364            0 :     std::vector<uint32_t> lValues;
     365            0 :     while ( true ) {
     366              :       // FIXME : Improve by simply adding fileWrite method that takes uint32_t ref as argument (or returns uint32_t)
     367            0 :       IPCScopedLock_t lGuard(*mIPCMutex);
     368            0 :       mMappedFile.read(0, 4, lValues);
     369            0 :       lHwPublishedPageCount = lValues.at(3);
     370            0 :       log (uhal::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()));
     371              : 
     372            0 :       if (lHwPublishedPageCount != mPublishedReplyPageCount) {
     373            0 :         mPublishedReplyPageCount = lHwPublishedPageCount;
     374            0 :         break;
     375              :       }
     376              :       // FIXME: Throw if published page count is invalid number
     377              : 
     378            0 :       if (SteadyClock_t::now() - lStartTime > std::chrono::microseconds(getBoostTimeoutPeriod().total_microseconds())) {
     379            0 :         exception::Axi4LiteTimeout lExc;
     380            0 :         log(lExc, "Next page (index ", Integer(lPageIndexToRead), " count ", Integer(mPublishedReplyPageCount+1), ") of Axi4Lite device '" + mMappedFile.getPath() + "' is not ready after timeout period");
     381            0 :         throw lExc;
     382            0 :       }
     383              : 
     384            0 :       log(uhal::Debug(), "Axi4Lite client ", uhal::Quote(id()), " (URI: ", uhal::Quote(uri()), ") : Trying to read page index ", Integer(lPageIndexToRead), " = count ", Integer(mReadReplyPageCount+1), "; published page count is ", Integer(lHwPublishedPageCount), "; sleeping for ", mSleepDuration.count(), "us");
     385            0 :       if (mSleepDuration > std::chrono::microseconds(0))
     386            0 :         std::this_thread::sleep_for( mSleepDuration );
     387            0 :       lValues.clear();
     388            0 :     }
     389              : 
     390            0 :     log(uhal::Info(), "Axi4Lite client ", uhal::Quote(id()), " (URI: ", uhal::Quote(uri()), ") : Reading page ", Integer(lPageIndexToRead), " (published count ", Integer(lHwPublishedPageCount), ", surpasses required, ", Integer(mReadReplyPageCount + 1), ")");
     391              : 
     392            0 :   }
     393            0 :   mReadReplyPageCount++;
     394              :   
     395              :   // PART 1 : Read the page
     396            0 :   std::shared_ptr<uhal::Buffers> lBuffers = mReplyQueue.front();
     397            0 :   mReplyQueue.pop_front();
     398              : 
     399            0 :   uint32_t lNrWordsToRead(lBuffers->replyCounter() >> 2);
     400            0 :   lNrWordsToRead += 1;
     401              :  
     402            0 :   std::vector<uint32_t> lPageContents;
     403            0 :   IPCScopedLock_t lGuard(*mIPCMutex);
     404            0 :   mMappedFile.read(4 + lPageIndexToRead * mPageSize, lNrWordsToRead , lPageContents);
     405            0 :   lGuard.unlock();
     406            0 :   log (uhal::Debug(), "Read " , Integer(lNrWordsToRead), " 32-bit words from address " , Integer(4 + lPageIndexToRead * 4 * mPageSize), " ... ", PacketFmt((const uint8_t*)lPageContents.data(), 4 * lPageContents.size()));
     407              : 
     408              :   // PART 2 : Transfer to reply buffer
     409            0 :   const std::deque< std::pair< uint8_t* , uint32_t > >& lReplyBuffers ( lBuffers->getReplyBuffer() );
     410            0 :   size_t lNrWordsInPacket = (lPageContents.at(0) >> 16) + (lPageContents.at(0) & 0xFFFF);
     411            0 :   if (lNrWordsInPacket != (lBuffers->replyCounter() >> 2))
     412            0 :     log (uhal::Warning(), "Expected reply packet to contain ", Integer(lBuffers->replyCounter() >> 2), " words, but it actually contains ", Integer(lNrWordsInPacket), " words");
     413              : 
     414            0 :   size_t lNrBytesCopied = 0;
     415            0 :   for (const auto& lBuffer: lReplyBuffers)
     416              :   {
     417              :     // Don't copy more of page than was written to, for cases when less data received than expected
     418            0 :     if ( lNrBytesCopied >= 4*lNrWordsInPacket)
     419              :       break;
     420              : 
     421            0 :     size_t lNrBytesToCopy = std::min( lBuffer.second , uint32_t(4*lNrWordsInPacket - lNrBytesCopied) );
     422            0 :     memcpy ( lBuffer.first, &lPageContents.at(1 + (lNrBytesCopied / 4)), lNrBytesToCopy );
     423            0 :     lNrBytesCopied += lNrBytesToCopy;
     424              :   }
     425              : 
     426              : 
     427              :   // PART 3 : Validate the packet contents
     428            0 :   uhal::exception::exception* lExc = NULL;
     429            0 :   try
     430              :   {
     431            0 :     lExc = ClientInterface::validate ( lBuffers );
     432              :   }
     433            0 :   catch ( uhal::exception::exception& aExc )
     434              :   {
     435            0 :     uhal::exception::ValidationError lExc2;
     436            0 :     log ( lExc2 , "Exception caught during reply validation for Axi4Lite device with URI " , uhal::Quote ( this->uri() ) , "; what returned: " , uhal::Quote ( aExc.what() ) );
     437            0 :     throw lExc2;
     438            0 :   }
     439              : 
     440            0 :   if (lExc != NULL)
     441            0 :     lExc->throwAsDerivedType();
     442            0 : }
     443              : 
     444              : } // namespace uhal
        

Generated by: LCOV version 2.0-1