DUNE-DAQ
DUNE Trigger and Data Acquisition software
Loading...
Searching...
No Matches
ProtocolFlx.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
43
44
45#include <algorithm> // for min
46#include <assert.h>
47#include <cstdlib>
48#include <fcntl.h>
49#include <iomanip> // for operator<<
50#include <iostream> // for operator<<
51#include <sys/mman.h>
52#include <sys/stat.h>
53#include <sys/file.h>
54#include <stdlib.h> // for size_t, free
55#include <string.h> // for memcpy
56#include <unistd.h>
57
58#include <chrono> // for operator>
59#include <thread> // for sleep_for
60#include <filesystem>
61
62#include "uhal/grammars/URI.hpp" // for URI
63#include "uhal/log/LogLevels.hpp" // for BaseLogLevel
64#include "uhal/log/log_inserters.integer.hpp" // for uhal::Integer
65#include "uhal/log/log_inserters.quote.hpp" // for uhal::Quote
66#include "uhal/log/log.hpp"
67#include "uhal/Buffers.hpp"
68#include "uhal/ClientFactory.hpp"
69
71
72#include "regmap/regmap-struct.h"
73
74UHAL_REGISTER_EXTERNAL_CLIENT(uhallibs::Flx, "ipbusflx-2.0", "A client description")
75
76
77namespace uhallibs {
78
79
80//-----------------------------------------------------------------------------
81regmap_register_t* Flx::Card::find_reg( const std::string& aName ) {
82
83 regmap_register_t *reg;
84 for (reg = regmap_registers; reg->name != NULL; reg++) {
85 if (aName == reg->name) {
86 return reg;
87 }
88 }
89
90 return nullptr;
91}
92
93//-----------------------------------------------------------------------------
94Flx::Card::Card(const std::string& aDevicePath, u_int aLockMask) :
95 mPath(aDevicePath),
96 mLockMask(aLockMask),
97 mFlxCard(),
98 mIsOpen(false) {
99
100 const std::string prefix("/dev/flx");
101 if ( aDevicePath.rfind(prefix, 0) != 0 ) {
102
103 exception::FlxInvalidDevice lExc;
104 log(lExc, "Invalid device path ", uhal::Quote(mPath));
105 throw lExc;
106
107 }
108
109 std::string device_id_str = aDevicePath.substr(prefix.size()).data();
110 try {
111 mDeviceId = std::stoi(device_id_str);
112 } catch (std::invalid_argument const& ex) {
113
114 exception::FlxInvalidDevice lExc;
115 log(lExc, "Invalid device id ", uhal::Quote(mPath));
116 throw lExc;
117
118 }
119}
120
121
122//-----------------------------------------------------------------------------
125
126
127//-----------------------------------------------------------------------------
129
130 log(uhal::Debug(), "Flx::Card client Opening felix endpoint ", mDeviceId, " on ", mPath);
131
132 if( access( mPath.c_str(), F_OK ) == -1 ) {
133
134 exception::FlxInitialisationError lExc;
135 log(lExc, "Failed to open device file ", uhal::Quote(mPath), "; errno=", uhal::Integer(errno), ", meaning ", uhal::Quote (strerror(errno)));
136 throw lExc;
137 }
138
139 mFd = ::open(mPath.c_str(), O_RDWR);
140 if (mFd < 0) {
141 return;
142 }
143
144 mFlxCard.card_open(mDeviceId, mLockMask);
145
146 mIsOpen = true;
147}
148
149
150//-----------------------------------------------------------------------------
152
153 if (mIsOpen)
154 mFlxCard.card_close();
155
156 if (mFd != -1) {
157 if (haveLock())
158 unlock();
159 int rc = ::close(mFd);
160 mFd = -1;
161 if (rc == -1)
162 log (uhal::Error(), "Failed to close file ", uhal::Quote(mPath), "; errno=", uhal::Integer(errno), ", meaning ", uhal::Quote (strerror(errno)));
163 }
164}
165
166
167//-----------------------------------------------------------------------------
168const std::string& Flx::Card::getPath() const {
169 return mPath;
170}
171
172//-----------------------------------------------------------------------------
174 return mDeviceId;
175}
176
177//-----------------------------------------------------------------------------
178void Flx::Card::read(const uint32_t aAddr, const uint32_t aNrWords, std::vector<uint32_t>& aValues) {
179
180 if (!mIsOpen)
181 open();
182
183 flxcard_bar2_regs_t *bar2 = (flxcard_bar2_regs_t *) mFlxCard.openBackDoor( 2 );
184
185 // +1 is ceiling rounding in integers
186 uint32_t lNrReads64b = (aNrWords+1)/2;
187 uint32_t lAddr = aAddr/2;
188
189 for ( uint32_t i(0); i<lNrReads64b; i++) {
190 // *lReadAddrPtr = lAddr+i;
191 bar2->IPBUS_READ_ADDRESS = lAddr+i;
192 // uint64_t lDataWord = *lReadDataPtr;
193 uint64_t lDataWord = bar2->IPBUS_READ_DATA;
194 // Split the 64b word in 32b chunks
195 aValues.push_back(lDataWord & 0xffffffff);
196 if ( 2*i+1 < aNrWords )
197 aValues.push_back(lDataWord >> 32);
198 }
199
200 log(uhal::Debug(), "Flx::Card::read, ", aNrWords, " requested, ", aValues.size(), " read");
201
202}
203
204
205//-----------------------------------------------------------------------------
206void Flx::Card::write(const uint32_t aAddr, const std::vector<std::pair<const uint8_t*, size_t> >& aData)
207{
208
209 if (!mIsOpen)
210 open();
211
212 flxcard_bar2_regs_t *bar2 = (flxcard_bar2_regs_t *) mFlxCard.openBackDoor( 2 );
213
214 size_t lNrBytes = 0;
215 for (size_t i = 0; i < aData.size(); i++)
216 lNrBytes += aData.at(i).second;
217
218 assert((lNrBytes % 4) == 0);
219
220 char *allocated = NULL;
221 posix_memalign((void **)&allocated, 4096/*alignment*/, lNrBytes + 4096);
222 if (allocated == NULL) {
223 exception::FlxCommunicationError lExc;
224 log(lExc, "Failed to allocate ", uhal::Integer(lNrBytes + 4096), " bytes in File::write/2 function");
225 throw lExc;
226 }
227
228 // data to write to register address
229 char* buffer = allocated;
230 size_t lNrBytesCopied = 0;
231 for (size_t i = 0; i < aData.size(); i++) {
232 memcpy(buffer + lNrBytesCopied, aData.at(i).first, aData.at(i).second);
233 lNrBytesCopied += aData.at(i).second;
234 }
235
236 lNrBytesCopied = 0;
237 uint32_t lAddr = aAddr/2;
238
239 while (lNrBytesCopied < lNrBytes) {
240 bar2->IPBUS_WRITE_ADDRESS = lAddr;
241 char* lSrcPtr = buffer + lNrBytesCopied;
242 if ((lNrBytes - lNrBytesCopied) >= 8) {
243 bar2->IPBUS_WRITE_DATA.DATA = *(uint64_t*) lSrcPtr;
244 lNrBytesCopied += 8;
245 }
246 else if ((lNrBytes - lNrBytesCopied) >= 4) {
247 bar2->IPBUS_WRITE_DATA.DATA = uint64_t(*(uint32_t*) lSrcPtr);
248 lNrBytesCopied += 4;
249 }
250
251 ++lAddr;
252 }
253
254 free(allocated);
255}
256
257bool Flx::Card::haveLock() const { return mLocked; }
258
259
261 if (flock(mFd, LOCK_EX) == -1) {
262 ipc::exception::MutexError lExc;
263 log(lExc, "Failed to lock device file ", uhal::Quote(mPath),
264 "; errno=", uhal::Integer(errno), ", meaning ", uhal::Quote(strerror(errno)));
265 throw lExc;
266 }
267 mLocked = true;
268}
269
271 if (flock(mFd, LOCK_UN) == -1) {
272 log(uhal::Warning(), "Failed to unlock device file ", uhal::Quote(mPath),
273 "; errno=", uhal::Integer(errno), ", meaning ", uhal::Quote(strerror(errno)));
274 } else
275 mLocked = false;
276}
277
278
279// Flx Transport
280std::string Flx::getSharedMemName(const std::string& aPath)
281{
282 std::string lSanitizedPath(aPath);
283 std::replace(lSanitizedPath.begin(), lSanitizedPath.end(), '/', ':');
284
285 return "/uhal::ipbusflx-2.0::" + lSanitizedPath;
286}
287
288
289Flx::Flx ( const std::string& aId, const uhal::URI& aUri ) :
290 IPbus< 2 , 0 > ( aId , aUri ),
291 mConnected(false),
292 mDeviceFile(aUri.mHostname, LOCK_NONE),
293 mIPCMutex(getSharedMemName(aUri.mHostname)),
295 mPageSize(0),
300{
301 mSleepDuration = std::chrono::microseconds(50);
302
303 for (uhal::NameValuePairVectorType::const_iterator lIt = aUri.mArguments.begin(); lIt != aUri.mArguments.end(); lIt++) {
304 if (lIt->first == "sleep") {
305 mSleepDuration = std::chrono::microseconds(std::stoul(lIt->second));
306 log (uhal::Notice() , "flx client with URI ", uhal::Quote (uri()), " : Inter-poll-/-interrupt sleep duration set to ", std::stoul(lIt->second), " us by URI 'sleep' attribute");
307 }
308 // else if (lIt->first == "offset") {
309 // const bool lIsHex = (lIt->second.find("0x") == 0) or (lIt->second.find("0X") == 0);
310 // const size_t lOffset = (lIsHex ? std::lexical_cast<HexTo<size_t> >(lIt->second) : std::stoul(lIt->second));
311 // mDeviceFile.setOffset(lOffset);
312 // log (uhal::Notice(), "flx client with URI ", uhal::Quote (uri()), " : Address offset set to ", uhal::Integer(lOffset, IntFmt<hex>()));
313 // }
314 else {
315 log (uhal::Warning() , "Unknown attribute ", uhal::Quote (lIt->first), " used in URI ", uhal::Quote(uri()));
316 }
317 }
318}
319
320
322{
323 disconnect();
324}
325
326
327void Flx::implementDispatch ( std::shared_ptr< uhal::Buffers > aBuffers )
328{
329 log(uhal::Debug(), "flx client (URI: ", uhal::Quote(uri()), ") : implementDispatch method called");
330
331 if ( ! mConnected )
332 connect();
333
334 if ( mReplyQueue.size() == mNumberOfPages )
335 read();
336 write(aBuffers);
337}
338
339
341{
342 log(uhal::Debug(), "flx client (URI: ", uhal::Quote(uri()), ") : Flush method called");
343 while ( !mReplyQueue.empty() )
344 read();
345
347
348 IPCScopedLock_t lLockGuard(*mIPCMutex);
349 mIPCMutex->endSession();
350
351}
352
353
355{
356 // FIXME: Adapt to PCIe implementation
357 // log(uhal::Notice(), "flx client ", uhal::Quote(id()), " (URI: ", uhal::Quote(uri()), ") : closing device files since exception detected");
358
359 // ClientInterface::returnBufferToPool ( mReplyQueue );
360
362
363 disconnect();
364
365 InnerProtocol::dispatchExceptionHandler();
366}
367
368
370{
371 if ( ! mConnected )
372 connect();
373
374 return (mPageSize - 1) * 4;
375}
376
377
379{
380 if ( ! mConnected )
381 connect();
382
383 return (mPageSize - 1) * 4;
384}
385
386
388{
389 IPCScopedLock_t lLockGuard(*mIPCMutex);
390 connect(lLockGuard);
391}
392
393
395{
396 // Read current value of session counter when reading status info from FPGA
397 // (So that can check whether this info is up-to-date later on, when sending next request packet)
399 mIPCSessionCount = mIPCMutex->getCounter();
400
401 log ( uhal::Debug() , "flx client is opening device file " , uhal::Quote ( mDeviceFile.getPath() ) );
402 std::vector<uint32_t> lValues;
403 mDeviceFile.read(0x0, 4, lValues);
404 log (uhal::Debug(), "Read status info from addr 0 (", uhal::Integer(lValues.at(0)), ", ", uhal::Integer(lValues.at(1)), ", ", uhal::Integer(lValues.at(2)), ", ", uhal::Integer(lValues.at(3)), "): ", PacketFmt((const uint8_t*)lValues.data(), 4 * lValues.size()));
405 aGuard.unlock();
406
407 mNumberOfPages = lValues.at(0);
408 // mPageSize = std::min(uint32_t(4096), lValues.at(1));
409 mPageSize = lValues.at(1);
410 mIndexNextPage = lValues.at(2);
411 mPublishedReplyPageCount = lValues.at(3);
413
414 if (lValues.at(1) > 0xFFFF) {
415 exception::FlxInitialisationError lExc;
416 log (lExc, "Invalid page size, ", uhal::Integer(lValues.at(1)), ", reported in device file ", uhal::Quote(mDeviceFile.getPath()));
417 throw lExc;
418 }
419
421 exception::FlxInitialisationError lExc;
422 log (lExc, "Next page index, ", uhal::Integer(mIndexNextPage), ", reported in device file ", uhal::Quote(mDeviceFile.getPath()), " is inconsistent with number of pages, ", uhal::Integer(mNumberOfPages));
423 throw lExc;
424 }
425
426 mConnected = true;
427 log ( uhal::Info() , "flx client connected to device at ", uhal::Quote(mDeviceFile.getPath()), "; FPGA has ", uhal::Integer(mNumberOfPages), " pages, each of size ", uhal::Integer(mPageSize), " words, index ", uhal::Integer(mIndexNextPage), " should be filled next" );
428}
429
430
432{
433 log ( uhal::Debug() , "flx client is closing device file " , uhal::Quote ( mDeviceFile.getPath() ) );
435 mConnected = false;
436}
437
438
439void Flx::write(const std::shared_ptr<uhal::Buffers>& aBuffers)
440{
441 if (not mDeviceFile.haveLock()) {
443
444 IPCScopedLock_t lGuard(*mIPCMutex);
445 mIPCMutex->startSession();
447
448 if (mIPCExternalSessionActive or (mIPCMutex->getCounter() != mIPCSessionCount)) {
449 connect(lGuard);
450 }
451 }
452
453 log (uhal::Info(), "flx client ", uhal::Quote(id()), " (URI: ", uhal::Quote(uri()), ") : writing ", uhal::Integer(aBuffers->sendCounter() / 4), "-word packet to page ", uhal::Integer(mIndexNextPage), " in ", uhal::Quote(mDeviceFile.getPath()));
454
455 const uint32_t lHeaderWord = (0x10000 | (((aBuffers->sendCounter() / 4) - 1) & 0xFFFF));
456 std::vector<std::pair<const uint8_t*, size_t> > lDataToWrite;
457 lDataToWrite.push_back( std::make_pair(reinterpret_cast<const uint8_t*>(&lHeaderWord), sizeof lHeaderWord) );
458 lDataToWrite.push_back( std::make_pair(aBuffers->getSendBuffer(), aBuffers->sendCounter()) );
459
460 IPCScopedLock_t lGuard(*mIPCMutex);
461 mDeviceFile.write(mIndexNextPage * mPageSize, lDataToWrite);
462 log (uhal::Debug(), "Wrote " , uhal::Integer((aBuffers->sendCounter() / 4) + 1), " 32-bit words at address " , uhal::Integer(mIndexNextPage * 4 * mPageSize), " ... ", PacketFmt(lDataToWrite));
463
465 mReplyQueue.push_back(aBuffers);
466}
467
468
470{
471 const size_t lPageIndexToRead = (mIndexNextPage - mReplyQueue.size() + mNumberOfPages) % mNumberOfPages;
472 SteadyClock_t::time_point lStartTime = SteadyClock_t::now();
473
475 {
476 uint32_t lHwPublishedPageCount = 0x0;
477
478 std::vector<uint32_t> lValues;
479 while ( true ) {
480 IPCScopedLock_t lGuard(*mIPCMutex);
481 // FIXME : Improve by simply adding dmaWrite method that takes uint32_t ref as argument (or returns uint32_t)
482 mDeviceFile.read(0, 4, lValues);
483 lHwPublishedPageCount = lValues.at(3);
484 // log (uhal::Info(), "Read status info from addr 0 (", uhal::Integer(lValues.at(0)), ", ", uhal::Integer(lValues.at(1)), ", ", uhal::Integer(lValues.at(2)), ", ", uhal::Integer(lValues.at(3)), "): ", PacketFmt((const uint8_t*)lValues.data(), 4 * lValues.size()));
485 log (uhal::Debug(), "Read status info from addr 0 (", uhal::Integer(lValues.at(0)), ", ", uhal::Integer(lValues.at(1)), ", ", uhal::Integer(lValues.at(2)), ", ", uhal::Integer(lValues.at(3)), "): ", PacketFmt((const uint8_t*)lValues.data(), 4 * lValues.size()));
486
487 if (lHwPublishedPageCount != mPublishedReplyPageCount) {
488 mPublishedReplyPageCount = lHwPublishedPageCount;
489 break;
490 }
491 // FIXME: Throw if published page count is invalid number
492
493 if (SteadyClock_t::now() - lStartTime > std::chrono::microseconds(getBoostTimeoutPeriod().total_microseconds())) {
494 exception::FlxTimeout lExc;
495 log(lExc, "Next page (index ", uhal::Integer(lPageIndexToRead), " count ", uhal::Integer(mPublishedReplyPageCount+1), ") of flx device '" + mDeviceFile.getPath() + "' is not ready after timeout period");
496 throw lExc;
497 }
498
499 log(uhal::Debug(), "flx client ", uhal::Quote(id()), " (URI: ", uhal::Quote(uri()), ") : Trying to read page index ", uhal::Integer(lPageIndexToRead), " = count ", uhal::Integer(mReadReplyPageCount+1), "; published page count is ", uhal::Integer(lHwPublishedPageCount), "; sleeping for ", mSleepDuration.count(), "us");
500 if (mSleepDuration > std::chrono::microseconds(0))
501 std::this_thread::sleep_for( mSleepDuration );
502 lValues.clear();
503 }
504
505 log(uhal::Info(), "flx client ", uhal::Quote(id()), " (URI: ", uhal::Quote(uri()), ") : Reading page ", uhal::Integer(lPageIndexToRead), " (published count ", uhal::Integer(lHwPublishedPageCount), ", surpasses required, ", uhal::Integer(mReadReplyPageCount + 1), ")");
506 }
508
509 // PART 1 : Read the page
510 std::shared_ptr<uhal::Buffers> lBuffers = mReplyQueue.front();
511 mReplyQueue.pop_front();
512
513 uint32_t lNrWordsToRead(lBuffers->replyCounter() >> 2);
514 lNrWordsToRead += 1;
515
516 std::vector<uint32_t> lPageContents;
517 IPCScopedLock_t lGuard(*mIPCMutex);
518 mDeviceFile.read(4 + lPageIndexToRead * mPageSize, lNrWordsToRead , lPageContents);
519 lGuard.unlock();
520 log (uhal::Debug(), "Read " , uhal::Integer(lNrWordsToRead), " 32-bit words from address " , uhal::Integer(4 + lPageIndexToRead * 4 * mPageSize), " ... ", PacketFmt((const uint8_t*)lPageContents.data(), 4 * lPageContents.size()));
521
522 // PART 2 : Transfer to reply buffer
523 const std::deque< std::pair< uint8_t* , uint32_t > >& lReplyBuffers ( lBuffers->getReplyBuffer() );
524 size_t lNrWordsInPacket = (lPageContents.at(0) >> 16) + (lPageContents.at(0) & 0xFFFF);
525 if (lNrWordsInPacket != (lBuffers->replyCounter() >> 2))
526 log (uhal::Warning(), "Expected reply packet to contain ", uhal::Integer(lBuffers->replyCounter() >> 2), " words, but it actually contains ", uhal::Integer(lNrWordsInPacket), " words");
527
528 size_t lNrBytesCopied = 0;
529 for ( std::deque< std::pair< uint8_t* , uint32_t > >::const_iterator lIt = lReplyBuffers.begin() ; lIt != lReplyBuffers.end() ; ++lIt )
530 {
531 // Don't copy more of page than was written to, for cases when less data received than expected
532 if ( lNrBytesCopied >= 4*lNrWordsInPacket)
533 break;
534
535 size_t lNrBytesToCopy = std::min( lIt->second , uint32_t(4*lNrWordsInPacket - lNrBytesCopied) );
536 memcpy ( lIt->first, &lPageContents.at(1 + (lNrBytesCopied / 4)), lNrBytesToCopy );
537 lNrBytesCopied += lNrBytesToCopy;
538 }
539
540
541 // PART 3 : Validate the packet contents
542 try
543 {
544 if ( uhal::exception::exception* lExc = ClientInterface::validate ( lBuffers ) ) //Control of the pointer has been passed back to the client interface
545 {
547 }
548 }
549 catch ( uhal::exception::exception& aExc )
550 {
551 mAsynchronousException = new uhal::exception::ValidationError ();
552 log ( *mAsynchronousException , "Exception caught during reply validation for flx device with URI " , uhal::Quote ( this->uri() ) , "; what returned: " , uhal::Quote ( aExc.what() ) );
553 }
554
556 {
557 mAsynchronousException->throwAsDerivedType();
558 }
559}
560
561
562} // end ns uhal
const std::string & getPath() const
void write(const uint32_t aAddr, const std::vector< std::pair< const uint8_t *, size_t > > &aData)
bool haveLock() const
int getDeviceId() const
void read(const uint32_t aAddr, const uint32_t aNrWords, std::vector< uint32_t > &aValues)
Transport protocol to transfer an IPbus buffer via device file, using mmap.
virtual void dispatchExceptionHandler()
Function which tidies up this protocol layer in the event of an exception.
uint32_t getMaxReplySize()
static std::string getSharedMemName(const std::string &aPath)
uint32_t mPublishedReplyPageCount
void connect()
Set up the connection to the device.
void write(const std::shared_ptr< uhal::Buffers > &aBuffers)
Write request packet to next page in host-to-FPGA device file.
uint32_t mNumberOfPages
void implementDispatch(std::shared_ptr< uhal::Buffers > aBuffers)
std::deque< std::shared_ptr< uhal::Buffers > > mReplyQueue
The list of buffers still awaiting a reply.
uint32_t getMaxSendSize()
std::chrono::microseconds mSleepDuration
virtual void Flush()
Concrete implementation of the synchronization function to block until all buffers have been sent,...
uint32_t mPageSize
Flx(const Flx &aFlx)
bool mIPCExternalSessionActive
ipc::SharedMemObject< IPCMutex_t > mIPCMutex
uint32_t mReadReplyPageCount
std::unique_lock< IPCMutex_t > IPCScopedLock_t
void read()
Read next pending reply packet from appropriate page of FPGA-to-host device file, and validate conten...
void disconnect()
Close the connection to the device.
uhal::exception::exception * mAsynchronousException
uint32_t mIndexNextPage
uint64_t mIPCSessionCount
virtual ~Flx()
Destructor.