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 // disable() joins the TX thread; otherwise it would outlive taps_/threadEn_.
104 disable();
105 free(taps_);
106}
107
109double ru::Prbs::updateTime(struct timeval* last) {
110 struct timeval cmp;
111 struct timeval now;
112 struct timeval per;
113 double ret;
114
115 cmp.tv_sec = 1;
116 cmp.tv_usec = 0;
117
118 gettimeofday(&now, NULL);
119
120 timersub(&now, last, &per);
121
122 if (timercmp(&per, &cmp, >)) {
123 ret = static_cast<float>(per.tv_sec) + static_cast<float>(per.tv_usec) / 1e6;
124 gettimeofday(last, NULL);
125 } else {
126 ret = 0.0;
127 }
128 return ret;
129}
130
132void ru::Prbs::setWidth(uint32_t width) {
133 if ((width > (MaxBytes * 8)) || (width % 32) != 0) throw(rogue::GeneralError("Prbs::setWidth", "Invalid width."));
134
135 rogue::GilRelease noGil;
136 std::lock_guard<std::mutex> lockT(pMtx_);
137
138 width_ = width;
139 byteWidth_ = width / 8;
140 minSize_ = byteWidth_ * 3;
141}
142
144void ru::Prbs::setTaps(uint32_t tapCnt, uint8_t* taps) {
145 uint32_t i;
146
147 std::lock_guard<std::mutex> lockT(pMtx_);
148
149 free(taps_);
150 tapCnt_ = tapCnt;
151 taps_ = reinterpret_cast<uint8_t*>(malloc(sizeof(uint8_t) * tapCnt));
152
153 for (i = 0; i < tapCnt_; i++) taps_[i] = taps[i];
154}
155
156#ifndef NO_PYTHON
157
159void ru::Prbs::setTapsPy(boost::python::object p) {
160 Py_buffer pyBuf;
161
162 if (PyObject_GetBuffer(p.ptr(), &pyBuf, PyBUF_SIMPLE) < 0)
163 throw(rogue::GeneralError("Prbs::setTapsPy", "Python Buffer Error"));
164
165 setTaps(pyBuf.len, reinterpret_cast<uint8_t*>(pyBuf.buf));
166 PyBuffer_Release(&pyBuf);
167}
168
169#endif
170
172void ru::Prbs::sendCount(bool state) {
173 rogue::GilRelease noGil;
174 std::lock_guard<std::mutex> lockT(pMtx_);
175
176 sendCount_ = state;
177}
178
179void ru::Prbs::flfsr(uint8_t* data) {
180 uint32_t iByte;
181 uint32_t iBit;
182 uint32_t x;
183 bool msbOut;
184 bool lsbIn = false;
185
186 for (x = 0; x < tapCnt_; x++) {
187 iByte = taps_[x] / 8;
188 iBit = taps_[x] % 8;
189 lsbIn ^= ((data[iByte] >> iBit) & 0x1);
190 }
191
192 for (x = 0; x < byteWidth_; x++) {
193 msbOut = data[x] & 0x80;
194 data[x] <<= 1;
195 data[x] |= lsbIn;
196 lsbIn = msbOut;
197 }
198}
199
201void ru::Prbs::runThread() {
202 txLog_->logThreadId();
203
204 while (threadEn_) {
205 genFrame(txSize_);
206 if ( txPeriod_ > 0 ) usleep(txPeriod_);
207 }
208}
209
211void ru::Prbs::enable(uint32_t size) {
212 // Verify size first
213 if (((size % byteWidth_) != 0) || size < minSize_) throw rogue::GeneralError("Prbs::enable", "Invalid frame size");
214
215 if (txThread_ == NULL) {
216 txSize_ = size;
217 threadEn_ = true;
218 txThread_ = new std::thread(&Prbs::runThread, this);
219
220 // Set a thread name
221#ifndef __MACH__
222 pthread_setname_np(txThread_->native_handle(), "PrbsTx");
223#endif
224 }
225}
226
228void ru::Prbs::disable() {
229 if (txThread_ != NULL) {
230 rogue::GilRelease noGil;
231 threadEn_ = false;
232 txThread_->join();
233 delete txThread_;
234 txThread_ = NULL;
235 }
236}
237
239bool ru::Prbs::getRxEnable() {
240 return rxEnable_;
241}
242
244void ru::Prbs::setRxEnable(bool en) {
245 rogue::GilRelease noGil;
246 pMtx_.lock();
247 rxEnable_ = en;
248 pMtx_.unlock();
249}
250
252uint32_t ru::Prbs::getRxErrors() {
253 return (rxErrCount_);
254}
255
257uint32_t ru::Prbs::getRxCount() {
258 return (rxCount_);
259}
260
262uint64_t ru::Prbs::getRxBytes() {
263 return (rxBytes_);
264}
265
267uint32_t ru::Prbs::getTxErrors() {
268 return (txErrCount_);
269}
270
272uint32_t ru::Prbs::getTxCount() {
273 return (txCount_);
274}
275
277double ru::Prbs::getRxRate() {
278 return rxRate_;
279}
280
282double ru::Prbs::getRxBw() {
283 return rxBw_;
284}
285
287double ru::Prbs::getTxRate() {
288 return txRate_;
289}
290
292uint32_t ru::Prbs::getTxPeriod() {
293 return txPeriod_;
294}
295
297void ru::Prbs::setTxPeriod(uint32_t value) {
298 txPeriod_ = value;
299}
300
302double ru::Prbs::getTxBw() {
303 return txBw_;
304}
305
307uint64_t ru::Prbs::getTxBytes() {
308 return (txBytes_);
309}
310
312void ru::Prbs::checkPayload(bool state) {
313 checkPl_ = state;
314}
315
317void ru::Prbs::genPayload(bool state) {
318 genPl_ = state;
319}
320
322// Counters should really be locked!
323void ru::Prbs::resetCount() {
324 pMtx_.lock();
325 txErrCount_ = 0;
326 txCount_ = 0;
327 txBytes_ = 0;
328 rxErrCount_ = 0;
329 rxCount_ = 0;
330 rxBytes_ = 0;
331 pMtx_.unlock();
332}
333
335void ru::Prbs::genFrame(uint32_t size) {
336 ris::FrameIterator frIter;
337 ris::FrameIterator frEnd;
338 uint32_t frSeq[MaxBytes / 4];
339 uint32_t frSize[MaxBytes / 4];
340 uint32_t wCount[MaxBytes / 4];
341 uint8_t data[MaxBytes];
342 double per;
343 ris::FramePtr fr;
344
345 rogue::GilRelease noGil;
346 std::lock_guard<std::mutex> lock(pMtx_);
347
348 // Verify size first
349 if (((size % byteWidth_) != 0) || size < minSize_)
350 throw rogue::GeneralError("Prbs::genFrame", "Invalid frame size");
351
352 // Setup size
353 memset(frSize, 0, MaxBytes);
354 frSize[0] = (size / byteWidth_) - 1;
355
356 // Setup sequence
357 memset(frSeq, 0, MaxBytes);
358 frSeq[0] = txSeq_;
359
360 // Setup counter
361 memset(wCount, 0, MaxBytes);
362
363 // Get frame
364 fr = reqFrame(size, true);
365 fr->setPayload(size);
366
367 frIter = fr->begin();
368 frEnd = frIter + size;
369
370 // First word is sequence
371 ris::toFrame(frIter, byteWidth_, frSeq);
372 ++wCount[0];
373
374 // Second word is size
375 ris::toFrame(frIter, byteWidth_, frSize);
376 ++wCount[0];
377
378 if (genPl_) {
379 // Init data
380 std::memcpy(data, frSeq, byteWidth_);
381
382 // Generate payload
383 while (frIter != frEnd) {
384 if (sendCount_) {
385 ris::toFrame(frIter, byteWidth_, wCount);
386 } else {
387 flfsr(data);
388 ris::toFrame(frIter, byteWidth_, data);
389 }
390 ++wCount[0];
391 }
392 }
393
394 sendFrame(fr);
395
396 // Update counters
397 txSeq_++;
398 txCount_++;
399 txBytes_ += size;
400
401 if ((per = updateTime(&lastTxTime_)) > 0.0) {
402 txRate_ = static_cast<double>(txCount_ - lastTxCount_) / per;
403 txBw_ = static_cast<double>(txBytes_ - lastTxBytes_) / per;
404 lastTxCount_ = txCount_;
405 lastTxBytes_ = txBytes_;
406 }
407}
408
410void ru::Prbs::acceptFrame(ris::FramePtr frame) {
411 ris::FrameIterator frIter;
412 ris::FrameIterator frEnd;
413 uint32_t frSeq[MaxBytes / 4];
414 uint32_t frSize[MaxBytes / 4];
415 uint32_t expSeq;
416 uint32_t expSize;
417 uint32_t size;
418 uint32_t pos;
419 uint32_t x;
420 uint8_t expData[MaxBytes];
421 double per;
422 char debugA[10000];
423 char debugB[1000];
424
425 rogue::GilRelease noGil;
426
427 while (!rxEnable_) usleep(10000);
428
429 ris::FrameLockPtr fLock = frame->lock();
430 std::lock_guard<std::mutex> lock(pMtx_);
431
432 size = frame->getPayload();
433 frIter = frame->begin();
434 frEnd = frame->end();
435
436 // Check for frame errors
437 if (frame->getError()) {
438 rxLog_->warning("Frame error field is set: 0x%" PRIx8, frame->getError());
439 rxErrCount_++;
440 return;
441 }
442
443 // Verify size
444 if (((size % byteWidth_) != 0) || size < minSize_) {
445 rxLog_->warning("Size violation size=%" PRIu32 ", count=%" PRIu32, size, rxCount_);
446 rxErrCount_++;
447 return;
448 }
449
450 // First word is sequence
451 ris::fromFrame(frIter, byteWidth_, frSeq);
452 expSeq = rxSeq_;
453 rxSeq_ = frSeq[0] + 1;
454
455 // Second word is size
456 ris::fromFrame(frIter, byteWidth_, frSize);
457 expSize = (frSize[0] + 1) * byteWidth_;
458
459 // Check size and sequence
460 // Accept any sequence if our local count is zero
461 // incoming frames with seq = 0 never cause errors and treated as a restart
462 if ((expSize != size) || (frSeq[0] != 0 && expSeq != 0 && frSeq[0] != expSeq)) {
463 rxLog_->warning("Bad header. expSize=%" PRIu32 " gotSize=%" PRIu32 " expSeq=%" PRIu32 " gotSeq=%" PRIu32
464 " nxtSeq=%" PRIu32 " count=%" PRIu32,
465 expSize,
466 size,
467 expSeq,
468 frSeq[0],
469 rxSeq_,
470 rxCount_);
471 rxErrCount_++;
472 return;
473 }
474
475 // Is payload checking enabled
476 if (checkPl_) {
477 // Init data
478 std::memcpy(expData, frSeq, byteWidth_);
479 pos = 0;
480
481 // Read payload
482 while (frIter != frEnd) {
483 flfsr(expData);
484
485 if (!std::equal(frIter, frIter + byteWidth_, expData)) {
486 snprintf(debugA,
487 sizeof(debugA),
488 "Bad value at index %" PRIu32 ". count=%" PRIu32 ", size=%" PRIu32,
489 pos,
490 rxCount_,
491 (size / byteWidth_) - 1);
492
493 for (x = 0; x < byteWidth_; x++) {
494 snprintf(debugB,
495 sizeof(debugB),
496 "\n %" PRIu32 ":%" PRIu32 " Got=0x%" PRIx8 " Exp=0x%" PRIx8,
497 pos,
498 x,
499 *(frIter + x),
500 *(expData + x));
501 snprintf(debugA + strlen(debugA), sizeof(debugA) - strlen(debugA), "%s", debugB);
502 }
503 rxLog_->warning(debugA);
504 rxErrCount_++;
505 return;
506 }
507 frIter += byteWidth_;
508 ++pos;
509 }
510 }
511
512 rxCount_++;
513 rxBytes_ += size;
514
515 if ((per = updateTime(&lastRxTime_)) > 0.0) {
516 rxRate_ = static_cast<double>(rxCount_ - lastRxCount_) / per;
517 rxBw_ = static_cast<double>(rxBytes_ - lastRxBytes_) / per;
518 lastRxCount_ = rxCount_;
519 lastRxBytes_ = rxBytes_;
520 }
521}
522
523void ru::Prbs::setup_python() {
524#ifndef NO_PYTHON
525 // Keep the manual method docstrings, but suppress Boost.Python's generated
526 // Python/C++ signatures. The autogenerated forms are not very readable in
527 // the Sphinx Python API pages for exported C++ classes.
528 bp::docstring_options doc_options(true, false, false);
529
530 bp::class_<ru::Prbs, ru::PrbsPtr, bp::bases<ris::Master, ris::Slave>, boost::noncopyable>(
531 "Prbs",
532 "PRBS generator/checker that can act as both stream master and slave.\n\n"
533 "The engine can generate deterministic pseudo-random frames and verify\n"
534 "received frames for bit-error testing. It supports both one-shot frame\n"
535 "generation and optional background transmit mode.",
536 bp::init<>("Construct a PRBS generator/checker with default taps and width."))
537 .def("genFrame", &ru::Prbs::genFrame, bp::args("size"), "Generate and transmit one PRBS frame.")
538 .def("enable",
539 &ru::Prbs::enable,
540 bp::args("size"),
541 "Enable periodic background PRBS frame generation using the configured TX period.")
542 .def("disable", &ru::Prbs::disable, "Disable periodic background PRBS frame generation.")
543 .def("setWidth", &ru::Prbs::setWidth, bp::args("width"), "Set the PRBS generator/checker width in bits.")
544 .def("setTaps",
545 &ru::Prbs::setTapsPy,
546 bp::args("taps"),
547 "Configure the PRBS LFSR tap positions from a bytes-like object.")
548 .def("getRxEnable", &ru::Prbs::getRxEnable, "Return whether receive-side checking is enabled.")
549 .def("setRxEnable", &ru::Prbs::setRxEnable, bp::args("state"), "Enable or disable receive-side checking.")
550 .def("getRxErrors", &ru::Prbs::getRxErrors, "Return the receive-side error count.")
551 .def("getRxCount", &ru::Prbs::getRxCount, "Return the total received frame count.")
552 .def("getRxRate", &ru::Prbs::getRxRate, "Return the computed receive frame rate in frames/second.")
553 .def("getRxBw", &ru::Prbs::getRxBw, "Return the computed receive bandwidth in bytes/second.")
554 .def("getRxBytes", &ru::Prbs::getRxBytes, "Return the total received payload byte count.")
555 .def("getTxErrors", &ru::Prbs::getTxErrors, "Return the transmit-side error count.")
556 .def("getTxCount", &ru::Prbs::getTxCount, "Return the total transmitted frame count.")
557 .def("getTxBytes", &ru::Prbs::getTxBytes, "Return the total transmitted payload byte count.")
558 .def("getTxPeriod", &ru::Prbs::getTxPeriod, "Return the periodic transmit interval in microseconds.")
559 .def("setTxPeriod",
560 &ru::Prbs::setTxPeriod,
561 bp::args("period"),
562 "Set the periodic transmit interval in microseconds.")
563 .def("getTxRate", &ru::Prbs::getTxRate, "Return the computed transmit frame rate in frames/second.")
564 .def("getTxBw", &ru::Prbs::getTxBw, "Return the computed transmit bandwidth in bytes/second.")
565 .def("checkPayload",
566 &ru::Prbs::checkPayload,
567 bp::args("state"),
568 "Enable or disable PRBS payload checking on received frames.")
569 .def("genPayload",
570 &ru::Prbs::genPayload,
571 bp::args("state"),
572 "Enable or disable PRBS payload generation on transmitted frames.")
573 .def("resetCount", &ru::Prbs::resetCount, "Reset transmit and receive counters.")
574 .def("sendCount",
575 &ru::Prbs::sendCount,
576 bp::args("state"),
577 "Enable or disable insertion of frame counters into generated payloads.");
578
579 bp::implicitly_convertible<ru::PrbsPtr, ris::SlavePtr>();
580 bp::implicitly_convertible<ru::PrbsPtr, ris::MasterPtr>();
581#endif
582}
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:95
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:332