ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
2D Register Accessors

A TwoDRegisterAccessor behaves like a two-dimensional array, consisting of several sequences (or channels) which each have a number of samples.

It is implemented as a buffering accessor, so you do read() and write() to access the hardware, and in between you can modify the data in the accessors buffer efficiently at will.

The one-dimensional sequences/channels are implemeted as std::vector, so they are convenient to use. They are accessed via the [] operator of the accessor. This also allows the "matrix" syntax accessor[][].

Note
As with all std::vectors, iterators are slightly more efficient because the repeated calculation of the address and indirections are not necessary for each element.
// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
#include <ChimeraTK/Device.h>
#include <ChimeraTK/Utilities.h>
#include <iostream>
int main() {
ChimeraTK::setDMapFilePath("example.dmap");
ChimeraTK::Device myDevice("MY_DEVICE");
myDevice.open();
/*
* In this example there is a data region called "DATA" in
* a module called "ADC".
*/
ChimeraTK::TwoDRegisterAccessor<double> twoDAccessor = myDevice.getTwoDRegisterAccessor<double>("ADC/DATA");
/*
* Read data for all channels from the hardware
*/
twoDAccessor.read();
/*
* You can access each sequence/channel individually. They are std::vectors.
* You get a reference to the vector inside the accessor. No data copying.
*/
for(size_t i = 0; i < twoDAccessor.getNChannels(); ++i) {
std::cout << "Channel " << i << ":";
std::vector<double>& channel = twoDAccessor[i];
for(double sample : channel) {
std::cout << " " << sample;
}
std::cout << std::endl;
}
/*
* You can modify the stuff at will in the accessors internal buffer.
* In this example we use two [] operators like a 2D array.
*/
for(size_t i = 0; i < twoDAccessor.getNChannels(); ++i) {
for(size_t j = 0; j < twoDAccessor.getNElementsPerChannel(); ++j) {
twoDAccessor[i][j] = i * 100 + j;
}
}
/*
* Finally write to the hardware.
*/
twoDAccessor.write();
myDevice.close();
return 0;
}
int main()
Class allows to read/write registers from device.
Definition Device.h:39
bool write(ChimeraTK::VersionNumber versionNumber={})
Write the data to device.
void read()
Read the data from the device.
Accessor class to read and write 2D registers.
size_t getNElementsPerChannel() const
Return number of elements/samples per channel.
size_t getNChannels() const
Return the number of channels (formerly called sequences)
void setDMapFilePath(std::string dmapFilePath)
Set the location of the dmap file.

A special case: The 2D register might have multiplexed data on the backend side (backend specific implementation of the TwoDRegisterAccessor). This is commonly the case e.g. for PCIe backend devices. In the map file of a PCIe backend (or any other backend type based on the NumbericAddressedBackend) a special notation is used to define multiplexed 2D registers:

# name                              number_of_elements  address   size  bar  width  fracbits  signed
ADC.AREA_MULTIPLEXED_SEQUENCE_DATA                  13        0    132    2     32         0       0
ADC.SEQUENCE_DATA_0                                  1        0      2    2     16         0       1
ADC.SEQUENCE_DATA_1                                  1        2      2    2     16         0       1
ADC.SEQUENCE_DATA_2                                  1        4      4    2     20         0       1
ADC.SEQUENCE_DATA_3                                  1        8      2    2     16         0       1

These five lines in the map file will result in a single register named "ADC.DATA". It will have 4 sequences (or channels) with each 13 elements. The number of elements per channel is determined by the total size of the register (132 in this example) divided by the number of bytes per element summed for all channels (10 in this example), rounded down. The number of elements specified by the first line (13 in this case) is ignored, but it is convenient to write the correct number of elements per channel there. Please note that the total size of the register must be divisible by the size of the raw data type (which is fixed at 4 corrently), this is a general requirement by the NumbericAddressedBackend. The addresses of the SEQUENCE entries must be absolute addresses of each first element of the sequence in the BAR.

The following code demonstrates how the (de)multiplexing internally works:

// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
// SPDX-License-Identifier: LGPL-3.0-or-later
#include <ChimeraTK/Device.h>
#include <ChimeraTK/Utilities.h>
#include <iostream>
int main() {
ChimeraTK::setDMapFilePath("example.dmap");
ChimeraTK::Device myDevice("MY_DEVICE");
myDevice.open();
/*
* We populate the memory region with multiple multiplexed sequences
* so that we can use this for demonstrating the demultiplexing of the
* TwoDRegisterAccessor (for some implementations depeding on the backend).
*
* In this example we only have 4 sequences with 4 samples each.
* We write numbers 0 to 15 as multiplexed data and expect the following
* result: sequence 0: 0 4 8 12 sequence 1: 1 5 9 13 sequence 2:
* 2 6 10 14 sequence 3: 3 7 11 15
*
* We use a register named AREA_DATA_RAW which provides plain access to the
* data region.
*/
auto dataRegion = myDevice.getOneDRegisterAccessor<double>("ADC/AREA_DATA_RAW");
int counter = 0;
for(auto& dataWord : dataRegion) {
dataWord = counter++;
}
dataRegion.write();
/*
* Now check how it looks using the TwoDRegisterAccessor. We just copy it from
* the accessor2D.cpp example.
*/
ChimeraTK::TwoDRegisterAccessor<double> twoDAccessor = myDevice.getTwoDRegisterAccessor<double>("ADC/DATA");
twoDAccessor.read();
for(size_t i = 0; i < twoDAccessor.getNChannels(); ++i) {
std::cout << "Channel " << i << ":";
std::vector<double>& channel = twoDAccessor[i];
for(double sample : channel) {
std::cout << " " << sample;
}
std::cout << std::endl;
}
myDevice.close();
return 0;
}