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