rogue
Loading...
Searching...
No Matches
Prbs.cpp
Go to the documentation of this file.
1
17#include "rogue/Directives.h"
18
20
21#include <inttypes.h>
22#include <stdarg.h>
23#include <sys/time.h>
24#include <unistd.h>
25
26#include <cstdio>
27#include <cstring>
28#include <memory>
29
30#include "rogue/GeneralError.h"
31#include "rogue/GilRelease.h"
32#include "rogue/Logging.h"
39
41namespace ru = rogue::utilities;
42
43#ifndef NO_PYTHON
44 #include <boost/python.hpp>
45namespace bp = boost::python;
46#endif
47
49ru::PrbsPtr ru::Prbs::create() {
50 ru::PrbsPtr p = std::make_shared<ru::Prbs>();
51 return (p);
52}
53
55ru::Prbs::Prbs() {
56 txThread_ = NULL;
57 rxSeq_ = 0;
58 rxErrCount_ = 0;
59 rxCount_ = 0;
60 rxBytes_ = 0;
61 rxEnable_ = true;
62 txSeq_ = 0;
63 txSize_ = 0;
64 txErrCount_ = 0;
65 txCount_ = 0;
66 txBytes_ = 0;
67 checkPl_ = true;
68 genPl_ = true;
69 rxLog_ = rogue::Logging::create("prbs.rx");
70 txLog_ = rogue::Logging::create("prbs.tx");
71 txPeriod_ = 0;
72
73 // Init width = 32
74 width_ = 32;
75 byteWidth_ = 4;
76 minSize_ = 12;
77 sendCount_ = false;
78
79 // Init 4 taps
80 tapCnt_ = 4;
81 taps_ = reinterpret_cast<uint8_t*>(malloc(sizeof(uint8_t) * tapCnt_));
82 taps_[0] = 1;
83 taps_[1] = 2;
84 taps_[2] = 6;
85 taps_[3] = 31;
86
87 gettimeofday(&lastRxTime_, NULL);
88 gettimeofday(&lastTxTime_, NULL);
89
90 lastRxCount_ = 0;
91 lastRxBytes_ = 0;
92 rxRate_ = 0.0;
93 rxBw_ = 0.0;
94
95 lastTxCount_ = 0;
96 lastTxBytes_ = 0;
97 txRate_ = 0.0;
98 txBw_ = 0.0;
99}
100
102ru::Prbs::~Prbs() {
103 free(taps_);
104}
105
107double ru::Prbs::updateTime(struct timeval* last) {
108 struct timeval cmp;
109 struct timeval now;
110 struct timeval per;
111 double ret;
112
113 cmp.tv_sec = 1;
114 cmp.tv_usec = 0;
115
116 gettimeofday(&now, NULL);
117
118 timersub(&now, last, &per);
119
120 if (timercmp(&per, &cmp, >)) {
121 ret = static_cast<float>(per.tv_sec) + static_cast<float>(per.tv_usec) / 1e6;
122 gettimeofday(last, NULL);
123 } else {
124 ret = 0.0;
125 }
126 return ret;
127}
128
130void ru::Prbs::setWidth(uint32_t width) {
131 if ((width > (MaxBytes * 8)) || (width % 32) != 0) throw(rogue::GeneralError("Prbs::setWidth", "Invalid width."));
132
133 rogue::GilRelease noGil;
134 std::lock_guard<std::mutex> lockT(pMtx_);
135
136 width_ = width;
137 byteWidth_ = width / 8;
138 minSize_ = byteWidth_ * 3;
139}
140
142void ru::Prbs::setTaps(uint32_t tapCnt, uint8_t* taps) {
143 uint32_t i;
144
145 std::lock_guard<std::mutex> lockT(pMtx_);
146
147 free(taps_);
148 tapCnt_ = tapCnt;
149 taps_ = reinterpret_cast<uint8_t*>(malloc(sizeof(uint8_t) * tapCnt));
150
151 for (i = 0; i < tapCnt_; i++) taps_[i] = taps[i];
152}
153
154#ifndef NO_PYTHON
155
157void ru::Prbs::setTapsPy(boost::python::object p) {
158 Py_buffer pyBuf;
159
160 if (PyObject_GetBuffer(p.ptr(), &pyBuf, PyBUF_SIMPLE) < 0)
161 throw(rogue::GeneralError("Prbs::setTapsPy", "Python Buffer Error"));
162
163 setTaps(pyBuf.len, reinterpret_cast<uint8_t*>(pyBuf.buf));
164 PyBuffer_Release(&pyBuf);
165}
166
167#endif
168
170void ru::Prbs::sendCount(bool state) {
171 rogue::GilRelease noGil;
172 std::lock_guard<std::mutex> lockT(pMtx_);
173
174 sendCount_ = state;
175}
176
177void ru::Prbs::flfsr(uint8_t* data) {
178 uint32_t iByte;
179 uint32_t iBit;
180 uint32_t x;
181 bool msbOut;
182 bool lsbIn = false;
183
184 for (x = 0; x < tapCnt_; x++) {
185 iByte = taps_[x] / 8;
186 iBit = taps_[x] % 8;
187 lsbIn ^= ((data[iByte] >> iBit) & 0x1);
188 }
189
190 for (x = 0; x < byteWidth_; x++) {
191 msbOut = data[x] & 0x80;
192 data[x] <<= 1;
193 data[x] |= lsbIn;
194 lsbIn = msbOut;
195 }
196}
197
199void ru::Prbs::runThread() {
200 txLog_->logThreadId();
201
202 while (threadEn_) {
203 genFrame(txSize_);
204 if ( txPeriod_ > 0 ) usleep(txPeriod_);
205 }
206}
207
209void ru::Prbs::enable(uint32_t size) {
210 // Verify size first
211 if (((size % byteWidth_) != 0) || size < minSize_) throw rogue::GeneralError("Prbs::enable", "Invalid frame size");
212
213 if (txThread_ == NULL) {
214 txSize_ = size;
215 threadEn_ = true;
216 txThread_ = new std::thread(&Prbs::runThread, this);
217
218 // Set a thread name
219#ifndef __MACH__
220 pthread_setname_np(txThread_->native_handle(), "PrbsTx");
221#endif
222 }
223}
224
226void ru::Prbs::disable() {
227 if (txThread_ != NULL) {
228 rogue::GilRelease noGil;
229 threadEn_ = false;
230 txThread_->join();
231 delete txThread_;
232 txThread_ = NULL;
233 }
234}
235
237bool ru::Prbs::getRxEnable() {
238 return rxEnable_;
239}
240
242void ru::Prbs::setRxEnable(bool en) {
243 rogue::GilRelease noGil;
244 pMtx_.lock();
245 rxEnable_ = en;
246 pMtx_.unlock();
247}
248
250uint32_t ru::Prbs::getRxErrors() {
251 return (rxErrCount_);
252}
253
255uint32_t ru::Prbs::getRxCount() {
256 return (rxCount_);
257}
258
260uint64_t ru::Prbs::getRxBytes() {
261 return (rxBytes_);
262}
263
265uint32_t ru::Prbs::getTxErrors() {
266 return (txErrCount_);
267}
268
270uint32_t ru::Prbs::getTxCount() {
271 return (txCount_);
272}
273
275double ru::Prbs::getRxRate() {
276 return rxRate_;
277}
278
280double ru::Prbs::getRxBw() {
281 return rxBw_;
282}
283
285double ru::Prbs::getTxRate() {
286 return txRate_;
287}
288
290uint32_t ru::Prbs::getTxPeriod() {
291 return txPeriod_;
292}
293
295void ru::Prbs::setTxPeriod(uint32_t value) {
296 txPeriod_ = value;
297}
298
300double ru::Prbs::getTxBw() {
301 return txBw_;
302}
303
305uint64_t ru::Prbs::getTxBytes() {
306 return (txBytes_);
307}
308
310void ru::Prbs::checkPayload(bool state) {
311 checkPl_ = state;
312}
313
315void ru::Prbs::genPayload(bool state) {
316 genPl_ = state;
317}
318
320// Counters should really be locked!
321void ru::Prbs::resetCount() {
322 pMtx_.lock();
323 txErrCount_ = 0;
324 txCount_ = 0;
325 txBytes_ = 0;
326 rxErrCount_ = 0;
327 rxCount_ = 0;
328 rxBytes_ = 0;
329 pMtx_.unlock();
330}
331
333void ru::Prbs::genFrame(uint32_t size) {
334 ris::FrameIterator frIter;
335 ris::FrameIterator frEnd;
336 uint32_t frSeq[MaxBytes / 4];
337 uint32_t frSize[MaxBytes / 4];
338 uint32_t wCount[MaxBytes / 4];
339 uint8_t data[MaxBytes];
340 double per;
341 ris::FramePtr fr;
342
343 rogue::GilRelease noGil;
344 std::lock_guard<std::mutex> lock(pMtx_);
345
346 // Verify size first
347 if (((size % byteWidth_) != 0) || size < minSize_)
348 throw rogue::GeneralError("Prbs::genFrame", "Invalid frame size");
349
350 // Setup size
351 memset(frSize, 0, MaxBytes);
352 frSize[0] = (size / byteWidth_) - 1;
353
354 // Setup sequence
355 memset(frSeq, 0, MaxBytes);
356 frSeq[0] = txSeq_;
357
358 // Setup counter
359 memset(wCount, 0, MaxBytes);
360
361 // Get frame
362 fr = reqFrame(size, true);
363 fr->setPayload(size);
364
365 frIter = fr->begin();
366 frEnd = frIter + size;
367
368 // First word is sequence
369 ris::toFrame(frIter, byteWidth_, frSeq);
370 ++wCount[0];
371
372 // Second word is size
373 ris::toFrame(frIter, byteWidth_, frSize);
374 ++wCount[0];
375
376 if (genPl_) {
377 // Init data
378 std::memcpy(data, frSeq, byteWidth_);
379
380 // Generate payload
381 while (frIter != frEnd) {
382 if (sendCount_) {
383 ris::toFrame(frIter, byteWidth_, wCount);
384 } else {
385 flfsr(data);
386 ris::toFrame(frIter, byteWidth_, data);
387 }
388 ++wCount[0];
389 }
390 }
391
392 sendFrame(fr);
393
394 // Update counters
395 txSeq_++;
396 txCount_++;
397 txBytes_ += size;
398
399 if ((per = updateTime(&lastTxTime_)) > 0.0) {
400 txRate_ = static_cast<double>(txCount_ - lastTxCount_) / per;
401 txBw_ = static_cast<double>(txBytes_ - lastTxBytes_) / per;
402 lastTxCount_ = txCount_;
403 lastTxBytes_ = txBytes_;
404 }
405}
406
408void ru::Prbs::acceptFrame(ris::FramePtr frame) {
409 ris::FrameIterator frIter;
410 ris::FrameIterator frEnd;
411 uint32_t frSeq[MaxBytes / 4];
412 uint32_t frSize[MaxBytes / 4];
413 uint32_t expSeq;
414 uint32_t expSize;
415 uint32_t size;
416 uint32_t pos;
417 uint32_t x;
418 uint8_t expData[MaxBytes];
419 double per;
420 char debugA[10000];
421 char debugB[1000];
422
423 rogue::GilRelease noGil;
424
425 while (!rxEnable_) usleep(10000);
426
427 ris::FrameLockPtr fLock = frame->lock();
428 std::lock_guard<std::mutex> lock(pMtx_);
429
430 size = frame->getPayload();
431 frIter = frame->begin();
432 frEnd = frame->end();
433
434 // Check for frame errors
435 if (frame->getError()) {
436 rxLog_->warning("Frame error field is set: 0x%" PRIx8, frame->getError());
437 rxErrCount_++;
438 return;
439 }
440
441 // Verify size
442 if (((size % byteWidth_) != 0) || size < minSize_) {
443 rxLog_->warning("Size violation size=%" PRIu32 ", count=%" PRIu32, size, rxCount_);
444 rxErrCount_++;
445 return;
446 }
447
448 // First word is sequence
449 ris::fromFrame(frIter, byteWidth_, frSeq);
450 expSeq = rxSeq_;
451 rxSeq_ = frSeq[0] + 1;
452
453 // Second word is size
454 ris::fromFrame(frIter, byteWidth_, frSize);
455 expSize = (frSize[0] + 1) * byteWidth_;
456
457 // Check size and sequence
458 // Accept any sequence if our local count is zero
459 // incoming frames with seq = 0 never cause errors and treated as a restart
460 if ((expSize != size) || (frSeq[0] != 0 && expSeq != 0 && frSeq[0] != expSeq)) {
461 rxLog_->warning("Bad header. expSize=%" PRIu32 " gotSize=%" PRIu32 " expSeq=%" PRIu32 " gotSeq=%" PRIu32
462 " nxtSeq=%" PRIu32 " count=%" PRIu32,
463 expSize,
464 size,
465 expSeq,
466 frSeq[0],
467 rxSeq_,
468 rxCount_);
469 rxErrCount_++;
470 return;
471 }
472
473 // Is payload checking enabled
474 if (checkPl_) {
475 // Init data
476 std::memcpy(expData, frSeq, byteWidth_);
477 pos = 0;
478
479 // Read payload
480 while (frIter != frEnd) {
481 flfsr(expData);
482
483 if (!std::equal(frIter, frIter + byteWidth_, expData)) {
484 snprintf(debugA,
485 sizeof(debugA),
486 "Bad value at index %" PRIu32 ". count=%" PRIu32 ", size=%" PRIu32,
487 pos,
488 rxCount_,
489 (size / byteWidth_) - 1);
490
491 for (x = 0; x < byteWidth_; x++) {
492 snprintf(debugB,
493 sizeof(debugB),
494 "\n %" PRIu32 ":%" PRIu32 " Got=0x%" PRIx8 " Exp=0x%" PRIx8,
495 pos,
496 x,
497 *(frIter + x),
498 *(expData + x));
499 snprintf(debugA + strlen(debugA), sizeof(debugA) - strlen(debugA), "%s", debugB);
500 }
501 rxLog_->warning(debugA);
502 rxErrCount_++;
503 return;
504 }
505 frIter += byteWidth_;
506 ++pos;
507 }
508 }
509
510 rxCount_++;
511 rxBytes_ += size;
512
513 if ((per = updateTime(&lastRxTime_)) > 0.0) {
514 rxRate_ = static_cast<double>(rxCount_ - lastRxCount_) / per;
515 rxBw_ = static_cast<double>(rxBytes_ - lastRxBytes_) / per;
516 lastRxCount_ = rxCount_;
517 lastRxBytes_ = rxBytes_;
518 }
519}
520
521void ru::Prbs::setup_python() {
522#ifndef NO_PYTHON
523 // Keep the manual method docstrings, but suppress Boost.Python's generated
524 // Python/C++ signatures. The autogenerated forms are not very readable in
525 // the Sphinx Python API pages for exported C++ classes.
526 bp::docstring_options doc_options(true, false, false);
527
528 bp::class_<ru::Prbs, ru::PrbsPtr, bp::bases<ris::Master, ris::Slave>, boost::noncopyable>(
529 "Prbs",
530 "PRBS generator/checker that can act as both stream master and slave.\n\n"
531 "The engine can generate deterministic pseudo-random frames and verify\n"
532 "received frames for bit-error testing. It supports both one-shot frame\n"
533 "generation and optional background transmit mode.",
534 bp::init<>("Construct a PRBS generator/checker with default taps and width."))
535 .def("genFrame", &ru::Prbs::genFrame, bp::args("size"), "Generate and transmit one PRBS frame.")
536 .def("enable",
537 &ru::Prbs::enable,
538 bp::args("size"),
539 "Enable periodic background PRBS frame generation using the configured TX period.")
540 .def("disable", &ru::Prbs::disable, "Disable periodic background PRBS frame generation.")
541 .def("setWidth", &ru::Prbs::setWidth, bp::args("width"), "Set the PRBS generator/checker width in bits.")
542 .def("setTaps",
543 &ru::Prbs::setTapsPy,
544 bp::args("taps"),
545 "Configure the PRBS LFSR tap positions from a bytes-like object.")
546 .def("getRxEnable", &ru::Prbs::getRxEnable, "Return whether receive-side checking is enabled.")
547 .def("setRxEnable", &ru::Prbs::setRxEnable, bp::args("state"), "Enable or disable receive-side checking.")
548 .def("getRxErrors", &ru::Prbs::getRxErrors, "Return the receive-side error count.")
549 .def("getRxCount", &ru::Prbs::getRxCount, "Return the total received frame count.")
550 .def("getRxRate", &ru::Prbs::getRxRate, "Return the computed receive frame rate in frames/second.")
551 .def("getRxBw", &ru::Prbs::getRxBw, "Return the computed receive bandwidth in bytes/second.")
552 .def("getRxBytes", &ru::Prbs::getRxBytes, "Return the total received payload byte count.")
553 .def("getTxErrors", &ru::Prbs::getTxErrors, "Return the transmit-side error count.")
554 .def("getTxCount", &ru::Prbs::getTxCount, "Return the total transmitted frame count.")
555 .def("getTxBytes", &ru::Prbs::getTxBytes, "Return the total transmitted payload byte count.")
556 .def("getTxPeriod", &ru::Prbs::getTxPeriod, "Return the periodic transmit interval in microseconds.")
557 .def("setTxPeriod",
558 &ru::Prbs::setTxPeriod,
559 bp::args("period"),
560 "Set the periodic transmit interval in microseconds.")
561 .def("getTxRate", &ru::Prbs::getTxRate, "Return the computed transmit frame rate in frames/second.")
562 .def("getTxBw", &ru::Prbs::getTxBw, "Return the computed transmit bandwidth in bytes/second.")
563 .def("checkPayload",
564 &ru::Prbs::checkPayload,
565 bp::args("state"),
566 "Enable or disable PRBS payload checking on received frames.")
567 .def("genPayload",
568 &ru::Prbs::genPayload,
569 bp::args("state"),
570 "Enable or disable PRBS payload generation on transmitted frames.")
571 .def("resetCount", &ru::Prbs::resetCount, "Reset transmit and receive counters.")
572 .def("sendCount",
573 &ru::Prbs::sendCount,
574 bp::args("state"),
575 "Enable or disable insertion of frame counters into generated payloads.");
576
577 bp::implicitly_convertible<ru::PrbsPtr, ris::SlavePtr>();
578 bp::implicitly_convertible<ru::PrbsPtr, ris::MasterPtr>();
579#endif
580}
Generic Rogue exception type.
RAII helper that releases the Python GIL for a scope.
Definition GilRelease.h:36
static std::shared_ptr< rogue::Logging > create(const std::string &name, bool quiet=false)
Creates a logger instance.
Definition Logging.cpp:60
Random-access byte iterator across a Frame payload.
static void fromFrame(rogue::interfaces::stream::FrameIterator &iter, uint32_t size, void *dst)
Copies bytes from a frame iterator to a destination pointer.
std::shared_ptr< rogue::interfaces::stream::Frame > FramePtr
Shared pointer alias for Frame.
Definition Frame.h:549
static void toFrame(rogue::interfaces::stream::FrameIterator &iter, uint32_t size, void *src)
Copies bytes from a source pointer into a frame iterator.
std::shared_ptr< rogue::interfaces::stream::FrameLock > FrameLockPtr
Shared pointer alias for FrameLock.
Definition FrameLock.h:110
std::shared_ptr< rogue::utilities::Prbs > PrbsPtr
Definition Prbs.h:328