rogue
Loading...
Searching...
No Matches
Block.cpp
Go to the documentation of this file.
1
18#include "rogue/Directives.h"
19
20#ifndef NO_PYTHON
21
22#define NO_IMPORT_ARRAY
23#include "rogue/numpy.h"
24#include <boost/python.hpp>
25
26namespace bp = boost::python;
27
28#endif
29
31
32#include <inttypes.h>
33#include <sys/time.h>
34
35#include <cmath>
36#include <cstdio>
37#include <cstring>
38#include <exception>
39#include <iomanip>
40#include <memory>
41#include <sstream>
42#include <string>
43#include <vector>
44
45#include "rogue/GeneralError.h"
46#include "rogue/GilRelease.h"
47#include "rogue/ScopedGil.h"
52
54
55
56// Class factory which returns a pointer to a Block (BlockPtr)
57rim::BlockPtr rim::Block::create(uint64_t offset, uint32_t size) {
58 rim::BlockPtr b = std::make_shared<rim::Block>(offset, size);
59 return (b);
60}
61
62#ifndef NO_PYTHON
63// Setup class for use in python
64void rim::Block::setup_python() {
65 bp::class_<rim::Block, rim::BlockPtr, bp::bases<rim::Master>, boost::noncopyable>("Block",
66 bp::init<uint64_t, uint32_t>())
67 .add_property("path", &rim::Block::path)
68 .add_property("mode", &rim::Block::mode)
69 .add_property("bulkOpEn", &rim::Block::bulkOpEn)
70 .add_property("offset", &rim::Block::offset)
71 .add_property("address", &rim::Block::address)
72 .add_property("size", &rim::Block::size)
73 .def("setEnable", &rim::Block::setEnable)
74 .def("_startTransaction", &rim::Block::startTransactionPy)
75 .def("_checkTransaction", &rim::Block::checkTransactionPy)
76 .def("addVariables", &rim::Block::addVariablesPy)
77 .def("_rateTest", &rim::Block::rateTest)
78 .add_property("variables", &rim::Block::variablesPy);
79
80 bp::implicitly_convertible<rim::BlockPtr, rim::MasterPtr>();
81}
82#endif
83
84// Create a Hub device with a given offset
85rim::Block::Block(uint64_t offset, uint32_t size) {
86 path_ = "Undefined";
87 mode_ = "RW";
88 bulkOpEn_ = false;
89 updateEn_ = false;
90 offset_ = offset;
91 size_ = size;
92 verifyEn_ = false;
93 verifyReq_ = false;
94 verifyInp_ = false;
95 doUpdate_ = false;
96 blockPyTrans_ = false;
97 enable_ = false;
98 stale_ = false;
99 retryCount_ = 0;
100
101 verifyBase_ = 0; // Verify Range
102 verifySize_ = 0; // Verify Range
103
104 blockData_ = reinterpret_cast<uint8_t*>(malloc(size_));
105 memset(blockData_, 0, size_);
106
107 verifyData_ = reinterpret_cast<uint8_t*>(malloc(size_));
108 memset(verifyData_, 0, size_);
109
110 verifyMask_ = reinterpret_cast<uint8_t*>(malloc(size_));
111 memset(verifyMask_, 0, size_);
112
113 verifyBlock_ = reinterpret_cast<uint8_t*>(malloc(size_));
114 memset(verifyBlock_, 0, size_);
115
116 expectedData_ = reinterpret_cast<uint8_t*>(malloc(size_));
117 memset(expectedData_, 0, size_);
118}
119
120// Destroy the Hub
121rim::Block::~Block() {
122 // Custom clean
123 customClean();
124
125 free(blockData_);
126 free(verifyData_);
127 free(verifyMask_);
128 free(verifyBlock_);
129 free(expectedData_);
130}
131
132// Return the path of the block
133std::string rim::Block::path() {
134 return path_;
135}
136
137// Return the mode of the block
138std::string rim::Block::mode() {
139 return mode_;
140}
141
142// Return bulk enable flag
143bool rim::Block::bulkOpEn() {
144 return bulkOpEn_;
145}
146
147// Set enable state
148void rim::Block::setEnable(bool newState) {
149 rogue::GilRelease noGil;
150 std::lock_guard<std::mutex> lock(mtx_);
151 enable_ = newState;
152}
153
154// Get offset of this Block
155uint64_t rim::Block::offset() {
156 return offset_;
157}
158
159// Get full address of this Block
160uint64_t rim::Block::address() {
161 return (reqAddress() + offset_);
162}
163
164// Get size of this block in bytes.
165uint32_t rim::Block::size() {
166 return size_;
167}
168
169// Block transactions
170bool rim::Block::blockPyTrans() {
171 return blockPyTrans_;
172}
173
174// Start a transaction for this block
175void rim::Block::intStartTransaction(uint32_t type, bool forceWr, bool check, rim::Variable* var, int32_t index) {
176 uint32_t x;
177 uint32_t tOff;
178 uint32_t tSize;
179 uint8_t* tData;
180 uint32_t highByte;
181 uint32_t lowByte;
182 uint32_t minAccess = getSlave()->doMinAccess();
183
184 std::vector<rim::VariablePtr>::iterator vit;
185
186 // Check for valid combinations
187 if ((type == rim::Write && ((mode_ == "RO") || (!stale_ && !forceWr))) || (type == rim::Post && (mode_ == "RO")) ||
188 (type == rim::Read && ((mode_ == "WO") || stale_)) ||
189 (type == rim::Verify && ((mode_ == "WO") || (mode_ == "RO") || stale_ || !verifyReq_)))
190 return;
191 {
192 rogue::GilRelease noGil;
193 std::lock_guard<std::mutex> lock(mtx_);
194 waitTransaction(0);
195 clearError();
196
197 // Determine transaction range
198 if (var == NULL) {
199 lowByte = 0;
200 highByte = size_ - 1;
201 if (type == rim::Write || type == rim::Post) {
202 stale_ = false;
203 for (vit = variables_.begin(); vit != variables_.end(); ++vit) {
204 (*vit)->stale_ = false;
205 }
206 }
207 } else {
208 if (type == rim::Read || type == rim::Verify) {
209 if (index < 0 || index >= var->numValues_) {
210 lowByte = var->lowTranByte_[0];
211
212 if (var->numValues_ == 0) {
213 highByte = var->highTranByte_[0];
214 } else {
215 highByte = var->highTranByte_[var->numValues_ - 1];
216 }
217 } else {
218 lowByte = var->lowTranByte_[index];
219 highByte = var->highTranByte_[index];
220 }
221 } else {
222 lowByte = var->staleLowByte_;
223 highByte = var->staleHighByte_;
224
225 // Catch case where fewer stale bytes than min access or non-aligned
226 if (lowByte % minAccess != 0) lowByte -= lowByte % minAccess;
227 if ((highByte + 1) % minAccess != 0) highByte += minAccess - ((highByte + 1) % minAccess);
228 stale_ = false;
229 for (vit = variables_.begin(); vit != variables_.end(); ++vit) {
230 if ((*vit)->stale_) {
231 if ((*vit)->staleLowByte_ < lowByte) lowByte = (*vit)->staleLowByte_;
232 if ((*vit)->staleHighByte_ > highByte) highByte = (*vit)->staleHighByte_;
233 (*vit)->stale_ = false;
234 }
235 }
236 }
237 }
238
239 // Device is disabled, check after clearing stale states
240 if (!enable_) return;
241
242 // Setup verify data, clear verify write flag if verify transaction
243 if (type == rim::Verify) {
244 tOff = verifyBase_;
245 tSize = verifySize_;
246 tData = verifyData_ + verifyBase_;
247 verifyInp_ = true;
248
249 // Not a verify transaction
250 } else {
251 // Derive offset and size based upon min transaction size
252 tOff = lowByte;
253 tSize = (highByte - lowByte) + 1;
254
255 // Set transaction pointer
256 tData = blockData_ + tOff;
257
258 // Track verify after writes.
259 // Only verify blocks that have been written since last verify
260 if (type == rim::Write) {
261 verifyBase_ = tOff;
262 verifySize_ = tSize;
263 verifyReq_ = verifyEn_;
264
265 // Snapshot expected data at write time so that concurrent
266 // modifications to blockData_ do not cause false verify failures
267 memcpy(expectedData_ + tOff, blockData_ + tOff, tSize);
268 }
269 }
270 doUpdate_ = updateEn_;
271
272 bLog_->debug("Start transaction type = %" PRIu32 ", Offset=0x%" PRIx64 ", lByte=%" PRIu32 ", hByte=%" PRIu32
273 ", tOff=0x%" PRIx32 ", tSize=%" PRIu32,
274 type,
275 offset_,
276 lowByte,
277 highByte,
278 tOff,
279 tSize);
280
281 // Start transaction
282 reqTransaction(offset_ + tOff, tSize, tData, type);
283 }
284}
285
286// Start a transaction for this block, cpp version
287void rim::Block::startTransaction(uint32_t type, bool forceWr, bool check, rim::Variable* var, int32_t index) {
288 uint32_t count;
289 bool fWr;
290
291 count = 0;
292 fWr = forceWr;
293
294 do {
295 intStartTransaction(type, fWr, check, var, index);
296
297 try {
298 if (check || retryCount_ > 0) checkTransaction();
299 count = retryCount_; // Success
300 } catch (rogue::GeneralError err) {
301 fWr = true; // Stale state is now lost
302
303 // Rethrow the error if this is the final retry, else log warning
304 if ((count + 1) > retryCount_) {
305 bLog_->error("Error on try %" PRIu32 " out of %" PRIu32 ": %s",
306 (count + 1),
307 (retryCount_ + 1),
308 err.what());
309 throw err;
310 } else {
311 bLog_->warning("Error on try %" PRIu32 " out of %" PRIu32 ": %s",
312 (count + 1),
313 (retryCount_ + 1),
314 err.what());
315 }
316 }
317 } while (count++ < retryCount_);
318}
319
320#ifndef NO_PYTHON
321
322// Start a transaction for this block, python version
323void rim::Block::startTransactionPy(uint32_t type, bool forceWr, bool check, rim::VariablePtr var, int32_t index) {
324 uint32_t count;
325 bool upd;
326 bool fWr;
327
328 if (blockPyTrans_) return;
329
330 count = 0;
331 upd = false;
332 fWr = forceWr;
333
334 do {
335 intStartTransaction(type, fWr, check, var.get(), index);
336
337 try {
338 if (check || retryCount_ > 0) upd = checkTransaction();
339 count = retryCount_; // Success
340 } catch (rogue::GeneralError err) {
341 fWr = true; // Stale state is now lost
342
343 // Rethrow the error if this is the final retry, else log warning
344 if ((count + 1) > retryCount_) {
345 bLog_->error("Error on try %" PRIu32 " out of %" PRIu32 ": %s",
346 (count + 1),
347 (retryCount_ + 1),
348 err.what());
349 throw err;
350 } else {
351 bLog_->warning("Error on try %" PRIu32 " out of %" PRIu32 ": %s",
352 (count + 1),
353 (retryCount_ + 1),
354 err.what());
355 }
356 }
357 } while (count++ < retryCount_);
358
359 if (upd) varUpdate();
360}
361
362#endif
363
364// Check transaction result
365bool rim::Block::checkTransaction() {
366 std::string err;
367 bool locUpdate;
368 uint32_t x;
369
370 {
371 rogue::GilRelease noGil;
372 std::lock_guard<std::mutex> lock(mtx_);
373 waitTransaction(0);
374
375 err = getError();
376 clearError();
377
378 if (err != "") {
379 throw(rogue::GeneralError::create("Block::checkTransaction",
380 "Transaction error for block %s with address 0x%.8x. Error %s",
381 path_.c_str(),
382 address(),
383 err.c_str()));
384 }
385
386 // Device is disabled
387 if (!enable_) return false;
388
389 // Check verify data if verifyInp is set
390 if (verifyInp_) {
391 bLog_->debug("Verfying data. Base=0x%" PRIx32 ", size=%" PRIu32, verifyBase_, verifySize_);
392 verifyReq_ = false;
393 verifyInp_ = false;
394
395 for (x = verifyBase_; x < verifyBase_ + verifySize_; x++) {
396 if ((verifyData_[x] & verifyMask_[x]) != (expectedData_[x] & verifyMask_[x])) {
397 throw(rogue::GeneralError::create("Block::checkTransaction",
398 "Verify error for block %s with address 0x%.8" PRIx64
399 ". Byte: %" PRIu32 ". Got: 0x%.2" PRIx8 ", Exp: 0x%.2" PRIx8
400 ", Mask: 0x%.2" PRIx8,
401 path_.c_str(),
402 address(),
403 x,
404 verifyData_[x],
405 expectedData_[x],
406 verifyMask_[x]));
407 }
408 }
409 }
410 bLog_->debug("Transaction complete");
411
412 locUpdate = doUpdate_;
413 doUpdate_ = false;
414 }
415 return locUpdate;
416}
417
418#ifndef NO_PYTHON
419
420// Check transaction result
421void rim::Block::checkTransactionPy() {
422 if (blockPyTrans_) return;
423
424 if (checkTransaction()) varUpdate();
425}
426
427#endif
428
429// Write sequence
430void rim::Block::write(rim::Variable* var, int32_t index) {
431 startTransaction(rim::Write, true, false, var, index);
432 startTransaction(rim::Verify, false, false, var, index);
433 checkTransaction();
434}
435
436// Read sequence
437void rim::Block::read(rim::Variable* var, int32_t index) {
438 startTransaction(rim::Read, false, false, var, index);
439 checkTransaction();
440}
441
442#ifndef NO_PYTHON
443
444// Call variable update for all variables
445void rim::Block::varUpdate() {
446 std::vector<rim::VariablePtr>::iterator vit;
447
449
450 for (vit = variables_.begin(); vit != variables_.end(); ++vit) {
451 if ((*vit)->updateNotify_) (*vit)->queueUpdate();
452 }
453}
454
455#endif
456
457// Add variables to block
458void rim::Block::addVariables(std::vector<rim::VariablePtr> variables) {
459 std::vector<rim::VariablePtr>::iterator vit;
460
461 uint32_t x;
462
463 uint8_t excMask[size_];
464 uint8_t oleMask[size_];
465
466 memset(excMask, 0, size_);
467 memset(oleMask, 0, size_);
468
469 variables_ = variables;
470
471 for (vit = variables_.begin(); vit != variables_.end(); ++vit) {
472 (*vit)->block_ = this;
473
474 if (vit == variables_.begin()) {
475 path_ = (*vit)->path_;
476 std::string logName = "memory.block." + path_;
477 bLog_ = rogue::Logging::create(logName.c_str());
478 mode_ = (*vit)->mode_;
479 }
480
481 if ((*vit)->bulkOpEn_) bulkOpEn_ = true;
482 if ((*vit)->updateNotify_) updateEn_ = true;
483
484 // Retry count
485 if ((*vit)->retryCount_ > retryCount_) retryCount_ = (*vit)->retryCount_;
486
487 // If variable modes mismatch, set block to read/write
488 if (mode_ != (*vit)->mode_) mode_ = "RW";
489
490 // Update variable masks for standard variable
491 if ((*vit)->numValues_ == 0) {
492 for (x = 0; x < (*vit)->bitOffset_.size(); x++) {
493 // Variable allows overlaps, add to overlap enable mask
494 if ((*vit)->overlapEn_) {
495 setBits(oleMask, (*vit)->bitOffset_[x], (*vit)->bitSize_[x]);
496
497 // Otherwise add to exclusive mask and check for existing mapping
498 } else {
499 if (anyBits(excMask, (*vit)->bitOffset_[x], (*vit)->bitSize_[x]))
501 "Block::addVariables",
502 "Variable bit overlap detected for block %s with address 0x%.8x and variable %s",
503 path_.c_str(),
504 address(),
505 (*vit)->name_.c_str()));
506
507 setBits(excMask, (*vit)->bitOffset_[x], (*vit)->bitSize_[x]);
508 }
509
510 // update verify mask
511 if ((*vit)->mode_ == "RW" && (*vit)->verifyEn_) {
512 verifyEn_ = true;
513 setBits(verifyMask_, (*vit)->bitOffset_[x], (*vit)->bitSize_[x]);
514 }
515
516 // update verify block
517 if ((*vit)->mode_ == "RO" || (*vit)->mode_ == "WO" || !(*vit)->verifyEn_) {
518 setBits(verifyBlock_, (*vit)->bitOffset_[x], (*vit)->bitSize_[x]);
519 }
520
521 bLog_->debug(
522 "Adding variable %s to block %s at offset 0x%.8x, bitIdx=%i, bitOffset %i, bitSize %i, mode %s, "
523 "verifyEn "
524 "%d " PRIx64,
525 (*vit)->name_.c_str(),
526 path_.c_str(),
527 offset_,
528 x,
529 (*vit)->bitOffset_[x],
530 (*vit)->bitSize_[x],
531 (*vit)->mode_.c_str(),
532 (*vit)->verifyEn_);
533 }
534
535 // List variables
536 } else {
537 for (x = 0; x < (*vit)->numValues_; x++) {
538 // Variable allows overlaps, add to overlap enable mask
539 if ((*vit)->overlapEn_) {
540 setBits(oleMask, x * (*vit)->valueStride_ + (*vit)->bitOffset_[0], (*vit)->valueBits_);
541
542 // Otherwise add to exclusive mask and check for existing mapping
543 } else {
544 if (anyBits(excMask, x * (*vit)->valueStride_ + (*vit)->bitOffset_[0], (*vit)->valueBits_))
546 "Block::addVariables",
547 "Variable bit overlap detected for block %s with address 0x%.8x and variable %s",
548 path_.c_str(),
549 address(),
550 (*vit)->name_.c_str()));
551
552 setBits(excMask, x * (*vit)->valueStride_ + (*vit)->bitOffset_[0], (*vit)->valueBits_);
553 }
554
555 // update verify mask
556 if ((*vit)->mode_ == "RW" && (*vit)->verifyEn_) {
557 verifyEn_ = true;
558 setBits(verifyMask_, x * (*vit)->valueStride_ + (*vit)->bitOffset_[0], (*vit)->valueBits_);
559 }
560
561 // update verify block
562 if ((*vit)->mode_ == "RO" || (*vit)->mode_ == "WO" || !(*vit)->verifyEn_) {
563 setBits(verifyBlock_, x * (*vit)->valueStride_ + (*vit)->bitOffset_[0], (*vit)->valueBits_);
564 }
565
566 bLog_->debug(
567 "Adding variable %s to block %s at offset 0x%.8x, index=%i, valueOffset=%i, valueBits %i, mode %s, "
568 "verifyEn %d",
569 (*vit)->name_.c_str(),
570 path_.c_str(),
571 offset_,
572 x,
573 x * (*vit)->valueStride_ + (*vit)->bitOffset_[0],
574 (*vit)->valueBits_,
575 (*vit)->mode_.c_str(),
576 (*vit)->verifyEn_);
577 }
578 }
579 }
580
581 // Check for overlaps by anding exclusive and overlap bit vectors
582 for (x = 0; x < size_; x++) {
583 if (oleMask[x] & excMask[x])
584 throw(rogue::GeneralError::create("Block::addVariables",
585 "Variable bit mask overlap detected for block %s with address 0x%.8x",
586 path_.c_str(),
587 address()));
588
589 // Update very mask using verify block
590 verifyMask_[x] &= (verifyBlock_[x] ^ 0xFF);
591 }
592
593 // Execute custom init
594 customInit();
595
596 // Debug the results of the build
597 std::stringstream ss;
598 uint32_t rem = size_;
599 uint32_t idx;
600 idx = 0;
601 x = 0;
602
603 while (rem > 0) {
604 ss << "0x" << std::setfill('0') << std::hex << std::setw(2) << static_cast<uint32_t>(verifyMask_[x]) << " ";
605 x++;
606 rem--;
607 if (rem == 0 || x % 10 == 0) {
608 bLog_->debug("Done adding variables. Verify Mask %.3i - %.3i: %s", idx, x - 1, ss.str().c_str());
609 ss.str("");
610 idx = x;
611 }
612 }
613}
614
615#ifndef NO_PYTHON
616
617// Add variables to block
618void rim::Block::addVariablesPy(bp::object variables) {
619 std::vector<rim::VariablePtr> vars = py_list_to_std_vector<rim::VariablePtr>(variables);
620 addVariables(vars);
621}
622
623#endif
624
626std::vector<rim::VariablePtr> rim::Block::variables() {
627 return variables_;
628}
629
630#ifndef NO_PYTHON
631
633bp::object rim::Block::variablesPy() {
634 return std_vector_to_py_list<rim::VariablePtr>(variables_);
635}
636
637#endif
638
639// byte reverse
640void rim::Block::reverseBytes(uint8_t* data, uint32_t byteSize) {
641 uint32_t x;
642 uint8_t tmp;
643
644 for (x = 0; x < byteSize / 2; x++) {
645 tmp = data[x];
646 data[x] = data[byteSize - x - 1];
647 data[byteSize - x - 1] = tmp;
648 }
649}
650
651// Set data from pointer to internal staged memory
652void rim::Block::setBytes(const uint8_t* data, rim::Variable* var, uint32_t index) {
653 uint32_t srcBit;
654 uint32_t x;
655 uint8_t tmp;
656 uint8_t* buff;
657
658 rogue::GilRelease noGil;
659 std::lock_guard<std::mutex> lock(mtx_);
660
661 // Set stale flag
662 if (var->mode_ != "RO") stale_ = true;
663
664 // Change byte order, need to make a copy
665 if (var->byteReverse_) {
666 buff = reinterpret_cast<uint8_t*>(malloc(var->valueBytes_));
667 if (buff == NULL)
668 throw(rogue::GeneralError::create("Block::setBytes",
669 "Failed to allocate %" PRIu32 " bytes for byte-reversed copy of %s",
670 var->valueBytes_,
671 var->name_.c_str()));
672 memcpy(buff, data, var->valueBytes_);
673 reverseBytes(buff, var->valueBytes_);
674 } else {
675 buff = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(data));
676 }
677
678 // List variable
679 if (var->numValues_ != 0) {
680 if (index >= var->numValues_) {
681 if (var->byteReverse_) free(buff);
682 throw(rogue::GeneralError::create("Block::setBytes",
683 "Index %" PRIu32 " is out of range for %s",
684 index,
685 var->name_.c_str()));
686 }
687
688 // Fast copy.
689 // Intentionally writes valueBytes_ (not valueStride_/8): pyrogue
690 // RemoteVariable.add() rejects valueStride < valueBits before any
691 // C++ caller is reachable, so a stride-cap here would silently
692 // truncate writes for misconfigured direct-C++ callers and hide
693 // the bug rather than surface it.
694 if (var->fastByte_ != NULL)
695 memcpy(blockData_ + var->fastByte_[index], buff, var->valueBytes_);
696
697 else
698 copyBits(blockData_, var->bitOffset_[0] + (index * var->valueStride_), buff, 0, var->valueBits_);
699
700 if (var->mode_ != "RO") {
701 if (var->stale_) {
702 if (var->lowTranByte_[index] < var->staleLowByte_) var->staleLowByte_ = var->lowTranByte_[index];
703
704 if (var->highTranByte_[index] > var->staleHighByte_) var->staleHighByte_ = var->highTranByte_[index];
705 } else {
706 var->staleLowByte_ = var->lowTranByte_[index];
707 var->staleHighByte_ = var->highTranByte_[index];
708 }
709 }
710
711 // Standard variable
712 } else {
713 if (var->mode_ != "RO") {
714 var->staleLowByte_ = var->lowTranByte_[0];
715 var->staleHighByte_ = var->highTranByte_[0];
716 }
717
718 // Fast copy
719 if (var->fastByte_ != NULL) {
720 memcpy(blockData_ + var->fastByte_[0], buff, var->valueBytes_);
721
722 } else if (var->bitOffset_.size() == 1) {
723 copyBits(blockData_, var->bitOffset_[0], buff, 0, var->bitSize_[0]);
724
725 } else {
726 srcBit = 0;
727 for (x = 0; x < var->bitOffset_.size(); x++) {
728 copyBits(blockData_, var->bitOffset_[x], buff, srcBit, var->bitSize_[x]);
729 srcBit += var->bitSize_[x];
730 }
731 }
732 }
733 if ( var->mode_ != "RO" ) var->stale_ = true;
734 if (var->byteReverse_) free(buff);
735}
736
737// Get data to pointer from internal block or staged memory
738void rim::Block::getBytes(uint8_t* data, rim::Variable* var, uint32_t index) {
739 uint32_t dstBit;
740 uint32_t x;
741
742 rogue::GilRelease noGil;
743 std::lock_guard<std::mutex> lock(mtx_);
744
745 // List variable
746 if (var->numValues_ != 0) {
747 if (index >= var->numValues_)
748 throw(rogue::GeneralError::create("Block::getBytes",
749 "Index %" PRIu32 " is out of range for %s",
750 index,
751 var->name_.c_str()));
752
753 // Fast copy. See setBytes() above for why this reads valueBytes_
754 // and not valueStride_/8 -- the read-side mirrors the write-side
755 // because pyrogue rejects valueStride < valueBits upstream.
756 if (var->fastByte_ != NULL)
757 memcpy(data, blockData_ + var->fastByte_[index], var->valueBytes_);
758
759 else
760 copyBits(data, 0, blockData_, var->bitOffset_[0] + (index * var->valueStride_), var->valueBits_);
761
762 } else {
763 // Fast copy
764 if (var->fastByte_ != NULL) {
765 memcpy(data, blockData_ + var->fastByte_[0], var->valueBytes_);
766
767 } else if (var->bitOffset_.size() == 1) {
768 copyBits(data, 0, blockData_, var->bitOffset_[0], var->bitSize_[0]);
769
770 } else {
771 dstBit = 0;
772 for (x = 0; x < var->bitOffset_.size(); x++) {
773 copyBits(data, dstBit, blockData_, var->bitOffset_[x], var->bitSize_[x]);
774 dstBit += var->bitSize_[x];
775 }
776 }
777 }
778
779 // Change byte order
780 if (var->byteReverse_) {
781 reverseBytes(data, var->valueBytes_);
782 }
783}
784
786// Python functions
788
789#ifndef NO_PYTHON
790
791// Set data using python function
792void rim::Block::setPyFunc(bp::object& value, rim::Variable* var, int32_t index) {
793 uint32_t x;
794 Py_buffer valueBuf;
795 bp::object tmp;
796
797 if (index == -1) index = 0;
798
799 // Passed value is a numpy value
800 if (PyArray_Check(value.ptr())) {
801 throw(rogue::GeneralError::create("Block::setPyFunc",
802 "Passing ndarray not supported for %s",
803 var->name_.c_str()));
804
805 // Is passed value a list
806 } else if (PyList_Check(value.ptr())) {
807 bp::list vl = bp::extract<bp::list>(value);
808 uint32_t vlen = len(vl);
809
810 if ((index + vlen) > var->numValues_)
811 throw(rogue::GeneralError::create("Block::setPyFunc",
812 "Overflow error for passed array with length %" PRIu32
813 " at index %" PRIu32 ". Variable length = %" PRIu32 " for %s",
814 vlen,
815 index,
816 var->numValues_,
817 var->name_.c_str()));
818
819 for (x = 0; x < vlen; x++) {
820 tmp = vl[x];
821 bp::object ret = ((rim::VariableWrap*)var)->toBytes(tmp);
822
823 if (PyObject_GetBuffer(ret.ptr(), &(valueBuf), PyBUF_SIMPLE) < 0)
824 throw(rogue::GeneralError::create("Block::setPyFunc",
825 "Failed to extract byte array for %s",
826 var->name_.c_str()));
827
828 setBytes(reinterpret_cast<uint8_t*>(valueBuf.buf), var, index + x);
829 PyBuffer_Release(&valueBuf);
830 }
831
832 // Single value
833 } else {
834 // Call python function
835 bp::object ret = ((rim::VariableWrap*)var)->toBytes(value);
836
837 if (PyObject_GetBuffer(ret.ptr(), &(valueBuf), PyBUF_SIMPLE) < 0)
838 throw(rogue::GeneralError::create("Block::setPyFunc",
839 "Failed to extract byte array from pyFunc return value for %s",
840 var->name_.c_str()));
841
842 setBytes(reinterpret_cast<uint8_t*>(valueBuf.buf), var, index);
843 PyBuffer_Release(&valueBuf);
844 }
845}
846
847// Get data using python function
848bp::object rim::Block::getPyFunc(rim::Variable* var, int32_t index) {
849 uint8_t getBuffer[var->valueBytes_];
850
851 // Unindexed with a list variable not supported
852 if (index < 0 && var->numValues_ > 0) {
853 throw(rogue::GeneralError::create("Block::getPyFunc",
854 "Accessing unindexed value not support for %s",
855 var->name_.c_str()));
856
857 // Single value
858 } else {
859 memset(getBuffer, 0, var->valueBytes_);
860
861 getBytes(getBuffer, var, index);
862 PyObject* val = Py_BuildValue("y#", getBuffer, var->valueBytes_);
863
864 if (val == NULL) throw(rogue::GeneralError::create("Block::getPyFunc", "Failed to generate bytearray"));
865
866 bp::handle<> handle(val);
867 bp::object pass = bp::object(handle);
868
869 return ((rim::VariableWrap*)var)->fromBytes(pass);
870 }
871}
872
873#endif
874
876// Byte Array
878
879#ifndef NO_PYTHON
880
881// Set data using byte array
882void rim::Block::setByteArrayPy(bp::object& value, rim::Variable* var, int32_t index) {
883 Py_buffer valueBuf;
884
885 // Unindexed with a list variable
886 if (index < 0 && var->numValues_ > 0)
887 throw(rogue::GeneralError::create("Block::setByteArrayPy",
888 "Accessing unindexed value not supported for %s",
889 var->name_.c_str()));
890
891 if (PyObject_GetBuffer(value.ptr(), &(valueBuf), PyBUF_SIMPLE) < 0)
892 throw(rogue::GeneralError::create("Block::setByteArray",
893 "Failed to extract byte array for %s",
894 var->name_.c_str()));
895
896 setBytes(reinterpret_cast<uint8_t*>(valueBuf.buf), var, index);
897 PyBuffer_Release(&valueBuf);
898}
899
900// Get data using byte array
901bp::object rim::Block::getByteArrayPy(rim::Variable* var, int32_t index) {
902 uint8_t getBuffer[var->valueBytes_];
903
904 // Unindexed with a list variable
905 if (index < 0 && var->numValues_ > 0)
906 throw(rogue::GeneralError::create("Block::setByteArrayPy",
907 "Accessing unindexed value not supported for %s",
908 var->name_.c_str()));
909
910 memset(getBuffer, 0, var->valueBytes_);
911
912 getBytes(getBuffer, var, index);
913 PyObject* val = Py_BuildValue("y#", getBuffer, var->valueBytes_);
914
915 if (val == NULL) throw(rogue::GeneralError::create("Block::setByteArrayPy", "Failed to generate bytearray"));
916
917 bp::handle<> handle(val);
918 return bp::object(handle);
919}
920
921#endif
922
923// Set data using byte array
924void rim::Block::setByteArray(const uint8_t* value, rim::Variable* var, int32_t index) {
925 setBytes(value, var, index);
926}
927
928// Get data using byte array
929void rim::Block::getByteArray(uint8_t* value, rim::Variable* var, int32_t index) {
930 getBytes(value, var, index);
931}
932
934// Unsigned Int
936
937#ifndef NO_PYTHON
938
939// Set data using unsigned int
940void rim::Block::setUIntPy(bp::object& value, rim::Variable* var, int32_t index) {
941 if (index == -1) index = 0;
942
943 // Lambda to process an array of unsigned values.
944 auto process_uint_array = [&](auto* src, npy_intp stride, npy_intp length) {
945 for (npy_intp i = 0; i < length; ++i) {
946 setUInt(src[i * stride], var, index + i);
947 }
948 };
949
950 // Passed value is a numpy value
951 if (PyArray_Check(value.ptr())) {
952 // Cast to an array object and check that the numpy array
953 PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(value.ptr());
954 npy_intp ndims = PyArray_NDIM(arr);
955 npy_intp* dims = PyArray_SHAPE(arr);
956 npy_intp* strides = PyArray_STRIDES(arr);
957 if (ndims != 1)
958 throw(rogue::GeneralError::create("Block::setUIntPy",
959 "Invalid number of dimensions (%" PRIu32 ") for passed ndarray for %s",
960 ndims,
961 var->name_.c_str()));
962
963 if ((index + dims[0]) > var->numValues_)
964 throw(rogue::GeneralError::create("Block::setUIntPy",
965 "Overflow error for passed array with length %" PRIu32
966 " at index %" PRIu32 ". Variable length = %" PRIu32 " for %s",
967 dims[0],
968 index,
969 var->numValues_,
970 var->name_.c_str()));
971
972 int type = PyArray_TYPE(arr);
973 switch (type) {
974 case NPY_UINT64: {
975 uint64_t* src = reinterpret_cast<uint64_t*>(PyArray_DATA(arr));
976 npy_intp stride = strides[0] / sizeof(uint64_t);
977 process_uint_array(src, stride, dims[0]);
978 break;
979 }
980 case NPY_UINT32: {
981 uint32_t* src = reinterpret_cast<uint32_t*>(PyArray_DATA(arr));
982 npy_intp stride = strides[0] / sizeof(uint32_t);
983 process_uint_array(src, stride, dims[0]);
984 break;
985 }
986 case NPY_UINT16: {
987 uint16_t* src = reinterpret_cast<uint16_t*>(PyArray_DATA(arr));
988 npy_intp stride = strides[0] / sizeof(uint16_t);
989 process_uint_array(src, stride, dims[0]);
990 break;
991 }
992 case NPY_UINT8: {
993 uint8_t* src = reinterpret_cast<uint8_t*>(PyArray_DATA(arr));
994 npy_intp stride = strides[0] / sizeof(uint8_t);
995 process_uint_array(src, stride, dims[0]);
996 break;
997 }
998 default:
999 throw(rogue::GeneralError::create("Block::setUIntPy",
1000 "Passed nparray is not of an accepted unsigned int type (uint64, uint32, uint16, uint8) for %s",
1001 var->name_.c_str()));
1002 }
1003
1004 // Is passed value a list
1005 } else if (PyList_Check(value.ptr())) {
1006 bp::list vl = bp::extract<bp::list>(value);
1007 uint32_t vlen = len(vl);
1008 if ((index + vlen) > var->numValues_)
1009 throw(rogue::GeneralError::create("Block::setUIntPy",
1010 "Overflow error for passed list with length %" PRIu32
1011 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
1012 vlen,
1013 index,
1014 var->numValues_,
1015 var->name_.c_str()));
1016 for (uint32_t i = 0; i < vlen; i++) {
1017 bp::extract<uint64_t> tmp(vl[i]);
1018 if (!tmp.check())
1019 throw(rogue::GeneralError::create("Block::setUIntPy",
1020 "Failed to extract value for %s.",
1021 var->name_.c_str()));
1022 setUInt(tmp, var, index + i);
1023 }
1024
1025 // Passed scalar numpy value
1026 } else if (PyArray_CheckScalar(value.ptr())) {
1027 int type_num = PyArray_DescrFromScalar(value.ptr())->type_num;
1028 switch (type_num) {
1029 case NPY_UINT64: {
1030 uint64_t val;
1031 PyArray_ScalarAsCtype(value.ptr(), &val);
1032 setUInt(val, var, index);
1033 break;
1034 }
1035 case NPY_UINT32: {
1036 uint32_t val;
1037 PyArray_ScalarAsCtype(value.ptr(), &val);
1038 setUInt(val, var, index);
1039 break;
1040 }
1041 case NPY_UINT16: {
1042 uint16_t val;
1043 PyArray_ScalarAsCtype(value.ptr(), &val);
1044 setUInt(val, var, index);
1045 break;
1046 }
1047 case NPY_UINT8: {
1048 uint8_t val;
1049 PyArray_ScalarAsCtype(value.ptr(), &val);
1050 setUInt(val, var, index);
1051 break;
1052 }
1053 default:
1054 throw(rogue::GeneralError::create("Block::setUIntPy",
1055 "Failed to extract scalar unsigned int value for %s.",
1056 var->name_.c_str()));
1057 }
1058 } else {
1059 bp::extract<uint64_t> tmp(value);
1060 if (!tmp.check())
1061 throw(rogue::GeneralError::create("Block::setUIntPy", "Failed to extract value for %s.", var->name_.c_str()));
1062 setUInt(tmp, var, index);
1063 }
1064}
1065
1066// Get data using unsigned int
1067bp::object rim::Block::getUIntPy(rim::Variable* var, int32_t index) {
1068 bp::object ret;
1069
1070 // Unindexed with a list variable
1071 if (index < 0 && var->numValues_ > 0) {
1072 // Create a numpy array to receive it and locate the destination data buffer
1073 npy_intp dims[1] = {var->numValues_};
1074 int npType;
1075 // Choose numpy type based on the variable's valueBits.
1076 if (var->valueBits_ <= 8) {
1077 npType = NPY_UINT8;
1078 } else if (var->valueBits_ <= 16) {
1079 npType = NPY_UINT16;
1080 } else if (var->valueBits_ <= 32) {
1081 npType = NPY_UINT32;
1082 } else {
1083 npType = NPY_UINT64;
1084 }
1085
1086 PyObject* obj = PyArray_SimpleNew(1, dims, npType);
1087 PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(obj);
1088 uint32_t x;
1089 switch (npType) {
1090 case NPY_UINT8: {
1091 uint8_t* dst = reinterpret_cast<uint8_t*>(PyArray_DATA(arr));
1092 for (x = 0; x < var->numValues_; x++) {
1093 dst[x] = static_cast<uint8_t>(getUInt(var, x));
1094 }
1095 break;
1096 }
1097 case NPY_UINT16: {
1098 uint16_t* dst = reinterpret_cast<uint16_t*>(PyArray_DATA(arr));
1099 for (x = 0; x < var->numValues_; x++) {
1100 dst[x] = static_cast<uint16_t>(getUInt(var, x));
1101 }
1102 break;
1103 }
1104 case NPY_UINT32: {
1105 uint32_t* dst = reinterpret_cast<uint32_t*>(PyArray_DATA(arr));
1106 for (x = 0; x < var->numValues_; x++) {
1107 dst[x] = static_cast<uint32_t>(getUInt(var, x));
1108 }
1109 break;
1110 }
1111 case NPY_UINT64: {
1112 uint64_t* dst = reinterpret_cast<uint64_t*>(PyArray_DATA(arr));
1113 for (x = 0; x < var->numValues_; x++) {
1114 dst[x] = getUInt(var, x);
1115 }
1116 break;
1117 }
1118 }
1119 boost::python::handle<> handle(obj);
1120 ret = bp::object(handle);
1121 } else {
1122 PyObject* val = Py_BuildValue("K", getUInt(var, index));
1123 if (val == NULL)
1124 throw(rogue::GeneralError::create("Block::getUIntPy", "Failed to generate UInt"));
1125 bp::handle<> handle(val);
1126 ret = bp::object(handle);
1127 }
1128 return ret;
1129}
1130
1131#endif
1132
1133// Set data using unsigned int
1134void rim::Block::setUInt(const uint64_t& val, rim::Variable* var, int32_t index) {
1135 // Check range
1136 if ((var->minValue_ != 0 || var->maxValue_ != 0) && (val > var->maxValue_ || val < var->minValue_))
1137 throw(rogue::GeneralError::create("Block::setUInt",
1138 "Value range error for %s. Value=%" PRIu64 ", Min=%f, Max=%f",
1139 var->name_.c_str(),
1140 val,
1141 var->minValue_,
1142 var->maxValue_));
1143
1144 setBytes(reinterpret_cast<uint8_t*>(const_cast<uint64_t*>(&val)), var, index);
1145}
1146
1147// Get data using unsigned int
1148uint64_t rim::Block::getUInt(rim::Variable* var, int32_t index) {
1149 uint64_t tmp = 0;
1150
1151 getBytes(reinterpret_cast<uint8_t*>(&tmp), var, index);
1152
1153 return tmp;
1154}
1155
1157// Signed Int
1159
1160#ifndef NO_PYTHON
1161
1162// Set data using int
1163void rim::Block::setIntPy(bp::object& value, rim::Variable* var, int32_t index) {
1164 if (index == -1) index = 0;
1165
1166 // Lambda to process an array of signed values.
1167 auto process_int_array = [&](auto* src, npy_intp stride, npy_intp length) {
1168 for (npy_intp i = 0; i < length; ++i) {
1169 setInt(src[i * stride], var, index + i);
1170 }
1171 };
1172
1173 // Passed value is a numpy value
1174 if (PyArray_Check(value.ptr())) {
1175 // Cast to an array object and check that the numpy array
1176 PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(value.ptr());
1177 npy_intp ndims = PyArray_NDIM(arr);
1178 npy_intp* dims = PyArray_SHAPE(arr);
1179 npy_intp* strides = PyArray_STRIDES(arr);
1180 if (ndims != 1)
1181 throw(rogue::GeneralError::create("Block::setIntPy",
1182 "Invalid number of dimensions (%" PRIu32 ") for passed ndarray for %s",
1183 ndims,
1184 var->name_.c_str()));
1185
1186 if ((index + dims[0]) > var->numValues_)
1187 throw(rogue::GeneralError::create("Block::setIntPy",
1188 "Overflow error for passed array with length %" PRIu32
1189 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
1190 dims[0],
1191 index,
1192 var->numValues_,
1193 var->name_.c_str()));
1194
1195 int type = PyArray_TYPE(arr);
1196 switch (type) {
1197 case NPY_INT64: {
1198 int64_t* src = reinterpret_cast<int64_t*>(PyArray_DATA(arr));
1199 npy_intp stride = strides[0] / sizeof(int64_t);
1200 process_int_array(src, stride, dims[0]);
1201 break;
1202 }
1203 case NPY_INT32: {
1204 int32_t* src = reinterpret_cast<int32_t*>(PyArray_DATA(arr));
1205 npy_intp stride = strides[0] / sizeof(int32_t);
1206 process_int_array(src, stride, dims[0]);
1207 break;
1208 }
1209 case NPY_INT16: {
1210 int16_t* src = reinterpret_cast<int16_t*>(PyArray_DATA(arr));
1211 npy_intp stride = strides[0] / sizeof(int16_t);
1212 process_int_array(src, stride, dims[0]);
1213 break;
1214 }
1215 case NPY_INT8: {
1216 int8_t* src = reinterpret_cast<int8_t*>(PyArray_DATA(arr));
1217 npy_intp stride = strides[0] / sizeof(int8_t);
1218 process_int_array(src, stride, dims[0]);
1219 break;
1220 }
1221 default:
1222 throw(rogue::GeneralError::create("Block::setIntPy",
1223 "Passed nparray is not of an accepted signed int type (int64, int32, int16, int8) for %s",
1224 var->name_.c_str()));
1225 }
1226
1227 // Is passed value a list
1228 } else if (PyList_Check(value.ptr())) {
1229 bp::list vl = bp::extract<bp::list>(value);
1230 uint32_t vlen = len(vl);
1231
1232 if ((index + vlen) > var->numValues_)
1233 throw(rogue::GeneralError::create("Block::setIntPy",
1234 "Overflow error for passed list with length %" PRIu32
1235 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
1236 vlen,
1237 index,
1238 var->numValues_,
1239 var->name_.c_str()));
1240 for (uint32_t i = 0; i < vlen; i++) {
1241 bp::extract<int64_t> tmp(vl[i]);
1242 if (!tmp.check())
1243 throw(rogue::GeneralError::create("Block::setIntPy",
1244 "Failed to extract value for %s.",
1245 var->name_.c_str()));
1246 setInt(tmp, var, index + i);
1247 }
1248
1249 // Passed scalar numpy value
1250 } else if (PyArray_CheckScalar(value.ptr())) {
1251 int type_num = PyArray_DescrFromScalar(value.ptr())->type_num;
1252 switch (type_num) {
1253 case NPY_INT64: {
1254 int64_t val;
1255 PyArray_ScalarAsCtype(value.ptr(), &val);
1256 setInt(val, var, index);
1257 break;
1258 }
1259 case NPY_INT32: {
1260 int32_t val;
1261 PyArray_ScalarAsCtype(value.ptr(), &val);
1262 setInt(val, var, index);
1263 break;
1264 }
1265 case NPY_INT16: {
1266 int16_t val;
1267 PyArray_ScalarAsCtype(value.ptr(), &val);
1268 setInt(val, var, index);
1269 break;
1270 }
1271 case NPY_INT8: {
1272 int8_t val;
1273 PyArray_ScalarAsCtype(value.ptr(), &val);
1274 setInt(val, var, index);
1275 break;
1276 }
1277 default:
1278 throw(rogue::GeneralError::create("Block::setIntPy",
1279 "Failed to extract scalar signed int value for %s.",
1280 var->name_.c_str()));
1281 }
1282 } else {
1283 bp::extract<int64_t> tmp(value);
1284
1285 if (!tmp.check())
1286 throw(rogue::GeneralError::create("Block::setIntPy", "Failed to extract value for %s.", var->name_.c_str()));
1287 setInt(tmp, var, index);
1288 }
1289}
1290
1291// Get data using int
1292bp::object rim::Block::getIntPy(rim::Variable* var, int32_t index) {
1293 bp::object ret;
1294
1295 // Unindexed with a list variable
1296 if (index < 0 && var->numValues_ > 0) {
1297 // Create a numpy array to receive it and locate the destination data buffer
1298 npy_intp dims[1] = {var->numValues_};
1299 int npType;
1300 if (var->valueBits_ <= 8) {
1301 npType = NPY_INT8;
1302 } else if (var->valueBits_ <= 16) {
1303 npType = NPY_INT16;
1304 } else if (var->valueBits_ <= 32) {
1305 npType = NPY_INT32;
1306 } else {
1307 npType = NPY_INT64;
1308 }
1309 PyObject* obj = PyArray_SimpleNew(1, dims, npType);
1310 PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(obj);
1311 uint32_t x;
1312 switch (npType) {
1313 case NPY_INT8: {
1314 int8_t* dst = reinterpret_cast<int8_t*>(PyArray_DATA(arr));
1315 for (x = 0; x < var->numValues_; x++) {
1316 dst[x] = static_cast<int8_t>(getInt(var, x));
1317 }
1318 break;
1319 }
1320 case NPY_INT16: {
1321 int16_t* dst = reinterpret_cast<int16_t*>(PyArray_DATA(arr));
1322 for (x = 0; x < var->numValues_; x++) {
1323 dst[x] = static_cast<int16_t>(getInt(var, x));
1324 }
1325 break;
1326 }
1327 case NPY_INT32: {
1328 int32_t* dst = reinterpret_cast<int32_t*>(PyArray_DATA(arr));
1329 for (x = 0; x < var->numValues_; x++) {
1330 dst[x] = static_cast<int32_t>(getInt(var, x));
1331 }
1332 break;
1333 }
1334 case NPY_INT64: {
1335 int64_t* dst = reinterpret_cast<int64_t*>(PyArray_DATA(arr));
1336 for (x = 0; x < var->numValues_; x++) {
1337 dst[x] = getInt(var, x);
1338 }
1339 break;
1340 }
1341 }
1342 boost::python::handle<> handle(obj);
1343 ret = bp::object(handle);
1344 } else {
1345 PyObject* val = Py_BuildValue("L", getInt(var, index));
1346 if (val == NULL)
1347 throw(rogue::GeneralError::create("Block::getIntPy", "Failed to generate Int"));
1348 bp::handle<> handle(val);
1349 ret = bp::object(handle);
1350 }
1351 return ret;
1352}
1353
1354#endif
1355
1356// Set data using int
1357void rim::Block::setInt(const int64_t& val, rim::Variable* var, int32_t index) {
1358 // Check range
1359 if ((var->minValue_ != 0 || var->maxValue_ != 0) && (val > var->maxValue_ || val < var->minValue_))
1360 throw(rogue::GeneralError::create("Block::setInt",
1361 "Value range error for %s. Value=%" PRId64 ", Min=%f, Max=%f",
1362 var->name_.c_str(),
1363 val,
1364 var->minValue_,
1365 var->maxValue_));
1366
1367 // This works because all bits between the msb and bit 64 are set to '1' for a negative value
1368 setBytes(reinterpret_cast<uint8_t*>(const_cast<int64_t*>(&val)), var, index);
1369}
1370
1371// Get data using int
1372int64_t rim::Block::getInt(rim::Variable* var, int32_t index) {
1373 int64_t tmp = 0;
1374
1375 getBytes(reinterpret_cast<uint8_t*>(&tmp), var, index);
1376
1377 if (var->valueBits_ != 64) {
1378 if (tmp >= static_cast<uint64_t>(pow(2, var->valueBits_ - 1))) {
1379 tmp -= static_cast<uint64_t>(pow(2, var->valueBits_));
1380 }
1381 }
1382 return tmp;
1383}
1384
1386// Bool
1388
1389#ifndef NO_PYTHON
1390
1391// Set data using bool
1392void rim::Block::setBoolPy(bp::object& value, rim::Variable* var, int32_t index) {
1393 uint32_t x;
1394
1395 if (index == -1) index = 0;
1396
1397 // Passed value is a numpy value
1398 if (PyArray_Check(value.ptr())) {
1399 // Cast to an array object and check that the numpy array
1400 PyArrayObject* arr = reinterpret_cast<decltype(arr)>(value.ptr());
1401 npy_intp ndims = PyArray_NDIM(arr);
1402 npy_intp* dims = PyArray_SHAPE(arr);
1403 npy_intp* strides = PyArray_STRIDES(arr);
1404
1405 if (ndims != 1)
1406 throw(rogue::GeneralError::create("Block::setBoolPy",
1407 "Invalid number of dimensions (%" PRIu32 ") for passed ndarray for %s",
1408 ndims,
1409 var->name_.c_str()));
1410
1411 if ((index + dims[0]) > var->numValues_)
1412 throw(rogue::GeneralError::create("Block::setBoolPy",
1413 "Overflow error for passed array with length %" PRIu32
1414 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
1415 dims[0],
1416 index,
1417 var->numValues_,
1418 var->name_.c_str()));
1419
1420 if (PyArray_TYPE(arr) == NPY_BOOL) {
1421 bool* src = reinterpret_cast<bool*>(PyArray_DATA(arr));
1422 npy_intp stride = strides[0] / sizeof(bool);
1423 for (x = 0; x < dims[0]; x++) {
1424 setBool(src[x * stride], var, index + x);
1425 }
1426 } else {
1427 throw(rogue::GeneralError::create("Block::setBoolPy",
1428 "Passed nparray is not of type (bool) for %s",
1429 var->name_.c_str()));
1430 }
1431
1432 // Is passed value a list
1433 } else if (PyList_Check(value.ptr())) {
1434 bp::list vl = bp::extract<bp::list>(value);
1435 uint32_t vlen = len(vl);
1436
1437 if ((index + vlen) > var->numValues_)
1438 throw(rogue::GeneralError::create("Block::setBoolPy",
1439 "Overflow error for passed array with length %" PRIu32
1440 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
1441 vlen,
1442 index,
1443 var->numValues_,
1444 var->name_.c_str()));
1445
1446 for (x = 0; x < vlen; x++) {
1447 bp::extract<bool> tmp(vl[x]);
1448
1449 if (!tmp.check())
1450 throw(rogue::GeneralError::create("Block::setBoolPy",
1451 "Failed to extract value for %s.",
1452 var->name_.c_str()));
1453
1454 setBool(tmp, var, index + x);
1455 }
1456
1457 // Passed scalar numpy value
1458 } else if (PyArray_CheckScalar(value.ptr())) {
1459 if (PyArray_DescrFromScalar(value.ptr())->type_num == NPY_BOOL) {
1460 bool val;
1461 PyArray_ScalarAsCtype(value.ptr(), &val);
1462 setBool(val, var, index);
1463 } else {
1464 throw(
1465 rogue::GeneralError::create("Block::setBoolPy", "Failed to extract value for %s.", var->name_.c_str()));
1466 }
1467 } else {
1468 bp::extract<bool> tmp(value);
1469
1470 if (!tmp.check())
1471 throw(
1472 rogue::GeneralError::create("Block::setBoolPy", "Failed to extract value for %s.", var->name_.c_str()));
1473
1474 setBool(tmp, var, index);
1475 }
1476}
1477
1478// Get data using bool
1479bp::object rim::Block::getBoolPy(rim::Variable* var, int32_t index) {
1480 bp::object ret;
1481 uint32_t x;
1482
1483 // Unindexed with a list variable
1484 if (index < 0 && var->numValues_ > 0) {
1485 // Create a numpy array to receive it and locate the destination data buffer
1486 npy_intp dims[1] = {var->numValues_};
1487 PyObject* obj = PyArray_SimpleNew(1, dims, NPY_BOOL);
1488 PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(obj);
1489 bool* dst = reinterpret_cast<bool*>(PyArray_DATA(arr));
1490
1491 for (x = 0; x < var->numValues_; x++) dst[x] = getBool(var, x);
1492
1493 boost::python::handle<> handle(obj);
1494 ret = bp::object(handle);
1495
1496 } else {
1497 bp::handle<> handle(bp::borrowed(getBool(var, index) ? Py_True : Py_False));
1498 ret = bp::object(handle);
1499 }
1500 return ret;
1501}
1502
1503#endif
1504
1505// Set data using bool
1506void rim::Block::setBool(const bool& value, rim::Variable* var, int32_t index) {
1507 uint8_t val = (uint8_t)value;
1508 setBytes(reinterpret_cast<uint8_t*>(&val), var, index);
1509}
1510
1511// Get data using bool
1512bool rim::Block::getBool(rim::Variable* var, int32_t index) {
1513 uint8_t tmp = 0;
1514
1515 getBytes(reinterpret_cast<uint8_t*>(&tmp), var, index);
1516
1517 return tmp ? true : false;
1518}
1519
1521// String
1523
1524#ifndef NO_PYTHON
1525
1526// Set data using string
1527void rim::Block::setStringPy(bp::object& value, rim::Variable* var, int32_t index) {
1528 uint32_t x;
1529 std::string strVal;
1530
1531 // Unindexed with a list variable
1532 if (index < 0 && var->numValues_ > 0)
1533 throw(rogue::GeneralError::create("Block::setStringPy",
1534 "Using nparray not supported for %s",
1535 var->name_.c_str()));
1536
1537 bp::extract<char*> tmp(value);
1538
1539 if (!tmp.check())
1540 throw(rogue::GeneralError::create("Block::setStringPy", "Failed to extract value for %s.", var->name_.c_str()));
1541
1542 strVal = tmp;
1543 setString(strVal, var, index);
1544}
1545
1546// Get data using string
1547bp::object rim::Block::getStringPy(rim::Variable* var, int32_t index) {
1548 bp::object ret;
1549 std::string strVal;
1550 uint32_t x;
1551
1552 // Unindexed with a list variable
1553 if (index < 0 && var->numValues_ > 0)
1554 throw(
1555 rogue::GeneralError::create("Block::getStringPy", "Using ndarry not supported for %s", var->name_.c_str()));
1556
1557 getString(var, strVal, index);
1558 PyObject* val = Py_BuildValue("s", strVal.c_str());
1559
1560 if (val == NULL) throw(rogue::GeneralError::create("Block::getStringPy", "Failed to generate String"));
1561
1562 bp::handle<> handle(val);
1563 return bp::object(handle);
1564}
1565
1566#endif
1567
1568// Set data using string
1569void rim::Block::setString(const std::string& value, rim::Variable* var, int32_t index) {
1570 uint8_t getBuffer[var->valueBytes_];
1571
1572 memset(getBuffer, 0, var->valueBytes_);
1573
1574 strncpy(reinterpret_cast<char*>(getBuffer), value.c_str(), var->valueBytes_ - 1);
1575
1576 setBytes(getBuffer, var, index);
1577}
1578
1579// Get data using string
1580std::string rim::Block::getString(rim::Variable* var, int32_t index) {
1581 std::string ret;
1582 getString(var, ret, index);
1583 return ret;
1584}
1585
1586// Get data into string
1587void rim::Block::getString(rim::Variable* var, std::string& retString, int32_t index) {
1588 char getBuffer[var->valueBytes_ + 1];
1589
1590 memset(getBuffer, 0, var->valueBytes_ + 1);
1591
1592 getBytes(reinterpret_cast<uint8_t*>(getBuffer), var, index);
1593
1594 retString = getBuffer;
1595}
1596
1598// Float
1600
1601#ifndef NO_PYTHON
1602
1603// Set data using float
1604void rim::Block::setFloatPy(bp::object& value, rim::Variable* var, int32_t index) {
1605 uint32_t x;
1606
1607 if (index == -1) index = 0;
1608
1609 // Passed value is a numpy value
1610 if (PyArray_Check(value.ptr())) {
1611 // Cast to an array object and check that the numpy array
1612 PyArrayObject* arr = reinterpret_cast<decltype(arr)>(value.ptr());
1613 npy_intp ndims = PyArray_NDIM(arr);
1614 npy_intp* dims = PyArray_SHAPE(arr);
1615 npy_intp* strides = PyArray_STRIDES(arr);
1616
1617 if (ndims != 1)
1618 throw(rogue::GeneralError::create("Block::setFloatPy",
1619 "Invalid number of dimensions (%" PRIu32 ") for passed ndarray for %s",
1620 ndims,
1621 var->name_.c_str()));
1622
1623 if ((index + dims[0]) > var->numValues_)
1624 throw(rogue::GeneralError::create("Block::setFloatPy",
1625 "Overflow error for passed array with length %" PRIu32
1626 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
1627 dims[0],
1628 index,
1629 var->numValues_,
1630 var->name_.c_str()));
1631
1632 if (PyArray_TYPE(arr) == NPY_FLOAT32) {
1633 float* src = reinterpret_cast<float*>(PyArray_DATA(arr));
1634 npy_intp stride = strides[0] / sizeof(float);
1635 for (x = 0; x < dims[0]; x++) {
1636 setFloat(src[x * stride], var, index + x);
1637 }
1638 } else {
1639 throw(rogue::GeneralError::create("Block::setFLoatPy",
1640 "Passed nparray is not of type (float32) for %s",
1641 var->name_.c_str()));
1642 }
1643
1644 // Is passed value a list
1645 } else if (PyList_Check(value.ptr())) {
1646 bp::list vl = bp::extract<bp::list>(value);
1647 uint32_t vlen = len(vl);
1648
1649 if ((index + vlen) > var->numValues_)
1650 throw(rogue::GeneralError::create("Block::setFloatPy",
1651 "Overflow error for passed array with length %" PRIu32
1652 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
1653 vlen,
1654 index,
1655 var->numValues_,
1656 var->name_.c_str()));
1657
1658 for (x = 0; x < vlen; x++) {
1659 bp::extract<float> tmp(vl[x]);
1660
1661 if (!tmp.check())
1662 throw(rogue::GeneralError::create("Block::setFloatPy",
1663 "Failed to extract value for %s.",
1664 var->name_.c_str()));
1665
1666 setFloat(tmp, var, index + x);
1667 }
1668
1669 // Passed scalar numpy value
1670 } else if (PyArray_CheckScalar(value.ptr())) {
1671 if (PyArray_DescrFromScalar(value.ptr())->type_num == NPY_FLOAT32) {
1672 float val;
1673 PyArray_ScalarAsCtype(value.ptr(), &val);
1674 setFloat(val, var, index);
1675 } else {
1676 throw(rogue::GeneralError::create("Block::setFloatPy",
1677 "Failed to extract value for %s.",
1678 var->name_.c_str()));
1679 }
1680 } else {
1681 bp::extract<float> tmp(value);
1682
1683 if (!tmp.check())
1684 throw(rogue::GeneralError::create("Block::setFloatPy",
1685 "Failed to extract value for %s.",
1686 var->name_.c_str()));
1687
1688 setFloat(tmp, var, index);
1689 }
1690}
1691
1692// Get data using float
1693bp::object rim::Block::getFloatPy(rim::Variable* var, int32_t index) {
1694 bp::object ret;
1695 uint32_t x;
1696
1697 // Unindexed with a list variable
1698 if (index < 0 && var->numValues_ > 0) {
1699 // Create a numpy array to receive it and locate the destination data buffer
1700 npy_intp dims[1] = {var->numValues_};
1701 PyObject* obj = PyArray_SimpleNew(1, dims, NPY_FLOAT32);
1702 PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(obj);
1703 float* dst = reinterpret_cast<float*>(PyArray_DATA(arr));
1704
1705 for (x = 0; x < var->numValues_; x++) dst[x] = getFloat(var, x);
1706
1707 boost::python::handle<> handle(obj);
1708 ret = bp::object(handle);
1709
1710 } else {
1711 PyObject* val = Py_BuildValue("f", getFloat(var, index));
1712
1713 if (val == NULL) throw(rogue::GeneralError::create("Block::getFloatPy", "Failed to generate Float"));
1714
1715 bp::handle<> handle(val);
1716 ret = bp::object(handle);
1717 }
1718 return ret;
1719}
1720
1721#endif
1722
1723// Set data using float
1724void rim::Block::setFloat(const float& val, rim::Variable* var, int32_t index) {
1725 // Check range
1726 if ((var->minValue_ != 0 || var->maxValue_ != 0) && (val > var->maxValue_ || val < var->minValue_))
1727 throw(rogue::GeneralError::create("Block::setFloat",
1728 "Value range error for %s. Value=%f, Min=%f, Max=%f",
1729 var->name_.c_str(),
1730 val,
1731 var->minValue_,
1732 var->maxValue_));
1733
1734 setBytes(reinterpret_cast<uint8_t*>(const_cast<float*>(&val)), var, index);
1735}
1736
1737// Get data using float
1738float rim::Block::getFloat(rim::Variable* var, int32_t index) {
1739 float tmp = 0;
1740
1741 getBytes(reinterpret_cast<uint8_t*>(&tmp), var, index);
1742
1743 return tmp;
1744}
1745
1746// IEEE 754 half-precision conversion helpers
1747namespace {
1748
1749uint16_t floatToHalf(float value) {
1750 uint32_t f;
1751 std::memcpy(&f, &value, sizeof(f));
1752
1753 uint16_t sign = (f >> 16) & 0x8000;
1754 int32_t exponent = ((f >> 23) & 0xFF) - 127 + 15;
1755 uint32_t mantissa = f & 0x7FFFFF;
1756
1757 if (exponent <= 0) {
1758 if (exponent < -10) return sign;
1759 mantissa |= 0x800000;
1760 uint32_t shift = 14 - exponent;
1761 uint32_t halfMant = mantissa >> shift;
1762 return sign | static_cast<uint16_t>(halfMant);
1763 } else if (exponent == 0xFF - 127 + 15) {
1764 if (mantissa) {
1765 uint16_t halfMantissa = static_cast<uint16_t>(mantissa >> 13);
1766 if (halfMantissa == 0) halfMantissa = 0x0001;
1767 return sign | 0x7C00 | halfMantissa;
1768 }
1769 return sign | 0x7C00;
1770 } else if (exponent > 30) {
1771 return sign | 0x7C00;
1772 }
1773 return sign | (exponent << 10) | (mantissa >> 13);
1774}
1775
1776float halfToFloat(uint16_t h) {
1777 uint32_t sign = (h & 0x8000) << 16;
1778 uint32_t exponent = (h >> 10) & 0x1F;
1779 uint32_t mantissa = h & 0x3FF;
1780
1781 uint32_t f;
1782 if (exponent == 0) {
1783 if (mantissa == 0) {
1784 f = sign;
1785 } else {
1786 int32_t exponentWork = 1;
1787 while (!(mantissa & 0x400)) {
1788 mantissa <<= 1;
1789 exponentWork--;
1790 }
1791 mantissa &= 0x3FF;
1792 f = sign | (static_cast<uint32_t>(exponentWork + 127 - 15) << 23) | (mantissa << 13);
1793 }
1794 } else if (exponent == 31) {
1795 f = sign | 0x7F800000 | (mantissa << 13);
1796 } else {
1797 f = sign | ((exponent + 127 - 15) << 23) | (mantissa << 13);
1798 }
1799
1800 float result;
1801 std::memcpy(&result, &f, sizeof(result));
1802 return result;
1803}
1804
1805uint8_t floatToFloat8(float value) {
1806 uint32_t f;
1807 std::memcpy(&f, &value, sizeof(f));
1808
1809 uint8_t sign = (f >> 24) & 0x80;
1810 int32_t exponent = ((f >> 23) & 0xFF) - 127 + 7; // rebias float32 to E4M3 bias=7
1811 uint32_t mantissa = f & 0x7FFFFF;
1812
1813 // NaN input -> NaN output (0x7F)
1814 if (((f >> 23) & 0xFF) == 0xFF && mantissa != 0) return 0x7F;
1815
1816 // Infinity or overflow -> max finite (0x7E positive, 0xFE negative)
1817 if (((f >> 23) & 0xFF) == 0xFF || exponent > 15) return sign | 0x7E;
1818
1819 if (exponent <= 0) {
1820 // Subnormal path
1821 if (exponent < -3) return sign; // too small, flush to zero
1822 mantissa |= 0x800000;
1823 uint32_t shift = 1 - exponent;
1824 mantissa >>= shift;
1825 return sign | ((mantissa >> 20) & 0x07);
1826 }
1827 return sign | (exponent << 3) | ((mantissa >> 20) & 0x07);
1828}
1829
1830float float8ToFloat(uint8_t f8) {
1831 // Both 0x7F and 0xFF are NaN (sign bit can be 0 or 1, exp=1111, mant=111)
1832 if ((f8 & 0x7F) == 0x7F) {
1833 uint32_t nan = 0x7FC00000;
1834 float result;
1835 std::memcpy(&result, &nan, sizeof(result));
1836 return result;
1837 }
1838
1839 uint32_t sign = (static_cast<uint32_t>(f8) & 0x80) << 24;
1840 uint32_t exponent = (f8 >> 3) & 0x0F;
1841 uint32_t mantissa = f8 & 0x07;
1842
1843 uint32_t f;
1844 if (exponent == 0) {
1845 if (mantissa == 0) {
1846 f = sign; // zero
1847 } else {
1848 // Subnormal: shift left until the implicit leading 1 reaches bit 3,
1849 // then strip it. Use int32_t so exponentWork can go negative safely.
1850 int32_t exponentWork = 1;
1851 while (!(mantissa & 0x08)) {
1852 mantissa <<= 1;
1853 exponentWork--;
1854 }
1855 mantissa &= 0x07;
1856 f = sign | (static_cast<uint32_t>(exponentWork + 127 - 7) << 23) | (mantissa << 20);
1857 }
1858 } else {
1859 f = sign | (static_cast<uint32_t>(exponent + 127 - 7) << 23) | (mantissa << 20);
1860 }
1861
1862 float result;
1863 std::memcpy(&result, &f, sizeof(result));
1864 return result;
1865}
1866
1867uint16_t floatToBFloat16(float value) {
1868 uint32_t f;
1869 std::memcpy(&f, &value, sizeof(f));
1870 // BFloat16 is simply the upper 16 bits of the float32 bit pattern.
1871 // All special values (NaN, infinity, zero, subnormals) are preserved.
1872 uint16_t bf16 = static_cast<uint16_t>(f >> 16);
1873 // Preserve NaN: truncation can clear payload bits, turning NaN into Inf.
1874 uint32_t exponent = (f >> 23) & 0xFF;
1875 uint32_t mantissa = f & 0x7FFFFF;
1876 if (exponent == 0xFF && mantissa != 0 && (bf16 & 0x007F) == 0) {
1877 bf16 |= 0x0001;
1878 }
1879 return bf16;
1880}
1881
1882float bfloat16ToFloat(uint16_t bf16) {
1883 uint32_t f = static_cast<uint32_t>(bf16) << 16;
1884 float result;
1885 std::memcpy(&result, &f, sizeof(result));
1886 return result;
1887}
1888
1889// TensorFloat32: 1s / 8e / 10m, bias=127, same exponent as float32
1890// Stored in 4 bytes; lower 13 mantissa bits are zeroed.
1891
1892uint32_t floatToTensorFloat32(float value) {
1893 uint32_t f;
1894 std::memcpy(&f, &value, sizeof(f));
1895 uint32_t tf32 = f & 0xFFFFE000U;
1896 // Preserve NaN: masking can clear payload bits, turning NaN into Inf.
1897 if ((f & 0x7F800000U) == 0x7F800000U && (f & 0x007FFFFFU) != 0 &&
1898 (tf32 & 0x007FFFFFU) == 0) {
1899 tf32 |= 0x00002000U;
1900 }
1901 return tf32;
1902}
1903
1904float tensorFloat32ToFloat(uint32_t tf32) {
1905 float result;
1906 std::memcpy(&result, &tf32, sizeof(result));
1907 return result;
1908}
1909
1910// Float6 E3M2: 1s / 3e / 2m, bias=3, no Inf/NaN
1911// Stored in lower 6 bits of uint8_t
1912
1913uint8_t floatToFloat6(float value) {
1914 uint32_t f;
1915 std::memcpy(&f, &value, sizeof(f));
1916
1917 uint8_t sign = (f >> 26) & 0x20; // sign bit -> bit 5 of result
1918 int32_t exponent = ((f >> 23) & 0xFF) - 127 + 3; // rebias to E3M2 bias=3
1919 uint32_t mantissa = f & 0x7FFFFF;
1920
1921 // NaN or Infinity input -> clamp to max finite (no NaN/Inf in E3M2)
1922 if (((f >> 23) & 0xFF) == 0xFF) {
1923 // NaN has no meaningful sign: always clamp to positive max.
1924 // Infinity preserves sign.
1925 uint8_t nan_sign = (f & 0x7FFFFF) ? 0x00 : static_cast<uint8_t>(sign);
1926 return nan_sign | 0x1F;
1927 }
1928
1929 // Overflow -> clamp to max finite
1930 if (exponent > 7) return sign | 0x1F;
1931
1932 if (exponent <= 0) {
1933 // Subnormal path
1934 if (exponent < -2) return sign; // too small, flush to zero
1935 mantissa |= 0x800000;
1936 uint32_t shift = 1 - exponent;
1937 mantissa >>= shift;
1938 return sign | ((mantissa >> 21) & 0x03);
1939 }
1940 return sign | (exponent << 2) | ((mantissa >> 21) & 0x03);
1941}
1942
1943float float6ToFloat(uint8_t f6) {
1944 // Mask to 6 bits (upper 2 bits of byte are unused)
1945 f6 &= 0x3F;
1946
1947 float sign = (f6 & 0x20) ? -1.0f : 1.0f;
1948 uint32_t exponent = (f6 >> 2) & 0x07;
1949 uint32_t mantissa = f6 & 0x03;
1950
1951 if (exponent == 0) {
1952 if (mantissa == 0) {
1953 return sign * 0.0f; // +/- zero
1954 }
1955 // Subnormal: value = sign * mantissa/4 * 2^(1-3) = sign * mantissa * 2^(-4)
1956 return sign * static_cast<float>(mantissa) * 0.0625f; // 0.0625 = 2^(-4)
1957 }
1958 // Normal: value = sign * (1 + mantissa/4) * 2^(exponent-3)
1959 float frac = 1.0f + static_cast<float>(mantissa) / 4.0f;
1960 return sign * std::ldexp(frac, static_cast<int>(exponent) - 3);
1961}
1962
1963// Float4 E2M1: 1s / 2e / 1m, bias=1, no Inf/NaN
1964// Stored in lower 4 bits of uint8_t
1965uint8_t floatToFloat4(float value) {
1966 uint32_t f;
1967 std::memcpy(&f, &value, sizeof(f));
1968
1969 uint8_t sign = (f >> 28) & 0x08; // sign bit -> bit 3 of result
1970 int32_t exponent = ((f >> 23) & 0xFF) - 127 + 1; // rebias to E2M1 bias=1
1971 uint32_t mantissa = f & 0x7FFFFF;
1972
1973 // NaN or Infinity input -> clamp to max finite (no NaN/Inf in E2M1)
1974 if (((f >> 23) & 0xFF) == 0xFF) {
1975 // NaN has no meaningful sign: always clamp to positive max.
1976 // Infinity preserves sign.
1977 uint8_t nan_sign = (f & 0x7FFFFF) ? 0x00 : static_cast<uint8_t>(sign);
1978 return nan_sign | 0x07;
1979 }
1980
1981 // Overflow -> clamp to max finite
1982 if (exponent > 3) return sign | 0x07;
1983
1984 if (exponent <= 0) {
1985 // Subnormal path
1986 if (exponent < -1) return sign; // too small, flush to zero
1987 mantissa |= 0x800000;
1988 uint32_t shift = 1 - exponent;
1989 mantissa >>= shift;
1990 return sign | ((mantissa >> 22) & 0x01);
1991 }
1992 return sign | (exponent << 1) | ((mantissa >> 22) & 0x01);
1993}
1994
1995float float4ToFloat(uint8_t f4) {
1996 // Mask to 4 bits (upper 4 bits of byte are unused)
1997 f4 &= 0x0F;
1998
1999 float sign = (f4 & 0x08) ? -1.0f : 1.0f;
2000 uint32_t exponent = (f4 >> 1) & 0x03;
2001 uint32_t mantissa = f4 & 0x01;
2002
2003 if (exponent == 0) {
2004 if (mantissa == 0) {
2005 return sign * 0.0f; // +/- zero
2006 }
2007 // Subnormal: value = sign * mantissa * 0.5 (only one subnormal: +/-0.5)
2008 return sign * static_cast<float>(mantissa) * 0.5f;
2009 }
2010 // Normal: value = sign * (1 + mantissa/2) * 2^(exponent-1)
2011 float frac = 1.0f + static_cast<float>(mantissa) / 2.0f;
2012 return sign * std::ldexp(frac, static_cast<int>(exponent) - 1);
2013}
2014
2015} // anonymous namespace
2016
2018// Float16 (half-precision)
2020
2021#ifndef NO_PYTHON
2022
2023// Set data using float16
2024void rim::Block::setFloat16Py(bp::object& value, rim::Variable* var, int32_t index) {
2025 uint32_t x;
2026
2027 if (index == -1) index = 0;
2028
2029 // Passed value is a numpy value
2030 if (PyArray_Check(value.ptr())) {
2031 // Cast to an array object and check that the numpy array
2032 PyArrayObject* arr = reinterpret_cast<decltype(arr)>(value.ptr());
2033 npy_intp ndims = PyArray_NDIM(arr);
2034 npy_intp* dims = PyArray_SHAPE(arr);
2035 npy_intp* strides = PyArray_STRIDES(arr);
2036
2037 if (ndims != 1)
2038 throw(rogue::GeneralError::create("Block::setFloat16Py",
2039 "Invalid number of dimensions (%" PRIu32 ") for passed ndarray for %s",
2040 ndims,
2041 var->name_.c_str()));
2042
2043 if ((index + dims[0]) > var->numValues_)
2044 throw(rogue::GeneralError::create("Block::setFloat16Py",
2045 "Overflow error for passed array with length %" PRIu32
2046 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
2047 dims[0],
2048 index,
2049 var->numValues_,
2050 var->name_.c_str()));
2051
2052 if (PyArray_TYPE(arr) == NPY_HALF) {
2053 npy_half* src = reinterpret_cast<npy_half*>(PyArray_DATA(arr));
2054 npy_intp stride = strides[0] / sizeof(npy_half);
2055 for (x = 0; x < dims[0]; x++) {
2056 float val = halfToFloat(src[x * stride]);
2057 setFloat16(val, var, index + x);
2058 }
2059 } else {
2060 throw(rogue::GeneralError::create("Block::setFloat16Py",
2061 "Passed nparray is not of type (float16) for %s",
2062 var->name_.c_str()));
2063 }
2064
2065 // Is passed value a list
2066 } else if (PyList_Check(value.ptr())) {
2067 bp::list vl = bp::extract<bp::list>(value);
2068 uint32_t vlen = len(vl);
2069
2070 if ((index + vlen) > var->numValues_)
2071 throw(rogue::GeneralError::create("Block::setFloat16Py",
2072 "Overflow error for passed array with length %" PRIu32
2073 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
2074 vlen,
2075 index,
2076 var->numValues_,
2077 var->name_.c_str()));
2078
2079 for (x = 0; x < vlen; x++) {
2080 bp::extract<float> tmp(vl[x]);
2081
2082 if (!tmp.check())
2083 throw(rogue::GeneralError::create("Block::setFloat16Py",
2084 "Failed to extract value for %s.",
2085 var->name_.c_str()));
2086
2087 setFloat16(tmp, var, index + x);
2088 }
2089
2090 // Passed scalar numpy value
2091 } else if (PyArray_CheckScalar(value.ptr())) {
2092 if (PyArray_DescrFromScalar(value.ptr())->type_num == NPY_HALF) {
2093 npy_half val;
2094 PyArray_ScalarAsCtype(value.ptr(), &val);
2095 float fval = halfToFloat(val);
2096 setFloat16(fval, var, index);
2097 } else {
2098 throw(rogue::GeneralError::create("Block::setFloat16Py",
2099 "Failed to extract value for %s.",
2100 var->name_.c_str()));
2101 }
2102 } else {
2103 bp::extract<float> tmp(value);
2104
2105 if (!tmp.check())
2106 throw(rogue::GeneralError::create("Block::setFloat16Py",
2107 "Failed to extract value for %s.",
2108 var->name_.c_str()));
2109
2110 setFloat16(tmp, var, index);
2111 }
2112}
2113
2114// Get data using float16
2115bp::object rim::Block::getFloat16Py(rim::Variable* var, int32_t index) {
2116 bp::object ret;
2117 uint32_t x;
2118
2119 // Unindexed with a list variable
2120 if (index < 0 && var->numValues_ > 0) {
2121 // Create a numpy array to receive it and locate the destination data buffer
2122 npy_intp dims[1] = {var->numValues_};
2123 PyObject* obj = PyArray_SimpleNew(1, dims, NPY_HALF);
2124 PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(obj);
2125 npy_half* dst = reinterpret_cast<npy_half*>(PyArray_DATA(arr));
2126
2127 for (x = 0; x < var->numValues_; x++) dst[x] = floatToHalf(getFloat16(var, x));
2128
2129 boost::python::handle<> handle(obj);
2130 ret = bp::object(handle);
2131
2132 } else {
2133 PyObject* val = Py_BuildValue("f", getFloat16(var, index));
2134
2135 if (val == NULL) throw(rogue::GeneralError::create("Block::getFloat16Py", "Failed to generate Float16"));
2136
2137 bp::handle<> handle(val);
2138 ret = bp::object(handle);
2139 }
2140 return ret;
2141}
2142
2143#endif
2144
2145// Set data using float16
2146void rim::Block::setFloat16(const float& val, rim::Variable* var, int32_t index) {
2147 // Check range
2148 if ((var->minValue_ != 0 || var->maxValue_ != 0) && (val > var->maxValue_ || val < var->minValue_))
2149 throw(rogue::GeneralError::create("Block::setFloat16",
2150 "Value range error for %s. Value=%f, Min=%f, Max=%f",
2151 var->name_.c_str(),
2152 val,
2153 var->minValue_,
2154 var->maxValue_));
2155
2156 // Convert float to half-precision and store as 2 bytes
2157 uint16_t half = floatToHalf(val);
2158 setBytes(reinterpret_cast<uint8_t*>(&half), var, index);
2159}
2160
2161// Get data using float16
2162float rim::Block::getFloat16(rim::Variable* var, int32_t index) {
2163 uint16_t tmp = 0;
2164
2165 getBytes(reinterpret_cast<uint8_t*>(&tmp), var, index);
2166
2167 return halfToFloat(tmp);
2168}
2169
2171// Float8 (E4M3)
2173
2174#ifndef NO_PYTHON
2175
2176// Set data using float8
2177void rim::Block::setFloat8Py(bp::object& value, rim::Variable* var, int32_t index) {
2178 uint32_t x;
2179
2180 if (index == -1) index = 0;
2181
2182 // Passed value is a numpy value
2183 if (PyArray_Check(value.ptr())) {
2184 // Cast to an array object and check that the numpy array
2185 PyArrayObject* arr = reinterpret_cast<decltype(arr)>(value.ptr());
2186 npy_intp ndims = PyArray_NDIM(arr);
2187 npy_intp* dims = PyArray_SHAPE(arr);
2188 npy_intp* strides = PyArray_STRIDES(arr);
2189
2190 if (ndims != 1)
2191 throw(rogue::GeneralError::create("Block::setFloat8Py",
2192 "Invalid number of dimensions (%" PRIu32 ") for passed ndarray for %s",
2193 ndims,
2194 var->name_.c_str()));
2195
2196 if ((index + dims[0]) > var->numValues_)
2197 throw(rogue::GeneralError::create("Block::setFloat8Py",
2198 "Overflow error for passed array with length %" PRIu32
2199 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
2200 dims[0],
2201 index,
2202 var->numValues_,
2203 var->name_.c_str()));
2204
2205 if (PyArray_TYPE(arr) == NPY_FLOAT) {
2206 float* src = reinterpret_cast<float*>(PyArray_DATA(arr));
2207 npy_intp stride = strides[0] / sizeof(float);
2208 for (x = 0; x < dims[0]; x++) {
2209 float val = src[x * stride];
2210 setFloat8(val, var, index + x);
2211 }
2212 } else {
2213 throw(rogue::GeneralError::create("Block::setFloat8Py",
2214 "Passed nparray is not of type (float32) for %s",
2215 var->name_.c_str()));
2216 }
2217
2218 // Is passed value a list
2219 } else if (PyList_Check(value.ptr())) {
2220 bp::list vl = bp::extract<bp::list>(value);
2221 uint32_t vlen = len(vl);
2222
2223 if ((index + vlen) > var->numValues_)
2224 throw(rogue::GeneralError::create("Block::setFloat8Py",
2225 "Overflow error for passed array with length %" PRIu32
2226 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
2227 vlen,
2228 index,
2229 var->numValues_,
2230 var->name_.c_str()));
2231
2232 for (x = 0; x < vlen; x++) {
2233 bp::extract<float> tmp(vl[x]);
2234
2235 if (!tmp.check())
2236 throw(rogue::GeneralError::create("Block::setFloat8Py",
2237 "Failed to extract value for %s.",
2238 var->name_.c_str()));
2239
2240 setFloat8(tmp, var, index + x);
2241 }
2242
2243 } else {
2244 bp::extract<float> tmp(value);
2245
2246 if (!tmp.check())
2247 throw(rogue::GeneralError::create("Block::setFloat8Py",
2248 "Failed to extract value for %s.",
2249 var->name_.c_str()));
2250
2251 setFloat8(tmp, var, index);
2252 }
2253}
2254
2255// Get data using float8
2256bp::object rim::Block::getFloat8Py(rim::Variable* var, int32_t index) {
2257 bp::object ret;
2258 uint32_t x;
2259
2260 // Unindexed with a list variable
2261 if (index < 0 && var->numValues_ > 0) {
2262 npy_intp dims[1] = {var->numValues_};
2263 PyObject* obj = PyArray_SimpleNew(1, dims, NPY_FLOAT);
2264 PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(obj);
2265 float* dst = reinterpret_cast<float*>(PyArray_DATA(arr));
2266
2267 for (x = 0; x < var->numValues_; x++) dst[x] = getFloat8(var, x);
2268
2269 boost::python::handle<> handle(obj);
2270 ret = bp::object(handle);
2271
2272 } else {
2273 PyObject* val = Py_BuildValue("f", getFloat8(var, index));
2274
2275 if (val == NULL) throw(rogue::GeneralError::create("Block::getFloat8Py", "Failed to generate Float8"));
2276
2277 bp::handle<> handle(val);
2278 ret = bp::object(handle);
2279 }
2280 return ret;
2281}
2282
2283#endif
2284
2285// Set data using float8
2286void rim::Block::setFloat8(const float& val, rim::Variable* var, int32_t index) {
2287 // Check range
2288 if ((var->minValue_ != 0 || var->maxValue_ != 0) && (val > var->maxValue_ || val < var->minValue_))
2289 throw(rogue::GeneralError::create("Block::setFloat8",
2290 "Value range error for %s. Value=%f, Min=%f, Max=%f",
2291 var->name_.c_str(),
2292 val,
2293 var->minValue_,
2294 var->maxValue_));
2295
2296 // Convert float to E4M3 and store as 1 byte
2297 uint8_t f8 = floatToFloat8(val);
2298 setBytes(reinterpret_cast<uint8_t*>(&f8), var, index);
2299}
2300
2301// Get data using float8
2302float rim::Block::getFloat8(rim::Variable* var, int32_t index) {
2303 uint8_t tmp = 0;
2304
2305 getBytes(reinterpret_cast<uint8_t*>(&tmp), var, index);
2306
2307 return float8ToFloat(tmp);
2308}
2309
2311// BFloat16 (Brain Float 16)
2313
2314#ifndef NO_PYTHON
2315
2316// Set data using bfloat16
2317void rim::Block::setBFloat16Py(bp::object& value, rim::Variable* var, int32_t index) {
2318 uint32_t x;
2319
2320 if (index == -1) index = 0;
2321
2322 // Passed value is a numpy value
2323 if (PyArray_Check(value.ptr())) {
2324 // Cast to an array object and check that the numpy array
2325 PyArrayObject* arr = reinterpret_cast<decltype(arr)>(value.ptr());
2326 npy_intp ndims = PyArray_NDIM(arr);
2327 npy_intp* dims = PyArray_SHAPE(arr);
2328 npy_intp* strides = PyArray_STRIDES(arr);
2329
2330 if (ndims != 1)
2331 throw(rogue::GeneralError::create("Block::setBFloat16Py",
2332 "Invalid number of dimensions (%" PRIu32 ") for passed ndarray for %s",
2333 ndims,
2334 var->name_.c_str()));
2335
2336 if ((index + dims[0]) > var->numValues_)
2337 throw(rogue::GeneralError::create("Block::setBFloat16Py",
2338 "Overflow error for passed array with length %" PRIu32
2339 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
2340 dims[0],
2341 index,
2342 var->numValues_,
2343 var->name_.c_str()));
2344
2345 if (PyArray_TYPE(arr) == NPY_FLOAT) {
2346 float* src = reinterpret_cast<float*>(PyArray_DATA(arr));
2347 npy_intp stride = strides[0] / sizeof(float);
2348 for (x = 0; x < dims[0]; x++) {
2349 float val = src[x * stride];
2350 setBFloat16(val, var, index + x);
2351 }
2352 } else {
2353 throw(rogue::GeneralError::create("Block::setBFloat16Py",
2354 "Passed nparray is not of type (float32) for %s",
2355 var->name_.c_str()));
2356 }
2357
2358 // Is passed value a list
2359 } else if (PyList_Check(value.ptr())) {
2360 bp::list vl = bp::extract<bp::list>(value);
2361 uint32_t vlen = len(vl);
2362
2363 if ((index + vlen) > var->numValues_)
2364 throw(rogue::GeneralError::create("Block::setBFloat16Py",
2365 "Overflow error for passed array with length %" PRIu32
2366 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
2367 vlen,
2368 index,
2369 var->numValues_,
2370 var->name_.c_str()));
2371
2372 for (x = 0; x < vlen; x++) {
2373 bp::extract<float> tmp(vl[x]);
2374
2375 if (!tmp.check())
2376 throw(rogue::GeneralError::create("Block::setBFloat16Py",
2377 "Failed to extract value for %s.",
2378 var->name_.c_str()));
2379
2380 setBFloat16(tmp, var, index + x);
2381 }
2382
2383 } else {
2384 bp::extract<float> tmp(value);
2385
2386 if (!tmp.check())
2387 throw(rogue::GeneralError::create("Block::setBFloat16Py",
2388 "Failed to extract value for %s.",
2389 var->name_.c_str()));
2390
2391 setBFloat16(tmp, var, index);
2392 }
2393}
2394
2395// Get data using bfloat16
2396bp::object rim::Block::getBFloat16Py(rim::Variable* var, int32_t index) {
2397 bp::object ret;
2398 uint32_t x;
2399
2400 // Unindexed with a list variable
2401 if (index < 0 && var->numValues_ > 0) {
2402 npy_intp dims[1] = {var->numValues_};
2403 PyObject* obj = PyArray_SimpleNew(1, dims, NPY_FLOAT);
2404 PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(obj);
2405 float* dst = reinterpret_cast<float*>(PyArray_DATA(arr));
2406
2407 for (x = 0; x < var->numValues_; x++) dst[x] = getBFloat16(var, x);
2408
2409 boost::python::handle<> handle(obj);
2410 ret = bp::object(handle);
2411
2412 } else {
2413 PyObject* val = Py_BuildValue("f", getBFloat16(var, index));
2414
2415 if (val == NULL) throw(rogue::GeneralError::create("Block::getBFloat16Py", "Failed to generate BFloat16"));
2416
2417 bp::handle<> handle(val);
2418 ret = bp::object(handle);
2419 }
2420 return ret;
2421}
2422
2423#endif
2424
2425// Set data using bfloat16
2426void rim::Block::setBFloat16(const float& val, rim::Variable* var, int32_t index) {
2427 // Check range
2428 if ((var->minValue_ != 0 || var->maxValue_ != 0) && (val > var->maxValue_ || val < var->minValue_))
2429 throw(rogue::GeneralError::create("Block::setBFloat16",
2430 "Value range error for %s. Value=%f, Min=%f, Max=%f",
2431 var->name_.c_str(),
2432 val,
2433 var->minValue_,
2434 var->maxValue_));
2435
2436 // Convert float to BFloat16 and store as 2 bytes
2437 uint16_t bf16 = floatToBFloat16(val);
2438 setBytes(reinterpret_cast<uint8_t*>(&bf16), var, index);
2439}
2440
2441// Get data using bfloat16
2442float rim::Block::getBFloat16(rim::Variable* var, int32_t index) {
2443 uint16_t tmp = 0;
2444
2445 getBytes(reinterpret_cast<uint8_t*>(&tmp), var, index);
2446
2447 return bfloat16ToFloat(tmp);
2448}
2449
2451// TensorFloat32 (NVIDIA TF32)
2453
2454#ifndef NO_PYTHON
2455
2456// Set data using TensorFloat32
2457void rim::Block::setTensorFloat32Py(bp::object& value, rim::Variable* var, int32_t index) {
2458 uint32_t x;
2459
2460 if (index == -1) index = 0;
2461
2462 // Passed value is a numpy value
2463 if (PyArray_Check(value.ptr())) {
2464 // Cast to an array object and check that the numpy array
2465 PyArrayObject* arr = reinterpret_cast<decltype(arr)>(value.ptr());
2466 npy_intp ndims = PyArray_NDIM(arr);
2467 npy_intp* dims = PyArray_SHAPE(arr);
2468 npy_intp* strides = PyArray_STRIDES(arr);
2469
2470 if (ndims != 1)
2471 throw(rogue::GeneralError::create("Block::setTensorFloat32Py",
2472 "Invalid number of dimensions (%" PRIu32 ") for passed ndarray for %s",
2473 ndims,
2474 var->name_.c_str()));
2475
2476 if ((index + dims[0]) > var->numValues_)
2477 throw(rogue::GeneralError::create("Block::setTensorFloat32Py",
2478 "Overflow error for passed array with length %" PRIu32
2479 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
2480 dims[0],
2481 index,
2482 var->numValues_,
2483 var->name_.c_str()));
2484
2485 if (PyArray_TYPE(arr) == NPY_FLOAT) {
2486 float* src = reinterpret_cast<float*>(PyArray_DATA(arr));
2487 npy_intp stride = strides[0] / sizeof(float);
2488 for (x = 0; x < dims[0]; x++) {
2489 float val = src[x * stride];
2490 setTensorFloat32(val, var, index + x);
2491 }
2492 } else {
2493 throw(rogue::GeneralError::create("Block::setTensorFloat32Py",
2494 "Passed nparray is not of type (float32) for %s",
2495 var->name_.c_str()));
2496 }
2497
2498 // Is passed value a list
2499 } else if (PyList_Check(value.ptr())) {
2500 bp::list vl = bp::extract<bp::list>(value);
2501 uint32_t vlen = len(vl);
2502
2503 if ((index + vlen) > var->numValues_)
2504 throw(rogue::GeneralError::create("Block::setTensorFloat32Py",
2505 "Overflow error for passed array with length %" PRIu32
2506 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
2507 vlen,
2508 index,
2509 var->numValues_,
2510 var->name_.c_str()));
2511
2512 for (x = 0; x < vlen; x++) {
2513 bp::extract<float> tmp(vl[x]);
2514
2515 if (!tmp.check())
2516 throw(rogue::GeneralError::create("Block::setTensorFloat32Py",
2517 "Failed to extract value for %s.",
2518 var->name_.c_str()));
2519
2520 setTensorFloat32(tmp, var, index + x);
2521 }
2522
2523 } else {
2524 bp::extract<float> tmp(value);
2525
2526 if (!tmp.check())
2527 throw(rogue::GeneralError::create("Block::setTensorFloat32Py",
2528 "Failed to extract value for %s.",
2529 var->name_.c_str()));
2530
2531 setTensorFloat32(tmp, var, index);
2532 }
2533}
2534
2535// Get data using TensorFloat32
2536bp::object rim::Block::getTensorFloat32Py(rim::Variable* var, int32_t index) {
2537 bp::object ret;
2538 uint32_t x;
2539
2540 // Unindexed with a list variable
2541 if (index < 0 && var->numValues_ > 0) {
2542 npy_intp dims[1] = {var->numValues_};
2543 PyObject* obj = PyArray_SimpleNew(1, dims, NPY_FLOAT);
2544 PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(obj);
2545 float* dst = reinterpret_cast<float*>(PyArray_DATA(arr));
2546
2547 for (x = 0; x < var->numValues_; x++) dst[x] = getTensorFloat32(var, x);
2548
2549 boost::python::handle<> handle(obj);
2550 ret = bp::object(handle);
2551
2552 } else {
2553 PyObject* val = Py_BuildValue("f", getTensorFloat32(var, index));
2554
2555 if (val == NULL) throw(rogue::GeneralError::create("Block::getTensorFloat32Py", "Failed to generate TensorFloat32"));
2556
2557 bp::handle<> handle(val);
2558 ret = bp::object(handle);
2559 }
2560 return ret;
2561}
2562
2563#endif
2564
2565// Set data using TensorFloat32
2566void rim::Block::setTensorFloat32(const float& val, rim::Variable* var, int32_t index) {
2567 // Check range
2568 if ((var->minValue_ != 0 || var->maxValue_ != 0) && (val > var->maxValue_ || val < var->minValue_))
2569 throw(rogue::GeneralError::create("Block::setTensorFloat32",
2570 "Value range error for %s. Value=%f, Min=%f, Max=%f",
2571 var->name_.c_str(),
2572 val,
2573 var->minValue_,
2574 var->maxValue_));
2575
2576 // Convert float to TensorFloat32 and store as 4 bytes
2577 uint32_t tf32 = floatToTensorFloat32(val);
2578 setBytes(reinterpret_cast<uint8_t*>(&tf32), var, index);
2579}
2580
2581// Get data using TensorFloat32
2582float rim::Block::getTensorFloat32(rim::Variable* var, int32_t index) {
2583 uint32_t tmp = 0;
2584
2585 getBytes(reinterpret_cast<uint8_t*>(&tmp), var, index);
2586
2587 return tensorFloat32ToFloat(tmp);
2588}
2589
2591// Float6 (E3M2)
2593
2594#ifndef NO_PYTHON
2595
2596// Set data using float6
2597void rim::Block::setFloat6Py(bp::object& value, rim::Variable* var, int32_t index) {
2598 uint32_t x;
2599
2600 if (index == -1) index = 0;
2601
2602 // Passed value is a numpy value
2603 if (PyArray_Check(value.ptr())) {
2604 // Cast to an array object and check that the numpy array
2605 PyArrayObject* arr = reinterpret_cast<decltype(arr)>(value.ptr());
2606 npy_intp ndims = PyArray_NDIM(arr);
2607 npy_intp* dims = PyArray_SHAPE(arr);
2608 npy_intp* strides = PyArray_STRIDES(arr);
2609
2610 if (ndims != 1)
2611 throw(rogue::GeneralError::create("Block::setFloat6Py",
2612 "Invalid number of dimensions (%" PRIu32 ") for passed ndarray for %s",
2613 ndims,
2614 var->name_.c_str()));
2615
2616 if ((index + dims[0]) > var->numValues_)
2617 throw(rogue::GeneralError::create("Block::setFloat6Py",
2618 "Overflow error for passed array with length %" PRIu32
2619 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
2620 dims[0],
2621 index,
2622 var->numValues_,
2623 var->name_.c_str()));
2624
2625 if (PyArray_TYPE(arr) == NPY_FLOAT) {
2626 float* src = reinterpret_cast<float*>(PyArray_DATA(arr));
2627 npy_intp stride = strides[0] / sizeof(float);
2628 for (x = 0; x < dims[0]; x++) {
2629 float val = src[x * stride];
2630 setFloat6(val, var, index + x);
2631 }
2632 } else {
2633 throw(rogue::GeneralError::create("Block::setFloat6Py",
2634 "Passed nparray is not of type (float32) for %s",
2635 var->name_.c_str()));
2636 }
2637
2638 // Is passed value a list
2639 } else if (PyList_Check(value.ptr())) {
2640 bp::list vl = bp::extract<bp::list>(value);
2641 uint32_t vlen = len(vl);
2642
2643 if ((index + vlen) > var->numValues_)
2644 throw(rogue::GeneralError::create("Block::setFloat6Py",
2645 "Overflow error for passed array with length %" PRIu32
2646 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
2647 vlen,
2648 index,
2649 var->numValues_,
2650 var->name_.c_str()));
2651
2652 for (x = 0; x < vlen; x++) {
2653 bp::extract<float> tmp(vl[x]);
2654
2655 if (!tmp.check())
2656 throw(rogue::GeneralError::create("Block::setFloat6Py",
2657 "Failed to extract value for %s.",
2658 var->name_.c_str()));
2659
2660 setFloat6(tmp, var, index + x);
2661 }
2662
2663 } else {
2664 bp::extract<float> tmp(value);
2665
2666 if (!tmp.check())
2667 throw(rogue::GeneralError::create("Block::setFloat6Py",
2668 "Failed to extract value for %s.",
2669 var->name_.c_str()));
2670
2671 setFloat6(tmp, var, index);
2672 }
2673}
2674
2675// Get data using float6
2676bp::object rim::Block::getFloat6Py(rim::Variable* var, int32_t index) {
2677 bp::object ret;
2678 uint32_t x;
2679
2680 // Unindexed with a list variable
2681 if (index < 0 && var->numValues_ > 0) {
2682 npy_intp dims[1] = {var->numValues_};
2683 PyObject* obj = PyArray_SimpleNew(1, dims, NPY_FLOAT);
2684 PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(obj);
2685 float* dst = reinterpret_cast<float*>(PyArray_DATA(arr));
2686
2687 for (x = 0; x < var->numValues_; x++) dst[x] = getFloat6(var, x);
2688
2689 boost::python::handle<> handle(obj);
2690 ret = bp::object(handle);
2691
2692 } else {
2693 PyObject* val = Py_BuildValue("f", getFloat6(var, index));
2694
2695 if (val == NULL) throw(rogue::GeneralError::create("Block::getFloat6Py", "Failed to generate Float6"));
2696
2697 bp::handle<> handle(val);
2698 ret = bp::object(handle);
2699 }
2700 return ret;
2701}
2702
2703#endif
2704
2705// Set data using float6
2706void rim::Block::setFloat6(const float& val, rim::Variable* var, int32_t index) {
2707 // Check range
2708 if ((var->minValue_ != 0 || var->maxValue_ != 0) && (val > var->maxValue_ || val < var->minValue_))
2709 throw(rogue::GeneralError::create("Block::setFloat6",
2710 "Value range error for %s. Value=%f, Min=%f, Max=%f",
2711 var->name_.c_str(),
2712 val,
2713 var->minValue_,
2714 var->maxValue_));
2715
2716 // Convert float to E3M2 and store as 1 byte
2717 uint8_t f6 = floatToFloat6(val);
2718 setBytes(reinterpret_cast<uint8_t*>(&f6), var, index);
2719}
2720
2721// Get data using float6
2722float rim::Block::getFloat6(rim::Variable* var, int32_t index) {
2723 uint8_t tmp = 0;
2724
2725 getBytes(reinterpret_cast<uint8_t*>(&tmp), var, index);
2726
2727 return float6ToFloat(tmp);
2728}
2729
2731// Float4 (E2M1)
2733
2734#ifndef NO_PYTHON
2735
2736// Set data using float4
2737void rim::Block::setFloat4Py(bp::object& value, rim::Variable* var, int32_t index) {
2738 uint32_t x;
2739
2740 if (index == -1) index = 0;
2741
2742 // Passed value is a numpy value
2743 if (PyArray_Check(value.ptr())) {
2744 // Cast to an array object and check that the numpy array
2745 PyArrayObject* arr = reinterpret_cast<decltype(arr)>(value.ptr());
2746 npy_intp ndims = PyArray_NDIM(arr);
2747 npy_intp* dims = PyArray_SHAPE(arr);
2748 npy_intp* strides = PyArray_STRIDES(arr);
2749
2750 if (ndims != 1)
2751 throw(rogue::GeneralError::create("Block::setFloat4Py",
2752 "Invalid number of dimensions (%" PRIu32 ") for passed ndarray for %s",
2753 ndims,
2754 var->name_.c_str()));
2755
2756 if ((index + dims[0]) > var->numValues_)
2757 throw(rogue::GeneralError::create("Block::setFloat4Py",
2758 "Overflow error for passed array with length %" PRIu32
2759 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
2760 dims[0],
2761 index,
2762 var->numValues_,
2763 var->name_.c_str()));
2764
2765 if (PyArray_TYPE(arr) == NPY_FLOAT) {
2766 float* src = reinterpret_cast<float*>(PyArray_DATA(arr));
2767 npy_intp stride = strides[0] / sizeof(float);
2768 for (x = 0; x < dims[0]; x++) {
2769 float val = src[x * stride];
2770 setFloat4(val, var, index + x);
2771 }
2772 } else {
2773 throw(rogue::GeneralError::create("Block::setFloat4Py",
2774 "Passed nparray is not of type (float32) for %s",
2775 var->name_.c_str()));
2776 }
2777
2778 // Is passed value a list
2779 } else if (PyList_Check(value.ptr())) {
2780 bp::list vl = bp::extract<bp::list>(value);
2781 uint32_t vlen = len(vl);
2782
2783 if ((index + vlen) > var->numValues_)
2784 throw(rogue::GeneralError::create("Block::setFloat4Py",
2785 "Overflow error for passed array with length %" PRIu32
2786 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
2787 vlen,
2788 index,
2789 var->numValues_,
2790 var->name_.c_str()));
2791
2792 for (x = 0; x < vlen; x++) {
2793 bp::extract<float> tmp(vl[x]);
2794
2795 if (!tmp.check())
2796 throw(rogue::GeneralError::create("Block::setFloat4Py",
2797 "Failed to extract value for %s.",
2798 var->name_.c_str()));
2799
2800 setFloat4(tmp, var, index + x);
2801 }
2802
2803 } else {
2804 bp::extract<float> tmp(value);
2805
2806 if (!tmp.check())
2807 throw(rogue::GeneralError::create("Block::setFloat4Py",
2808 "Failed to extract value for %s.",
2809 var->name_.c_str()));
2810
2811 setFloat4(tmp, var, index);
2812 }
2813}
2814
2815// Get data using float4
2816bp::object rim::Block::getFloat4Py(rim::Variable* var, int32_t index) {
2817 bp::object ret;
2818 uint32_t x;
2819
2820 // Unindexed with a list variable
2821 if (index < 0 && var->numValues_ > 0) {
2822 npy_intp dims[1] = {var->numValues_};
2823 PyObject* obj = PyArray_SimpleNew(1, dims, NPY_FLOAT);
2824 PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(obj);
2825 float* dst = reinterpret_cast<float*>(PyArray_DATA(arr));
2826
2827 for (x = 0; x < var->numValues_; x++) dst[x] = getFloat4(var, x);
2828
2829 boost::python::handle<> handle(obj);
2830 ret = bp::object(handle);
2831
2832 } else {
2833 PyObject* val = Py_BuildValue("f", getFloat4(var, index));
2834
2835 if (val == NULL) throw(rogue::GeneralError::create("Block::getFloat4Py", "Failed to generate Float4"));
2836
2837 bp::handle<> handle(val);
2838 ret = bp::object(handle);
2839 }
2840 return ret;
2841}
2842
2843#endif
2844
2845// Set data using float4
2846void rim::Block::setFloat4(const float& val, rim::Variable* var, int32_t index) {
2847 // Check range
2848 if ((var->minValue_ != 0 || var->maxValue_ != 0) && (val > var->maxValue_ || val < var->minValue_))
2849 throw(rogue::GeneralError::create("Block::setFloat4",
2850 "Value range error for %s. Value=%f, Min=%f, Max=%f",
2851 var->name_.c_str(),
2852 val,
2853 var->minValue_,
2854 var->maxValue_));
2855
2856 // Convert float to E2M1 and store as 1 byte
2857 uint8_t f4 = floatToFloat4(val);
2858 setBytes(reinterpret_cast<uint8_t*>(&f4), var, index);
2859}
2860
2861// Get data using float4
2862float rim::Block::getFloat4(rim::Variable* var, int32_t index) {
2863 uint8_t tmp = 0;
2864
2865 getBytes(reinterpret_cast<uint8_t*>(&tmp), var, index);
2866
2867 return float4ToFloat(tmp);
2868}
2869
2871// Double
2873
2874#ifndef NO_PYTHON
2875
2876// Set data using double
2877void rim::Block::setDoublePy(bp::object& value, rim::Variable* var, int32_t index) {
2878 uint32_t x;
2879
2880 if (index == -1) index = 0;
2881
2882 // Passed value is a numpy value
2883 if (PyArray_Check(value.ptr())) {
2884 // Cast to an array object and check that the numpy array
2885 PyArrayObject* arr = reinterpret_cast<decltype(arr)>(value.ptr());
2886 npy_intp ndims = PyArray_NDIM(arr);
2887 npy_intp* dims = PyArray_SHAPE(arr);
2888 npy_intp* strides = PyArray_STRIDES(arr);
2889
2890 if (ndims != 1)
2891 throw(rogue::GeneralError::create("Block::setDoublePy",
2892 "Invalid number of dimensions (%" PRIu32 ") for passed ndarray for %s",
2893 ndims,
2894 var->name_.c_str()));
2895
2896 if ((index + dims[0]) > var->numValues_)
2897 throw(rogue::GeneralError::create("Block::setDoublePy",
2898 "Overflow error for passed array with length %" PRIu32
2899 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
2900 dims[0],
2901 index,
2902 var->numValues_,
2903 var->name_.c_str()));
2904
2905 if (PyArray_TYPE(arr) == NPY_FLOAT64) {
2906 double* src = reinterpret_cast<double*>(PyArray_DATA(arr));
2907 npy_intp stride = strides[0] / sizeof(double);
2908 for (x = 0; x < dims[0]; x++) {
2909 setDouble(src[x * stride], var, index + x);
2910 }
2911 } else {
2912 throw(rogue::GeneralError::create("Block::setFLoatPy",
2913 "Passed nparray is not of type (double) for %s",
2914 var->name_.c_str()));
2915 }
2916
2917 // Is passed value a list
2918 } else if (PyList_Check(value.ptr())) {
2919 bp::list vl = bp::extract<bp::list>(value);
2920 uint32_t vlen = len(vl);
2921
2922 if ((index + vlen) > var->numValues_)
2923 throw(rogue::GeneralError::create("Block::setDoublePy",
2924 "Overflow error for passed array with length %" PRIu32
2925 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
2926 vlen,
2927 index,
2928 var->numValues_,
2929 var->name_.c_str()));
2930
2931 for (x = 0; x < vlen; x++) {
2932 bp::extract<double> tmp(vl[x]);
2933
2934 if (!tmp.check())
2935 throw(rogue::GeneralError::create("Block::setDoublePy",
2936 "Failed to extract value for %s.",
2937 var->name_.c_str()));
2938
2939 setDouble(tmp, var, index + x);
2940 }
2941
2942 // Passed scalar numpy value
2943 } else if (PyArray_CheckScalar(value.ptr())) {
2944 if (PyArray_DescrFromScalar(value.ptr())->type_num == NPY_FLOAT64) {
2945 double val;
2946 PyArray_ScalarAsCtype(value.ptr(), &val);
2947 setDouble(val, var, index);
2948 } else {
2949 throw(rogue::GeneralError::create("Block::setDoublePy",
2950 "Failed to extract value for %s.",
2951 var->name_.c_str()));
2952 }
2953 } else {
2954 bp::extract<double> tmp(value);
2955
2956 if (!tmp.check())
2957 throw(rogue::GeneralError::create("Block::setDoublePy",
2958 "Failed to extract value for %s.",
2959 var->name_.c_str()));
2960
2961 setDouble(tmp, var, index);
2962 }
2963}
2964
2965// Get data using double
2966bp::object rim::Block::getDoublePy(rim::Variable* var, int32_t index) {
2967 bp::object ret;
2968 uint32_t x;
2969
2970 // Unindexed with a list variable
2971 if (index < 0 && var->numValues_ > 0) {
2972 // Create a numpy array to receive it and locate the destination data buffer
2973 npy_intp dims[1] = {var->numValues_};
2974 PyObject* obj = PyArray_SimpleNew(1, dims, NPY_FLOAT64);
2975 PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(obj);
2976 double* dst = reinterpret_cast<double*>(PyArray_DATA(arr));
2977
2978 for (x = 0; x < var->numValues_; x++) dst[x] = getDouble(var, x);
2979
2980 boost::python::handle<> handle(obj);
2981 ret = bp::object(handle);
2982
2983 } else {
2984 PyObject* val = Py_BuildValue("d", getDouble(var, index));
2985
2986 if (val == NULL) throw(rogue::GeneralError::create("Block::getDoublePy", "Failed to generate Double"));
2987
2988 bp::handle<> handle(val);
2989 ret = bp::object(handle);
2990 }
2991 return ret;
2992}
2993
2994#endif
2995
2996// Set data using double
2997void rim::Block::setDouble(const double& val, rim::Variable* var, int32_t index) {
2998 // Check range
2999 if ((var->minValue_ != 0 || var->maxValue_ != 0) && (val > var->maxValue_ || val < var->minValue_))
3000 throw(rogue::GeneralError::create("Block::setDouble",
3001 "Value range error for %s. Value=%f, Min=%f, Max=%f",
3002 var->name_.c_str(),
3003 val,
3004 var->minValue_,
3005 var->maxValue_));
3006
3007 setBytes(reinterpret_cast<uint8_t*>(const_cast<double*>(&val)), var, index);
3008}
3009
3010// Get data using double
3011double rim::Block::getDouble(rim::Variable* var, int32_t index) {
3012 double tmp = 0;
3013
3014 getBytes(reinterpret_cast<uint8_t*>(&tmp), var, index);
3015
3016 return tmp;
3017}
3018
3020// Fixed Point
3022
3023#ifndef NO_PYTHON
3024
3025// Set data using fixed point
3026void rim::Block::setFixedPy(bp::object& value, rim::Variable* var, int32_t index) {
3027 uint32_t x;
3028
3029 // Dispatch to the signed or unsigned fixed-point scalar setter based on modelId
3030 auto setScalar = [this, var](const double& v, int32_t idx) {
3031 if (var->modelId_ == rim::UFixed)
3032 setUFixed(v, var, idx);
3033 else
3034 setFixed(v, var, idx);
3035 };
3036
3037 if (index == -1) index = 0;
3038
3039 // Passed value is a numpy value
3040 if (PyArray_Check(value.ptr())) {
3041 // Cast to an array object and check that the numpy array
3042 PyArrayObject* arr = reinterpret_cast<decltype(arr)>(value.ptr());
3043 npy_intp ndims = PyArray_NDIM(arr);
3044 npy_intp* dims = PyArray_SHAPE(arr);
3045 npy_intp* strides = PyArray_STRIDES(arr);
3046
3047 if (ndims != 1)
3048 throw(rogue::GeneralError::create("Block::setFixedPy",
3049 "Invalid number of dimensions (%" PRIu32 ") for passed ndarray for %s",
3050 ndims,
3051 var->name_.c_str()));
3052
3053 if ((index + dims[0]) > var->numValues_)
3054 throw(rogue::GeneralError::create("Block::setFixedPy",
3055 "Overflow error for passed array with length %" PRIu32
3056 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
3057 dims[0],
3058 index,
3059 var->numValues_,
3060 var->name_.c_str()));
3061
3062 if (PyArray_TYPE(arr) == NPY_FLOAT64) {
3063 double* src = reinterpret_cast<double*>(PyArray_DATA(arr));
3064 npy_intp stride = strides[0] / sizeof(double);
3065 for (x = 0; x < dims[0]; x++) {
3066 setScalar(src[x * stride], index + x);
3067 }
3068 } else {
3069 throw(rogue::GeneralError::create("Block::setFixedPy",
3070 "Passed nparray is not of type (double) for %s",
3071 var->name_.c_str()));
3072 }
3073
3074 // Is passed value a list
3075 } else if (PyList_Check(value.ptr())) {
3076 bp::list vl = bp::extract<bp::list>(value);
3077 uint32_t vlen = len(vl);
3078
3079 if ((index + vlen) > var->numValues_)
3080 throw(rogue::GeneralError::create("Block::setFixedPy",
3081 "Overflow error for passed array with length %" PRIu32
3082 " at index %" PRIi32 ". Variable length = %" PRIu32 " for %s",
3083 vlen,
3084 index,
3085 var->numValues_,
3086 var->name_.c_str()));
3087
3088 for (x = 0; x < vlen; x++) {
3089 bp::extract<double> tmp(vl[x]);
3090
3091 if (!tmp.check())
3092 throw(rogue::GeneralError::create("Block::setFixedPy",
3093 "Failed to extract value for %s.",
3094 var->name_.c_str()));
3095
3096 setScalar(tmp, index + x);
3097 }
3098
3099 // Passed scalar numpy value
3100 } else if (PyArray_CheckScalar(value.ptr())) {
3101 if (PyArray_DescrFromScalar(value.ptr())->type_num == NPY_FLOAT64) {
3102 double val;
3103 PyArray_ScalarAsCtype(value.ptr(), &val);
3104 setScalar(val, index);
3105 } else {
3106 throw(rogue::GeneralError::create("Block::setFixedPy",
3107 "Failed to extract value for %s.",
3108 var->name_.c_str()));
3109 }
3110 } else {
3111 bp::extract<double> tmp(value);
3112
3113 if (!tmp.check())
3114 throw(rogue::GeneralError::create("Block::setFixedPy",
3115 "Failed to extract value for %s.",
3116 var->name_.c_str()));
3117
3118 setScalar(tmp, index);
3119 }
3120}
3121
3122// Get data using fixed point
3123bp::object rim::Block::getFixedPy(rim::Variable* var, int32_t index) {
3124 bp::object ret;
3125 uint32_t x;
3126
3127 // Dispatch to the signed or unsigned fixed-point scalar getter based on modelId
3128 auto getScalar = [this, var](int32_t idx) -> double {
3129 return (var->modelId_ == rim::UFixed) ? getUFixed(var, idx) : getFixed(var, idx);
3130 };
3131
3132 // Unindexed with a list variable
3133 if (index < 0 && var->numValues_ > 0) {
3134 // Create a numpy array to receive it and locate the destination data buffer
3135 npy_intp dims[1] = {var->numValues_};
3136 PyObject* obj = PyArray_SimpleNew(1, dims, NPY_FLOAT64);
3137 PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(obj);
3138 double* dst = reinterpret_cast<double*>(PyArray_DATA(arr));
3139
3140 for (x = 0; x < var->numValues_; x++) dst[x] = getScalar(x);
3141
3142 boost::python::handle<> handle(obj);
3143 ret = bp::object(handle);
3144
3145 } else {
3146 PyObject* val = Py_BuildValue("d", getScalar(index));
3147
3148 if (val == NULL) throw(rogue::GeneralError::create("Block::getFixedPy", "Failed to generate Fixed"));
3149
3150 bp::handle<> handle(val);
3151 ret = bp::object(handle);
3152 }
3153 return ret;
3154}
3155
3156#endif
3157
3158// Set data using fixed point
3159void rim::Block::setFixed(const double& val, rim::Variable* var, int32_t index) {
3160 // Check range
3161 if ((var->minValue_ != 0 || var->maxValue_ != 0) && (val > var->maxValue_ || val < var->minValue_))
3162 throw(rogue::GeneralError::create("Block::setFixed",
3163 "Value range error for %s. Value=%f, Min=%f, Max=%f",
3164 var->name_.c_str(),
3165 val,
3166 var->minValue_,
3167 var->maxValue_));
3168
3169 // Convert
3170 int64_t fPoint = static_cast<int64_t>(round(val * pow(2, var->binPoint_)));
3171
3172 // Compute representable range in integer domain (use 1ULL to avoid UB for 64-bit widths)
3173 int64_t maxInt = static_cast<int64_t>((1ULL << (var->valueBits_ - 1)) - 1);
3174 int64_t minInt = -maxInt - 1;
3175
3176 // Check for overflow (rounding may push value beyond representable range)
3177 if (fPoint > maxInt || fPoint < minInt)
3178 throw(rogue::GeneralError::create("Block::setFixed",
3179 "Fixed point overflow for %s. Value=%f, Min=%f, Max=%f",
3180 var->name_.c_str(),
3181 val,
3182 static_cast<double>(minInt) / pow(2, var->binPoint_),
3183 static_cast<double>(maxInt) / pow(2, var->binPoint_)));
3184
3185 setBytes(reinterpret_cast<uint8_t*>(&fPoint), var, index);
3186}
3187
3188// Get data using fixed point
3189double rim::Block::getFixed(rim::Variable* var, int32_t index) {
3190 int64_t fPoint = 0;
3191 double tmp;
3192
3193 getBytes(reinterpret_cast<uint8_t*>(&fPoint), var, index);
3194 // Sign-extend from valueBits_ to 64 bits. Use 1LL to avoid shifting a
3195 // plain int by >= 32 (UB) and guard the valueBits_ == 64 case, where
3196 // fPoint already holds the signed two's-complement value directly.
3197 if (var->valueBits_ < 64) {
3198 const int64_t signBit = 1LL << (var->valueBits_ - 1);
3199 const int64_t modulus = 1LL << var->valueBits_;
3200 if ((fPoint & signBit) != 0) fPoint -= modulus;
3201 }
3202
3203 // Convert to float
3204 tmp = static_cast<double>(fPoint);
3205 tmp = tmp / pow(2, var->binPoint_);
3206 return tmp;
3207}
3208
3209// Set data using unsigned fixed point
3210void rim::Block::setUFixed(const double& val, rim::Variable* var, int32_t index) {
3211 // Check range
3212 if ((var->minValue_ != 0 || var->maxValue_ != 0) && (val > var->maxValue_ || val < var->minValue_))
3213 throw(rogue::GeneralError::create("Block::setUFixed",
3214 "Value range error for %s. Value=%f, Min=%f, Max=%f",
3215 var->name_.c_str(),
3216 val,
3217 var->minValue_,
3218 var->maxValue_));
3219
3220 // Convert (unsigned; reject negatives explicitly before the cast)
3221 if (val < 0)
3222 throw(rogue::GeneralError::create("Block::setUFixed",
3223 "Unsigned fixed-point underflow for %s. Value=%f",
3224 var->name_.c_str(),
3225 val));
3226
3227 uint64_t fPoint = static_cast<uint64_t>(round(val * pow(2, var->binPoint_)));
3228
3229 // Compute representable unsigned range (cap at 64 bits to avoid UB)
3230 uint64_t maxUInt =
3231 (var->valueBits_ >= 64) ? UINT64_MAX : ((1ULL << var->valueBits_) - 1ULL);
3232
3233 // Check for overflow (rounding may push value beyond representable range)
3234 if (fPoint > maxUInt)
3235 throw(rogue::GeneralError::create("Block::setUFixed",
3236 "Unsigned fixed-point overflow for %s. Value=%f, Min=0, Max=%f",
3237 var->name_.c_str(),
3238 val,
3239 static_cast<double>(maxUInt) / pow(2, var->binPoint_)));
3240
3241 setBytes(reinterpret_cast<uint8_t*>(&fPoint), var, index);
3242}
3243
3244// Get data using unsigned fixed point
3245double rim::Block::getUFixed(rim::Variable* var, int32_t index) {
3246 uint64_t fPoint = 0;
3247
3248 getBytes(reinterpret_cast<uint8_t*>(&fPoint), var, index);
3249 // No sign extension — treat stored bits as unsigned
3250 return static_cast<double>(fPoint) / pow(2, var->binPoint_);
3251}
3252
3254// Custom
3256
3257// Custom Init function called after addVariables
3258void rim::Block::customInit() {}
3259
3260// Custom Clean function called before delete
3261void rim::Block::customClean() {}
3262
3263void rim::Block::rateTest() {
3264 uint32_t x;
3265
3266 struct timeval stime;
3267 struct timeval etime;
3268 struct timeval dtime;
3269
3270 uint64_t count = 1000000;
3271 double durr;
3272 double rate;
3273 uint32_t value;
3274
3275 gettimeofday(&stime, NULL);
3276 waitTransaction(0);
3277 for (x = 0; x < count; ++x) {
3278 reqTransaction(0, 4, &value, rim::Read);
3279 waitTransaction(0);
3280 }
3281 gettimeofday(&etime, NULL);
3282
3283 timersub(&etime, &stime, &dtime);
3284 durr = dtime.tv_sec + static_cast<float>(dtime.tv_usec) / 1.0e6;
3285 rate = count / durr;
3286
3287 printf("\nBlock c++ raw: Read %" PRIu64 " times in %f seconds. Rate = %f\n", count, durr, rate);
3288
3289 gettimeofday(&stime, NULL);
3290 waitTransaction(0);
3291 for (x = 0; x < count; ++x) {
3292 reqTransaction(0, 4, reinterpret_cast<uint8_t*>(&count), rim::Write);
3293 waitTransaction(0);
3294 }
3295 gettimeofday(&etime, NULL);
3296
3297 timersub(&etime, &stime, &dtime);
3298 durr = dtime.tv_sec + static_cast<float>(dtime.tv_usec) / 1.0e6;
3299 rate = count / durr;
3300
3301 printf("\nBlock c++ raw: Wrote %" PRIu64 " times in %f seconds. Rate = %f\n", count, durr, rate);
3302}
Generic Rogue exception type.
char const * what() const
Returns exception text for standard exception handling.
static GeneralError create(std::string src, const char *fmt,...)
Creates a formatted error instance.
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
RAII helper that acquires the Python GIL for a scope.
Definition ScopedGil.h:35
uint32_t size()
Returns block size in bytes.
Definition Block.cpp:165
uint64_t offset()
Returns the local offset of this block.
Definition Block.cpp:155
Internal Boost.Python wrapper for rogue::interfaces::memory::Variable. Enables Python subclasses to o...
Definition Variable.h:1099
Memory variable descriptor and typed accessor facade.
Definition Variable.h:62
std::vector< uint32_t > bitSize_
Definition Variable.h:103
std::vector< uint32_t > bitOffset_
Definition Variable.h:100
std::shared_ptr< rogue::interfaces::memory::Block > BlockPtr
Shared pointer alias for Block.
Definition Block.h:1189
static const uint32_t Read
Memory read transaction type.
Definition Constants.h:36
static const uint32_t Write
Memory write transaction type.
Definition Constants.h:43
static const uint32_t Verify
Memory verify readback transaction type.
Definition Constants.h:57
static const uint8_t UFixed
Block access type for unsigned fixed-point numeric data.
Definition Constants.h:141
static const uint32_t Post
Memory posted write transaction type.
Definition Constants.h:50
std::shared_ptr< rogue::interfaces::memory::Variable > VariablePtr
Definition Variable.h:43