Line data Source code
1 : // This file (mu2edev.cc) was created by Ron Rechenmacher <ron@fnal.gov> on
2 : // Feb 13, 2014. "TERMS AND CONDITIONS" governing this file are in the README
3 : // or COPYING file. If you do not have such a file, one can be obtained by
4 : // contacting Ron or Fermi Lab in Batavia IL, 60510, phone: 630-840-3000.
5 : // $RCSfile: .emacs.gnu,v $
6 : // rev="$Revision: 1.23 $$Date: 2012/01/23 15:32:40 $";
7 :
8 : /*
9 : * make mu2edev.o CFLAGS='-g -Wall -std=c++0x'
10 : */
11 :
12 : #include <signal.h>
13 : #include <chrono>
14 : #include <errno.h>
15 :
16 : #include "TRACE/tracemf.h"
17 :
18 : #include "mu2edev.h"
19 :
20 : #include "dtcInterfaceLib/otsStyleCoutMacros.h"
21 :
22 : typedef unsigned long dma_addr_t;
23 : #include "mu2e_driver/mu2e_proto_globals.h" // MU2E_NUM_RECV_BUFFS
24 :
25 : #define DEV_TLOG(lvl) TLOG(lvl) << "DEVICE " << this->getDeviceUID() << ": "
26 :
27 : static constexpr int TLVL_INIT_DMA_ENGINE = TLVL_DEBUG + 15;
28 : static constexpr int TLVL_READ_DATA = TLVL_DEBUG + 16;
29 : static constexpr int TLVL_READ_DATA_2 = TLVL_DEBUG + 17;
30 : static constexpr int TLVL_READ_DATA_3 = TLVL_DEBUG + 18;
31 : static constexpr int TLVL_READ_RELEASE = TLVL_DEBUG + 19;
32 : static constexpr int TLVL_READ_REGISTER = TLVL_DEBUG + 20;
33 : static constexpr int TLVL_WRITE_REGISTER = TLVL_DEBUG + 21;
34 : static constexpr int TLVL_WRITE_REGISTER_CHECKED = TLVL_DEBUG + 22;
35 : static constexpr int TLVL_META_DUMP = TLVL_DEBUG + 23;
36 : static constexpr int TLVL_WRITE_DATA = TLVL_DEBUG + 24;
37 : static constexpr int TLVL_RELEASE_ALL = TLVL_DEBUG + 25;
38 : static constexpr int TLVL_BEGIN_DCS_TRANSACTION = TLVL_DEBUG + 26;
39 : static constexpr int TLVL_BEGIN_DCS_TRANSACTION_VERBOSE = TLVL_DEBUG + 27;
40 : static constexpr int TLVL_END_DCS_TRANSACTION = TLVL_DEBUG + 28;
41 : static constexpr int TLVL_GET_DRIVER_VERSION = TLVL_DEBUG + 29;
42 :
43 : static const std::thread::id NULL_TID = std::thread::id();
44 :
45 : std::array<mu2edev::DCSLock, MU2E_MAX_NUM_DTCS> mu2edev::dcs_locks_{};
46 :
47 0 : mu2edev::mu2edev()
48 0 : : devfd_(0), buffers_held_(0), simulator_(nullptr), activeDeviceIndex_(0), deviceTime_(0LL), writeSize_(0), readSize_(0), UID_("")
49 : {
50 : // TRACE_CNTL( "lvlmskM", 0x3 );
51 : // TRACE_CNTL( "lvlmskS", 0x3 );
52 0 : TLOG(TLVL_INFO) << "CONSTRUCTOR";
53 0 : } // end constructor
54 :
55 0 : mu2edev::~mu2edev()
56 : {
57 0 : TLOG(TLVL_INFO) << "DESTRUCTOR " << UID_;
58 :
59 0 : end_dcs_transaction(false /* mustHaveLock */);
60 0 : if (debugFp_) fclose(debugFp_);
61 :
62 0 : close();
63 0 : TLOG(TLVL_INFO) << "DESTRUCTOR end";
64 0 : } // end destructor
65 :
66 0 : int mu2edev::init(DTCLib::DTC_SimMode simMode, int deviceIndex, std::string simMemoryFileName, const std::string& uid)
67 : {
68 0 : UID_ = uid;
69 :
70 0 : auto debugWriteFilePath = getenv("DTCLIB_DEBUG_WRITE_FILE_PATH");
71 0 : if (debugWriteFilePath != nullptr)
72 : {
73 0 : auto debugWriteFileStr = std::string(debugWriteFilePath) + "/Write_debug_" + uid + "_" + std::to_string(time(0)) + ".txt";
74 0 : if (debugFp_) fclose(debugFp_);
75 0 : debugFp_ = fopen(debugWriteFileStr.c_str(), "w");
76 0 : if (!debugFp_)
77 : {
78 0 : __SS__ << "mu2e Device write debug file could not be opened at path DTCLIB_DEBUG_WRITE_FILE_PATH! Exiting.\n"
79 0 : << "open " << debugWriteFileStr << __E__;
80 0 : perror(ss.str().c_str());
81 0 : __SS_THROW__;
82 : // exit(1);
83 0 : }
84 0 : }
85 :
86 0 : auto start = std::chrono::steady_clock::now();
87 0 : lastWriteTime_ = start; // init time
88 :
89 0 : if (simMode != DTCLib::DTC_SimMode_Disabled && simMode != DTCLib::DTC_SimMode_NoCFO &&
90 0 : simMode != DTCLib::DTC_SimMode_ROCEmulator && simMode != DTCLib::DTC_SimMode_Loopback)
91 : {
92 0 : simulator_ = new mu2esim(simMemoryFileName);
93 0 : simulator_->init(simMode);
94 : }
95 : else
96 : {
97 0 : if (simulator_ != nullptr)
98 : {
99 0 : delete simulator_;
100 0 : simulator_ = nullptr;
101 : }
102 :
103 0 : activeDeviceIndex_ = deviceIndex;
104 0 : initDMAEngine();
105 : }
106 :
107 : // init to free dcs locks
108 0 : end_dcs_transaction(true /* force */); // force unlock
109 :
110 0 : deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
111 0 : return simMode;
112 : }
113 :
114 : //==============================================================================
115 : // exec
116 : // run linux command and get result back in string
117 0 : std::string exec(const char* cmd)
118 : {
119 0 : __COUTV__(cmd);
120 :
121 : std::array<char, 128> buffer;
122 0 : std::string result;
123 0 : std::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
124 0 : if (!pipe)
125 : {
126 0 : __COUT_ERR__ << "popen() failed!" << __E__;
127 0 : return "popen() failed!";
128 : }
129 0 : while (!feof(pipe.get()))
130 : {
131 0 : if (fgets(buffer.data(), 128, pipe.get()) != nullptr)
132 0 : result += buffer.data();
133 : }
134 : //__COUTV__(result);
135 0 : return result;
136 0 : } // end exec()
137 :
138 : /*****************************
139 : initDMAEngine
140 : set up DMA engines
141 : */
142 0 : void mu2edev::initDMAEngine()
143 : {
144 0 : if (simulator_ != nullptr)
145 : {
146 0 : __COUT__ << "Using simulator, so no need to init DMA Engine..." << __E__;
147 0 : return; // do nothing if using simulator
148 : }
149 0 : __COUT__ << "Initializing DMA engine..." << __E__;
150 :
151 : char devfile[11];
152 0 : snprintf(devfile, 11, "/dev/" MU2E_DEV_FILE, activeDeviceIndex_);
153 : int sts;
154 0 : if (devfd_) ::close(devfd_);
155 0 : devfd_ = open(devfile, O_RDWR);
156 0 : if (devfd_ == -1 || devfd_ == 0)
157 : {
158 0 : __SS__ << "mu2e Device file not found (or DTCLIB_SIM_ENABLE not set)! Exiting.\n"
159 0 : << "Attempt to open '" << devfile << "' and received error: " << errno << " - " << strerror(errno) << __E__;
160 :
161 : ss << "Who owns it? (ls -l /dev/mu2e*)\n"
162 0 : << exec("ls -l /dev/mu2e*") << __E__;
163 : ss << "\nWhat process is using it (lsof /dev/mu2e* 2>/dev/null, will only show processes owned by the current user)?\n"
164 0 : << exec("lsof /dev/mu2e* 2>/dev/null") << __E__;
165 0 : perror(ss.str().c_str());
166 0 : __SS_THROW__;
167 : // exit(1);
168 0 : }
169 0 : for (unsigned chn = 0; chn < MU2E_MAX_CHANNELS; ++chn)
170 0 : for (unsigned dir = 0; dir < 2; ++dir)
171 : {
172 : m_ioc_get_info_t get_info;
173 0 : get_info.chn = chn;
174 0 : get_info.dir = dir;
175 0 : get_info.tmo_ms = 1000;
176 0 : TRACE(TLVL_INIT_DMA_ENGINE, UID_ + " - mu2edev::init before ioctl( devfd_, M_IOC_GET_INFO, &get_info ) chn=%u dir=%u", chn, dir);
177 0 : sts = ioctl(devfd_, M_IOC_GET_INFO, &get_info);
178 0 : if (sts != 0)
179 : {
180 0 : __SS__ << "Failed mu2edev::init before ioctl( devfd_, M_IOC_GET_INFO, &get_info)." << __E__;
181 0 : perror(ss.str().c_str());
182 0 : __SS_THROW__;
183 : // exit(1);
184 0 : }
185 0 : mu2e_channel_info_[activeDeviceIndex_][chn][dir] = get_info;
186 0 : TRACE(TLVL_INIT_DMA_ENGINE, UID_ + " - mu2edev::init %d %u:%u - num=%u size=%u hwIdx=%u, swIdx=%u delta=%u", activeDeviceIndex_, chn, dir,
187 : get_info.num_buffs, get_info.buff_size, get_info.hwIdx, get_info.swIdx,
188 : mu2e_chn_info_delta_(activeDeviceIndex_, chn, dir, &mu2e_channel_info_));
189 0 : for (unsigned map = 0; map < 2; ++map)
190 : {
191 0 : size_t length = get_info.num_buffs * ((map == MU2E_MAP_BUFF) ? get_info.buff_size : sizeof(int));
192 : // int prot = (((dir == S2C) && (map == MU2E_MAP_BUFF))? PROT_WRITE : PROT_READ);
193 0 : int prot = (((map == MU2E_MAP_BUFF)) ? PROT_WRITE : PROT_READ);
194 0 : off64_t offset = chnDirMap2offset(chn, dir, map);
195 0 : mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map] = mmap(0 /* hint address */
196 : ,
197 : length, prot, MAP_SHARED, devfd_, offset);
198 0 : if (mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map] == MAP_FAILED)
199 : {
200 0 : __SS__ << "mu2e Device mmap error! Exiting." << __E__;
201 0 : perror(ss.str().c_str());
202 0 : __SS_THROW__;
203 : // exit(1);
204 0 : }
205 0 : TRACE(TLVL_INIT_DMA_ENGINE, UID_ + " - mu2edev::init chnDirMap2offset=%lu mu2e_mmap_ptrs_[%d][%d][%d][%d]=%p p=%c l=%lu", offset, activeDeviceIndex_, chn,
206 : dir, map, mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map], prot == PROT_READ ? 'R' : 'W', length);
207 : }
208 :
209 : // do not want to release all from constructor, in case this instance of the device is not the 'owner' of a DMA-channel
210 : // if (dir == DTC_DMA_Direction_C2S)
211 : // {
212 : // release_all(static_cast<DTC_DMA_Engine>(chn));
213 : // }
214 :
215 : // Reset the DTC
216 : //{
217 : // write_register(0x9100, 0, 0xa0000000);
218 : // write_register(0x9118, 0, 0x0000003f);
219 : // write_register(0x9100, 0, 0x00000000);
220 : // write_register(0x9100, 0, 0x10000000);
221 : // write_register(0x9100, 0, 0x30000000);
222 : // write_register(0x9100, 0, 0x10000000);
223 : // write_register(0x9118, 0, 0x00000000);
224 : //}
225 :
226 : // Enable DMA Engines
227 : {
228 : // uint16_t addr = DTC_Register_Engine_Control(chn, dir);
229 : // TRACE(TLVL_INIT_DMA_ENGINE, UID_ + " - mu2edev::init write Engine_Control reg 0x%x", addr);
230 : // write_register(addr, 0, 0x100);//bit 8 enable=1
231 : }
232 : }
233 : } // end initDMAEngine()
234 :
235 : /*****************************
236 : read_data
237 : returns number of bytes read; negative value indicates an error
238 : */
239 0 : int mu2edev::read_data(DTC_DMA_Engine const& chn, void** buffer, int tmo_ms)
240 : {
241 : // WARNING NOTE: if there is existing data still sitting in unreleased buffer, the timeout will not add any delay
242 : int retsts;
243 0 : TRACE_EXIT { TRACE(TLVL_READ_DATA, UID_ + " - mu2edev::read_data returning retsts(bytes)=%d", retsts); };
244 :
245 0 : if (chn == DTC_DMA_Engine_DCS && !thread_owns_dcs_lock())
246 : {
247 0 : TRACE(TLVL_ERROR, UID_ + " - read_data dcs lock not held!");
248 0 : return retsts = -2;
249 : }
250 :
251 0 : auto start = std::chrono::steady_clock::now();
252 0 : if (simulator_ != nullptr)
253 : {
254 0 : retsts = simulator_->read_data(chn, buffer, tmo_ms);
255 : }
256 : else
257 : {
258 0 : retsts = 0;
259 : unsigned has_recv_data;
260 0 : TRACE(TLVL_READ_DATA, UID_ + " - mu2edev::read_data before (mu2e_mmap_ptrs_[%d][0][0][0]!=NULL) || ((retsts=init())==0) tmo_ms=%d", activeDeviceIndex_, tmo_ms);
261 0 : if ((mu2e_mmap_ptrs_[activeDeviceIndex_][0][0][0] != NULL) ||
262 0 : ((retsts = init(DTCLib::DTC_SimMode_Disabled, 0)) == 0)) // Default-init mu2edev if not given guidance
263 : {
264 0 : has_recv_data = mu2e_chn_info_delta_(activeDeviceIndex_, chn, C2S, &mu2e_channel_info_);
265 0 : TRACE(TLVL_READ_DATA, UID_ + " - mu2edev::read_data after %u=has_recv_data = delta_( chn=%d, C2S ), held=%u", has_recv_data, chn, buffers_held_);
266 0 : mu2e_channel_info_[activeDeviceIndex_][chn][C2S].tmo_ms = tmo_ms; // in case GET_INFO is called
267 0 : if ((has_recv_data > buffers_held_) ||
268 0 : ((retsts = ioctl(devfd_, M_IOC_GET_INFO, &mu2e_channel_info_[activeDeviceIndex_][chn][C2S])) == 0 &&
269 0 : (has_recv_data = mu2e_chn_info_delta_(activeDeviceIndex_, chn, C2S, &mu2e_channel_info_)) >
270 0 : buffers_held_))
271 : { // have data
272 : // get byte count from new/next
273 : unsigned newNxtIdx =
274 0 : idx_add(mu2e_channel_info_[activeDeviceIndex_][chn][C2S].swIdx, (int)buffers_held_ + 1, activeDeviceIndex_, chn, C2S);
275 0 : int* BC_p = (int*)mu2e_mmap_ptrs_[activeDeviceIndex_][chn][C2S][MU2E_MAP_META];
276 0 : retsts = BC_p[newNxtIdx];
277 0 : *buffer = ((mu2e_databuff_t*)(mu2e_mmap_ptrs_[activeDeviceIndex_][chn][C2S][MU2E_MAP_BUFF]))[newNxtIdx];
278 0 : TRACE(TLVL_READ_DATA_2,
279 : "mu2edev::read_data chn%d hIdx=%u, sIdx=%u num_buffs=%u hasRcvDat=%u %p(BC_p)[newNxtIdx=%d]=retsts=%d %p(buf)[0]=0x%08x",
280 : chn,
281 : mu2e_channel_info_[activeDeviceIndex_][chn][C2S].hwIdx,
282 : mu2e_channel_info_[activeDeviceIndex_][chn][C2S].swIdx,
283 : mu2e_channel_info_[activeDeviceIndex_][chn][C2S].num_buffs,
284 : has_recv_data, (void*)BC_p, newNxtIdx, retsts,
285 : *buffer, *(uint32_t*)*buffer);
286 0 : TRACE(TLVL_READ_DATA_3, "first 80 bytes: %016lx %016lx %016lx %016lx %016lx %016lx %016lx %016lx %016lx %016lx",
287 : *(((uint64_t*)*buffer) + 0), *(((uint64_t*)*buffer) + 1), *(((uint64_t*)*buffer) + 2), *(((uint64_t*)*buffer) + 3),
288 : *(((uint64_t*)*buffer) + 4), *(((uint64_t*)*buffer) + 5), *(((uint64_t*)*buffer) + 6), *(((uint64_t*)*buffer) + 7),
289 : *(((uint64_t*)*buffer) + 8), *(((uint64_t*)*buffer) + 9));
290 :
291 : /// increment buffers held if allowing multiple buffers to be held by user space (e.g. for Data DMA channel, which is different for CFO vs DTC)</param>
292 0 : if (chn == DTC_DMA_Engine_DAQ)
293 0 : ++buffers_held_;
294 : }
295 : else
296 : { // was it a tmo or error
297 0 : if (retsts != 0)
298 : {
299 0 : __SS__ << "Failed mu2edev::read_data with M_IOC_GET_INFO... return not 0." << __E__;
300 0 : perror(ss.str().c_str());
301 0 : __SS_THROW__;
302 : // exit(1);
303 0 : }
304 0 : TRACE(TLVL_READ_DATA_2, UID_ + " - mu2edev::read_data not error... return %d status", retsts);
305 : }
306 : }
307 : }
308 0 : deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
309 0 : if (retsts > 0) readSize_ += retsts;
310 0 : return retsts;
311 0 : } // end read_data()
312 :
313 0 : int mu2edev::GetBufferIndex(DTC_DMA_Engine const& chn, const void* ptr) const
314 : {
315 0 : const volatile void* mmapBase = mu2e_mmap_ptrs_[activeDeviceIndex_][chn][C2S][MU2E_MAP_BUFF];
316 0 : if (mmapBase == nullptr || ptr == nullptr) return -1;
317 0 : const auto bytesFromBase = reinterpret_cast<const volatile uint8_t*>(ptr) - reinterpret_cast<const volatile uint8_t*>(mmapBase);
318 0 : if (bytesFromBase < 0) return -1;
319 0 : const auto idx = static_cast<size_t>(bytesFromBase) / sizeof(mu2e_databuff_t);
320 0 : if (idx >= MU2E_NUM_RECV_BUFFS) return -1;
321 0 : return static_cast<int>(idx);
322 : }
323 :
324 : /* read_release
325 : release a number of buffers (usually 1)
326 : */
327 0 : int mu2edev::read_release(DTC_DMA_Engine const& chn, unsigned num)
328 : {
329 0 : if (chn == DTC_DMA_Engine_DCS && !thread_owns_dcs_lock())
330 : {
331 0 : TRACE(TLVL_ERROR, UID_ + " - read_release dcs lock not held!");
332 0 : return -2;
333 : }
334 :
335 0 : auto start = std::chrono::steady_clock::now();
336 0 : auto retsts = -1;
337 0 : if (simulator_ != nullptr)
338 : {
339 0 : retsts = simulator_->read_release(chn, num);
340 : }
341 : else
342 : {
343 0 : retsts = 0;
344 : unsigned long arg;
345 : unsigned has_recv_data;
346 0 : has_recv_data = mu2e_chn_info_delta_(activeDeviceIndex_, chn, C2S, &mu2e_channel_info_);
347 :
348 0 : TLOG(TLVL_READ_RELEASE) << "read_release(chn=" << chn << " num=" << num << ") dtc="
349 0 : << activeDeviceIndex_ << " has_recv_data=" << has_recv_data;
350 :
351 : // if (num > has_recv_data) num = has_recv_data;
352 0 : if (num <= has_recv_data)
353 : {
354 0 : arg = (chn << 24) | (C2S << 16) | (num & 0xffff); // THIS OBIVOUSLY SHOULD BE A MACRO
355 0 : retsts = ioctl(devfd_, M_IOC_BUF_GIVE, arg);
356 0 : if (retsts != 0)
357 : {
358 0 : __SS__ << "ioctl(devfd_, M_IOC_BUF_GIVE, arg)... return not 0." << __E__;
359 0 : perror(ss.str().c_str());
360 0 : DEV_TLOG(TLVL_ERROR) << ss.str();
361 : // __SS_THROW__;
362 0 : } // exit(1); } // Don't exit for now
363 :
364 : // increment our cached info
365 0 : mu2e_channel_info_[activeDeviceIndex_][chn][C2S].swIdx =
366 0 : idx_add(mu2e_channel_info_[activeDeviceIndex_][chn][C2S].swIdx, (int)num, activeDeviceIndex_, chn, C2S);
367 0 : if (num <= buffers_held_)
368 0 : buffers_held_ -= num;
369 : else
370 0 : buffers_held_ = 0;
371 : }
372 : else
373 0 : TLOG(TLVL_WARN) << "read_release num=" << num << " > has_recv_data=" << has_recv_data;
374 : }
375 0 : deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
376 0 : return retsts;
377 : } // end read_release()
378 :
379 0 : int mu2edev::read_register(uint16_t address, int tmo_ms, uint32_t* output)
380 : {
381 0 : auto start = std::chrono::steady_clock::now();
382 0 : if (simulator_ != nullptr)
383 : {
384 0 : return simulator_->read_register(address, tmo_ms, output);
385 : }
386 : m_ioc_reg_access_t reg;
387 0 : reg.reg_offset = address;
388 0 : reg.access_type = 0;
389 :
390 0 : int counter = 0;
391 0 : int errorCode = -99;
392 :
393 0 : while (counter < 5 && errorCode < 0)
394 : {
395 0 : errorCode = ioctl(devfd_, M_IOC_REG_ACCESS, ®);
396 0 : counter++;
397 0 : if (errorCode < 0) usleep(10000);
398 : }
399 0 : *output = reg.val;
400 0 : TRACE(TLVL_READ_REGISTER, UID_ + " - Read value 0x%x from register 0x%x errorcode %d", reg.val, address, errorCode);
401 0 : deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
402 0 : return errorCode;
403 : } // end read_register()
404 :
405 0 : int mu2edev::write_register(uint16_t address, int tmo_ms, uint32_t data)
406 : {
407 0 : auto start = std::chrono::steady_clock::now();
408 0 : auto retsts = -1;
409 0 : if (simulator_ != nullptr)
410 : {
411 0 : retsts = simulator_->write_register(address, tmo_ms, data);
412 : }
413 : else
414 : {
415 : m_ioc_reg_access_t reg;
416 0 : reg.reg_offset = address;
417 0 : reg.access_type = 1;
418 0 : reg.val = data;
419 0 : TRACE(TLVL_WRITE_REGISTER, UID_ + " - Writing value 0x%x to register 0x%x", data, address);
420 0 : if (debugFp_) fprintf(debugFp_, (UID_ + " - Writing value 0x%x to register 0x%x - time delta %ld\n").c_str(), data, address,
421 0 : std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - lastWriteTime_).count());
422 0 : retsts = ioctl(devfd_, M_IOC_REG_ACCESS, ®);
423 0 : lastWriteTime_ = start;
424 : }
425 0 : deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
426 0 : return retsts;
427 : } // end write_register()
428 :
429 0 : int mu2edev::write_register_checked(uint16_t address, int tmo_ms, uint32_t data, uint32_t* output)
430 : {
431 0 : auto start = std::chrono::steady_clock::now();
432 0 : auto retsts = -1;
433 0 : if (simulator_ != nullptr)
434 : {
435 0 : retsts = simulator_->write_register(address, tmo_ms, data);
436 0 : *output = data;
437 : }
438 : else
439 : {
440 : m_ioc_reg_access_t reg;
441 0 : reg.reg_offset = address;
442 0 : reg.access_type = 2;
443 0 : reg.val = data;
444 0 : if (debugFp_) fprintf(debugFp_, (UID_ + " - Writing value 0x%x to register 0x%x with readback - time delta %ld\n").c_str(), data, address,
445 0 : std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - lastWriteTime_).count());
446 0 : retsts = ioctl(devfd_, M_IOC_REG_ACCESS, ®);
447 0 : *output = reg.val;
448 0 : lastWriteTime_ = start;
449 :
450 0 : TRACE(TLVL_WRITE_REGISTER_CHECKED, UID_ + " - Writing value 0x%x to register 0x%x with readback=0x%x", data, address, *output);
451 : }
452 0 : deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
453 0 : return retsts;
454 : } // end write_register_checked()
455 :
456 0 : void mu2edev::meta_dump()
457 : {
458 0 : TRACE(TLVL_META_DUMP, UID_ + " - mu2edev::meta_dump");
459 0 : auto start = std::chrono::steady_clock::now();
460 0 : if (simulator_ == nullptr)
461 : {
462 0 : int retsts = 0;
463 0 : for (int chn = 0; chn < MU2E_MAX_CHANNELS; ++chn)
464 0 : for (int dir = 0; dir < 2; ++dir)
465 0 : if ((mu2e_mmap_ptrs_[activeDeviceIndex_][0][0][0] != NULL) ||
466 0 : ((retsts = init(DTCLib::DTC_SimMode_Disabled, 0)) == 0)) // Default-init mu2edev if not given guidance
467 : {
468 0 : for (unsigned buf = 0; buf < mu2e_channel_info_[activeDeviceIndex_][chn][dir].num_buffs; ++buf)
469 : {
470 0 : int* BC_p = (int*)mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][MU2E_MAP_META];
471 0 : printf("buf_%02d: %u\n", buf, BC_p[buf]);
472 : }
473 : }
474 0 : retsts = ioctl(devfd_, M_IOC_DUMP);
475 : }
476 0 : deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
477 0 : }
478 :
479 0 : int mu2edev::write_data(DTC_DMA_Engine const& chn, void* buffer, size_t bytes)
480 : {
481 0 : if (chn == DTC_DMA_Engine_DCS && !thread_owns_dcs_lock())
482 : {
483 0 : __SS__ << "write_data failed - dcs lock not held!" << __E__;
484 0 : __SS_THROW__;
485 : // return -2;
486 0 : }
487 :
488 0 : auto start = std::chrono::steady_clock::now();
489 0 : auto retsts = -1;
490 0 : if (simulator_ != nullptr)
491 : {
492 0 : retsts = simulator_->write_data(chn, buffer, bytes);
493 : }
494 : else
495 : {
496 0 : int dir = S2C;
497 0 : retsts = 0;
498 0 : unsigned delta = mu2e_chn_info_delta_(activeDeviceIndex_, chn, dir, &mu2e_channel_info_); // check cached info
499 0 : TRACE(TLVL_WRITE_DATA, UID_ + " - write_data delta=%u chn=%d dir=S2C, sz=%zu", delta, chn, bytes);
500 0 : while (delta <= 1 &&
501 0 : std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count() <
502 : 1000)
503 : {
504 : m_ioc_get_info_t get_info;
505 0 : get_info.chn = chn;
506 0 : get_info.dir = dir;
507 0 : get_info.tmo_ms = 1000;
508 0 : int sts = ioctl(devfd_, M_IOC_GET_INFO, &get_info);
509 0 : if (sts != 0)
510 : {
511 0 : __SS__ << "Failed mu2edev::write_data with M_IOC_GET_INFO... return not 0." << __E__;
512 0 : perror(ss.str().c_str());
513 0 : __SS_THROW__;
514 : // exit(1);
515 0 : }
516 0 : mu2e_channel_info_[activeDeviceIndex_][chn][dir] = get_info; // copy info struct
517 0 : delta = mu2e_chn_info_delta_(activeDeviceIndex_, chn, dir, &mu2e_channel_info_);
518 0 : usleep(1000);
519 : }
520 :
521 0 : if (delta <= 1)
522 : {
523 0 : __SS__ << "Failed mu2edev::write_data with HW_NOT_READING_BUFS." << __E__;
524 0 : perror(ss.str().c_str());
525 0 : __SS_THROW__;
526 : // kill(0, SIGUSR2);
527 : // exit(2);
528 0 : }
529 :
530 0 : unsigned idx = mu2e_channel_info_[activeDeviceIndex_][chn][dir].swIdx;
531 0 : void* data = ((mu2e_databuff_t*)(mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][MU2E_MAP_BUFF]))[idx];
532 0 : memcpy(data, buffer, bytes);
533 0 : unsigned long arg = (chn << 24) | (bytes & 0xffffff); // THIS OBIVOUSLY SHOULD BE A MACRO
534 :
535 0 : int retry = 15;
536 : do
537 : {
538 0 : retsts = ioctl(devfd_, M_IOC_BUF_XMIT, arg);
539 0 : if (retsts != 0)
540 : {
541 0 : TRACE(TLVL_WRITE_DATA, UID_ + " - write_data ioctl returned %d, errno=%d (%s), retrying.", retsts, errno, strerror(errno));
542 : // perror("M_IOC_BUF_XMIT");
543 0 : usleep(50000);
544 : } // exit(1); } // Take out the exit call for now
545 0 : retry--;
546 0 : } while (retry > 0 && retsts != 0);
547 : // increment our cached info
548 0 : if (retsts == 0)
549 : {
550 0 : mu2e_channel_info_[activeDeviceIndex_][chn][dir].swIdx =
551 0 : idx_add(mu2e_channel_info_[activeDeviceIndex_][chn][dir].swIdx, 1, activeDeviceIndex_, chn, dir);
552 : }
553 : }
554 0 : deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
555 0 : if (retsts >= 0) writeSize_ += bytes;
556 0 : return retsts;
557 : } // end write_data()
558 :
559 0 : int mu2edev::release_all(DTC_DMA_Engine const& chn)
560 : {
561 0 : TLOG_DEBUG(TLVL_RELEASE_ALL) << __PRETTY_FUNCTION__ << " called from\n"
562 0 : << otsStyleStackTrace();
563 0 : auto retsts = 0;
564 0 : TRACE_EXIT { TLOG_DEBUG(TLVL_RELEASE_ALL) << "Exit - retsts=" << retsts; };
565 0 : if (chn == DTC_DMA_Engine_DCS && !thread_owns_dcs_lock())
566 : {
567 0 : TRACE(TLVL_WARN, UID_ + " - release_all dcs lock not held!");
568 0 : retsts = -2;
569 0 : return retsts;
570 : }
571 0 : auto start = std::chrono::steady_clock::now();
572 0 : auto time_last_data = start;
573 :
574 0 : if (simulator_ != nullptr)
575 : {
576 0 : TRACE(TLVL_RELEASE_ALL, UID_ + " - release all!");
577 0 : retsts = simulator_->release_all(chn);
578 : }
579 : else
580 : {
581 0 : constexpr size_t kTimeCheckIntervalLoops = 100; // Recheck elapsed time every 100 loop iterations to avoid hot-spin clock polling.
582 0 : size_t time_check_counter = 0;
583 : while (1)
584 : {
585 0 : auto _tmo_ms = mu2e_channel_info_[activeDeviceIndex_][chn][C2S].tmo_ms;
586 0 : mu2e_channel_info_[activeDeviceIndex_][chn][C2S].tmo_ms = 0;
587 0 : int sts = ioctl(devfd_, M_IOC_GET_INFO, &mu2e_channel_info_[activeDeviceIndex_][chn][C2S]);
588 0 : mu2e_channel_info_[activeDeviceIndex_][chn][C2S].tmo_ms = _tmo_ms; // restore
589 0 : if (sts != 0)
590 : {
591 0 : __SS__ << "Failed mu2edev::release_all of chn=" << chn << " with M_IOC_GET_INFO... return " << sts << " which is not 0. " << strerror(errno) << __E__;
592 0 : perror(ss.str().c_str());
593 0 : __SS_THROW__;
594 0 : }
595 0 : auto has_recv_data = mu2e_chn_info_delta_(activeDeviceIndex_, chn, C2S, &mu2e_channel_info_); // reads cached value, need M_IOC_GET_INFO before to update
596 :
597 0 : if (has_recv_data)
598 : {
599 0 : TRACE(TLVL_RELEASE_ALL, UID_ + " - release_all calling read_release(chn=%d has_recv_data=%d", chn, has_recv_data);
600 0 : read_release(chn, has_recv_data);
601 0 : time_last_data = std::chrono::steady_clock::now();
602 : }
603 :
604 : // do not worry about silence on release_all for DCS (since we have lock)
605 0 : if (chn == DTC_DMA_Engine_DCS)
606 : {
607 0 : if (has_recv_data) continue;
608 :
609 0 : TRACE(TLVL_RELEASE_ALL, UID_ + " - release_all no data to release for chn=%d", chn);
610 0 : break;
611 : }
612 :
613 0 : if (++time_check_counter >= kTimeCheckIntervalLoops)
614 : {
615 0 : time_check_counter = 0;
616 0 : auto now = std::chrono::steady_clock::now();
617 0 : auto nano_since_last_data = std::chrono::duration_cast<std::chrono::nanoseconds>(now - time_last_data).count();
618 0 : if (!has_recv_data && nano_since_last_data > 1000000000LL) // require a full 1 second of buffer idle before declaring release_all complete
619 : {
620 0 : TRACE(TLVL_RELEASE_ALL, UID_ + " - release_all done after buffers idle for >= 1s...");
621 0 : break;
622 : }
623 :
624 0 : auto nano_since_start = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start).count();
625 0 : if (nano_since_start > 5000000000LL) // 5 seconds overall cap
626 : {
627 0 : __SS__ << "mu2edev::release_all of chn=" << chn << " timed out after 5 seconds while attempting to release buffers (data never went idle for the required 1 second)." << __E__;
628 0 : __SS_THROW__;
629 0 : }
630 : }
631 0 : }
632 :
633 : // releaseBuffersHeld if allowing multiple buffers to be held by user space (e.g. for Data DMA channel, which is different for CFO vs DTC)
634 0 : if (chn == DTC_DMA_Engine_DAQ)
635 0 : buffers_held_ = 0;
636 :
637 : // Stamp first qword of every receive buffer with 0xdeadbeef so the next
638 : // consumer can tell which buffers have been freshly filled by hardware
639 : // (anything still reading 0xdeadbeef at qword[0] has not been touched
640 : // since this release_all completed).
641 : {
642 0 : auto* bufArray = (mu2e_databuff_t*)(mu2e_mmap_ptrs_[activeDeviceIndex_][chn][C2S][MU2E_MAP_BUFF]);
643 0 : for (auto bufIdx = 0; bufIdx < MU2E_NUM_RECV_BUFFS; ++bufIdx)
644 : {
645 0 : *reinterpret_cast<uint64_t*>(bufArray[bufIdx]) = 0xdeadbeefULL;
646 : }
647 0 : unsigned lastReleasedIdx = idx_add(mu2e_channel_info_[activeDeviceIndex_][chn][C2S].swIdx, -1, activeDeviceIndex_, chn, C2S);
648 0 : TRACE(TLVL_RELEASE_ALL, UID_ + " - release_all stamped 0xdeadbeef on first qword of all %d receive buffers (chn=%d) last cleared buffer index #%02u",
649 : MU2E_NUM_RECV_BUFFS, chn, lastReleasedIdx);
650 : }
651 : }
652 0 : deviceTime_ += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count();
653 0 : return retsts;
654 0 : } // end release_all()
655 :
656 0 : void mu2edev::close()
657 : {
658 0 : if (simulator_ != nullptr)
659 : {
660 0 : delete simulator_;
661 0 : simulator_ = nullptr;
662 0 : TLOG(TLVL_INFO) << UID_ << " " << __PRETTY_FUNCTION__ << " - mu2edev::close() closed simulated device";
663 : }
664 0 : else if (devfd_ != -1)
665 : {
666 : // mmap also keeps handle to the device! so must unmap
667 0 : for (int chn = 0; chn < MU2E_MAX_CHANNELS; ++chn)
668 0 : for (unsigned dir = 0; dir < 2; ++dir)
669 0 : for (unsigned map = 0; map < 2; ++map)
670 : {
671 0 : if (mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map] &&
672 0 : mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map] != MAP_FAILED)
673 : {
674 0 : size_t length = mu2e_channel_info_[activeDeviceIndex_][chn][dir].num_buffs *
675 0 : ((map == MU2E_MAP_BUFF) ? mu2e_channel_info_[activeDeviceIndex_][chn][dir].buff_size : sizeof(int));
676 :
677 0 : TRACE(TLVL_INIT_DMA_ENGINE, UID_ + " - mu2edev::init chnDirMap2offset=%lu mu2e_mmap_ptrs_[offset][%d][%d][%d]=%p p=prot l=%lu",
678 : // offset,
679 : activeDeviceIndex_, chn,
680 : dir, map, mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map],
681 : // prot == PROT_READ ? 'R' : 'W',
682 : length);
683 :
684 0 : munmap((void*)mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map],
685 : length);
686 0 : mu2e_mmap_ptrs_[activeDeviceIndex_][chn][dir][map] = nullptr;
687 : }
688 : }
689 :
690 : // after unmap, then close device
691 0 : ::close(devfd_);
692 0 : devfd_ = -1;
693 0 : TLOG(TLVL_INFO) << UID_ << " " << __PRETTY_FUNCTION__ << " - mu2edev::close() closed device file descriptor";
694 : }
695 0 : TLOG(TLVL_DEBUG) << UID_ << " " << __PRETTY_FUNCTION__ << " - mu2edev::close() done: " << otsStyleStackTrace() << __E__;
696 0 : } // end close()
697 :
698 0 : void mu2edev::begin_dcs_transaction()
699 : {
700 0 : TLOG_DEBUG(TLVL_BEGIN_DCS_TRANSACTION) << UID_ << " " << __PRETTY_FUNCTION__ << " called from\n"
701 0 : << otsStyleStackTrace();
702 0 : TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transation: who has lock? dcs_lock_held_=%llu, threadid=%llu", dcs_locks_[activeDeviceIndex_].lock_count.load(), std::this_thread::get_id());
703 0 : if (thread_owns_dcs_lock())
704 : {
705 0 : TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transation: device lock already held by this thread. counter=%d, threadid=%llu", dcs_locks_[activeDeviceIndex_].lock_count.load(), std::this_thread::get_id());
706 0 : dcs_locks_[activeDeviceIndex_].lock_count++;
707 0 : return;
708 : }
709 0 : if (!dcs_lock_free())
710 0 : TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transaction: device lock for this instance held by another thread! Waiting... counter=%d, threadid=%llu", dcs_locks_[activeDeviceIndex_].lock_count.load(), std::this_thread::get_id());
711 :
712 0 : int tmo_ms = 1000; // 1s timeout
713 0 : auto start = std::chrono::steady_clock::now();
714 0 : TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transaction: waiting for library thread lock");
715 0 : while (!dcs_lock_free() && !thread_owns_dcs_lock() && (tmo_ms <= 0 || std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count() < tmo_ms))
716 : {
717 0 : std::this_thread::sleep_for(std::chrono::microseconds(100));
718 : }
719 :
720 0 : if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count() >= tmo_ms)
721 : {
722 0 : TRACE(TLVL_ERROR, UID_ + " begin_dcs_transaction: timed out waiting for library thread lock");
723 0 : std::string file = __FILE__;
724 : throw std::runtime_error(
725 0 : std::string(file.find("/srcs/") != std::string::npos ? file.substr(file.find("/srcs") + 6) : file) + ":" +
726 0 : std::to_string(__LINE__) + " | " +
727 0 : UID_ + " - mu2e Device could not take lock - library-internal lock error. Throwing exception.");
728 0 : }
729 :
730 0 : if (simulator_ != nullptr)
731 : {
732 0 : dcs_locks_[activeDeviceIndex_].thread_id = std::this_thread::get_id();
733 0 : dcs_locks_[activeDeviceIndex_].lock_count++;
734 0 : TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transaction: sim mode, taking library thread lock and returning, counter=%d, threadid=%llu", dcs_locks_[activeDeviceIndex_].lock_count.load(), std::this_thread::get_id());
735 0 : return;
736 : }
737 :
738 0 : TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transaction: waiting for driver lock");
739 0 : int retsts = -1;
740 0 : while (retsts == -1 && (tmo_ms <= 0 || std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count() < tmo_ms))
741 : {
742 0 : retsts = ioctl(devfd_, M_IOC_DCS_LOCK);
743 0 : if ((retsts == -EAGAIN) || ((retsts == -1) && (errno == EAGAIN)))
744 : {
745 0 : TRACE(TLVL_BEGIN_DCS_TRANSACTION_VERBOSE, UID_ + " begin_dcs_transaction: ioctl returned %d (errno %d), waiting and retrying, counter=%d, threadid=%llu", retsts, errno, dcs_locks_[activeDeviceIndex_].lock_count.load(), std::this_thread::get_id());
746 0 : std::this_thread::sleep_for(std::chrono::microseconds(100));
747 0 : }
748 0 : else if (retsts != 0)
749 : {
750 0 : TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transaction: Method not supported by driver, taking library lock and returning. ioctl returned %d, errno %d", retsts, errno);
751 0 : dcs_locks_[activeDeviceIndex_].thread_id = std::this_thread::get_id();
752 0 : dcs_locks_[activeDeviceIndex_].lock_count++;
753 0 : TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transaction: Method not supported by driver, taking library lock and returning. ioctl returned %d, errno %d, counter=%d, threadid=%llu", retsts, errno, dcs_locks_[activeDeviceIndex_].lock_count.load(), std::this_thread::get_id());
754 0 : return;
755 : }
756 : else
757 : {
758 0 : dcs_locks_[activeDeviceIndex_].thread_id = std::this_thread::get_id();
759 0 : dcs_locks_[activeDeviceIndex_].lock_count++;
760 0 : TRACE(TLVL_BEGIN_DCS_TRANSACTION, UID_ + " begin_dcs_transaction: have driver lock, setting library lock and returning true, counter=%d, threadid=%llu", dcs_locks_[activeDeviceIndex_].lock_count.load(), std::this_thread::get_id());
761 0 : return;
762 : }
763 : }
764 :
765 0 : TRACE(TLVL_ERROR, UID_ + " begin_dcs_transaction: timed out waiting for driver lock");
766 : // force unlock
767 0 : end_dcs_transaction(true /* force */);
768 0 : std::string file = __FILE__;
769 : throw std::runtime_error(
770 0 : std::string(file.find("/srcs/") != std::string::npos ? file.substr(file.find("/srcs") + 6) : file) + ":" +
771 0 : std::to_string(__LINE__) + " | " +
772 0 : UID_ + " - mu2e Device could not take lock - M_IOC_DCS_LOCK error. Attempting to force unlock and throwing exception.");
773 0 : } // end begin_dcs_transaction()
774 :
775 0 : void mu2edev::end_dcs_transaction(bool force)
776 : {
777 0 : TLOG_DEBUG(TLVL_END_DCS_TRANSACTION) << UID_ << " " << __PRETTY_FUNCTION__ << " called from\n"
778 0 : << otsStyleStackTrace();
779 0 : TRACE(TLVL_END_DCS_TRANSACTION, UID_ + " end_dcs_transaction: checking for ability to release lock force=%d, counter=%d", force, dcs_locks_[activeDeviceIndex_].lock_count.load());
780 0 : if (force || thread_owns_dcs_lock())
781 : {
782 0 : dcs_locks_[activeDeviceIndex_].lock_count--;
783 0 : TRACE(TLVL_END_DCS_TRANSACTION, UID_ + " end_dcs_transaction: after decrement lock counter, force=%d, counter=%d", force, dcs_locks_[activeDeviceIndex_].lock_count.load());
784 0 : if (dcs_locks_[activeDeviceIndex_].lock_count.load() <= 0 || force)
785 : {
786 0 : dcs_locks_[activeDeviceIndex_].lock_count = 0;
787 0 : if (simulator_ == nullptr)
788 : {
789 0 : TRACE(TLVL_END_DCS_TRANSACTION, UID_ + " end_dcs_transaction: releasing driver lock");
790 0 : int retsts = ioctl(devfd_, M_IOC_DCS_RELEASE);
791 0 : if (retsts != 0)
792 : {
793 0 : TRACE(TLVL_END_DCS_TRANSACTION, UID_ + " end_dcs_transaction: IOCTL returned %d!", retsts);
794 0 : perror("M_IOC_DCS_RELEASE");
795 : }
796 : }
797 0 : TRACE(TLVL_END_DCS_TRANSACTION, UID_ + " end_dcs_transaction: releasing library lock");
798 0 : dcs_locks_[activeDeviceIndex_].thread_id = NULL_TID;
799 : }
800 : }
801 :
802 0 : } // end end_dcs_transaction()
803 :
804 0 : bool mu2edev::thread_owns_dcs_lock()
805 : {
806 0 : return dcs_locks_[activeDeviceIndex_].thread_id.load() == std::this_thread::get_id();
807 : }
808 0 : bool mu2edev::dcs_lock_free()
809 : {
810 0 : return dcs_locks_[activeDeviceIndex_].thread_id.load() == NULL_TID;
811 : }
812 :
813 0 : std::string mu2edev::get_driver_version()
814 : {
815 0 : TRACE(TLVL_GET_DRIVER_VERSION, UID_ + " get_driver_version BEGIN");
816 0 : if (simulator_ != nullptr) { return "SIMULATED"; }
817 : mu2e_string_t output;
818 0 : int retsts = ioctl(devfd_, M_IOC_GET_VERSION, &output);
819 :
820 0 : if (retsts != 0)
821 : {
822 0 : TRACE(TLVL_GET_DRIVER_VERSION, UID_ + " get_driver_version: IOCTL returned %d!", retsts);
823 0 : perror("M_IOC_GET_VERSION");
824 : }
825 :
826 0 : std::string outstr = std::string(output);
827 0 : if (outstr == "")
828 : {
829 0 : return "UNSUPPORTED_DRIVER";
830 : }
831 :
832 0 : return outstr;
833 0 : } // end get_driver_version
834 :
835 : /// @brief Dump contents of receive buffers in a loop with various options for controlling format and behavior. Intended for debugging and development use, not for production use (can cause log-file chaos if used excessively).
836 : /// @param chn
837 : /// @param optsmsk - bitmask of options controlling behavior and format.
838 : /// Bit 0 (1) to suppress the initial screen clear; when this bit is not set, the screen is cleared once before the loop starts,
839 : /// bit 1 (2) to run only one iteration,
840 : /// bit 2 (4) reserved/legacy screen-clear option (current implementation does not clear between iterations),
841 : /// bit 3 (8) to dump all 8192 bytes of each buffer instead of just first 4 qwords,
842 : /// bit 4 (16) to include stack trace at end of output,
843 : /// bit 28 (268435456) to force spy to run even if it has already been called once for this instance (which is usually enough and more can cause log-file chaos).
844 : /// spyHasOccurred_ can also be reset to force spy to trigger once again, by calling mu2edev::resetSpyHasOccurred()
845 : /// @param out
846 0 : void mu2edev::spy(int chn, unsigned optsmsk, std::ostream& out /* = std::cout */)
847 : {
848 0 : if (!(optsmsk & (1 << 28)) && // set bit 28 to force spy to happen
849 0 : spyHasOccurred_ && !TTEST(50)) // set debug+50 to re-enable if you know what you're doing and want to see more than the first spy() call for a given instance in the logs (which is usually enough and more can cause log-file chaos)
850 : {
851 0 : TLOG(TLVL_WARNING) << "spy() already executed for this mu2edev instance (" << UID_ << "); skipping to avoid log-file chaos. Call resetSpyHasOccurred() to re-enable.";
852 0 : return;
853 : }
854 0 : if (!(optsmsk & (1 << 28))) // only flag spy occurring if not forcing it (with bit 28)
855 : {
856 0 : TLOG_INFO() << "Flagging that spy() has occurred.";
857 0 : spyHasOccurred_ = true;
858 : }
859 :
860 0 : TLOG_INFO() << "spy";
861 : void* buffer;
862 : uint64_t* datap;
863 0 : out << "optsmsk=" << optsmsk << '\n';
864 0 : if (!(optsmsk & 1)) out << "\033[0;0H\033[J";
865 0 : while (++spyIteration_)
866 : { // watch out (when using integer type) for compiler error: iteration 2147483647 invokes undefined behavior [-Werror=aggressive-loop-optimizations]
867 0 : unsigned lastReleasedIdx = idx_add(mu2e_channel_info_[activeDeviceIndex_][chn][C2S].swIdx, -1, activeDeviceIndex_, chn, C2S);
868 0 : out << "spy iteration: " << std::dec << std::setw(7) << std::setfill(' ') << spyIteration_
869 0 : << " -- last released buffer index #" << std::setw(2) << std::setfill('0') << lastReleasedIdx << '\n';
870 0 : for (auto bufIdx = 0; bufIdx < MU2E_NUM_RECV_BUFFS;)
871 : {
872 0 : out << std::dec << std::setw(3) << std::setfill(' ') << bufIdx << " ";
873 0 : for (auto buf = 0; buf < ((optsmsk & 8) ? 1 : 3); ++buf)
874 : {
875 0 : buffer = ((mu2e_databuff_t*)(mu2e_mmap_ptrs_[activeDeviceIndex_][chn][C2S][MU2E_MAP_BUFF]))[bufIdx++];
876 0 : datap = (uint64_t*)buffer;
877 0 : out << "0x" << std::hex << std::setw(16) << std::setfill('0') << datap[0];
878 0 : for (auto dd = 1; dd < ((optsmsk & 8) ? 13 : 4); ++dd)
879 : {
880 0 : out << " " << std::hex << std::setw(16) << std::setfill('0') << datap[dd];
881 : }
882 0 : if (optsmsk & 8)
883 : {
884 0 : constexpr size_t totalQwords = sizeof(mu2e_databuff_t) / sizeof(uint64_t); // 8192
885 0 : out << " ...";
886 0 : for (size_t dd = totalQwords - 13; dd < totalQwords; ++dd)
887 : {
888 0 : out << " " << std::hex << std::setw(16) << std::setfill('0') << datap[dd];
889 : }
890 : }
891 0 : if (!(bufIdx < MU2E_NUM_RECV_BUFFS)) break;
892 0 : if (buf < 3) out << " ";
893 : }
894 0 : out << '\n';
895 : }
896 0 : if (optsmsk & 2) break;
897 0 : sleep(1); // user should control-C here :)
898 0 : if (optsmsk & 4) out << "\033[0;0H";
899 : }
900 0 : if (optsmsk & 16)
901 : out << "\n\n"
902 0 : << otsStyleStackTrace() << std::flush;
903 : } // end spy()
|