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
|