From f72a6d20a614aeac421587c6aef1b90ffce41d14 Mon Sep 17 00:00:00 2001 From: Andrea Bellandi Date: Thu, 30 Apr 2026 17:02:41 +0200 Subject: [PATCH 1/7] feat: UIO backend supports multiple maps Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- backends/uio/include/UioAccess.h | 71 ++++++++++--- backends/uio/src/UioAccess.cc | 165 ++++++++++++++++++++++--------- backends/uio/src/UioBackend.cc | 2 +- 3 files changed, 181 insertions(+), 57 deletions(-) diff --git a/backends/uio/include/UioAccess.h b/backends/uio/include/UioAccess.h index 0e3596aac..d0a83fb7f 100644 --- a/backends/uio/include/UioAccess.h +++ b/backends/uio/include/UioAccess.h @@ -2,29 +2,68 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #pragma once +// The mainline kernel linux define the maximum number of UIO maps as 5 +#ifndef MAX_UIO_MAPS +# define MAX_UIO_MAPS 5 +#endif + #include +#include #include #include #include +#include namespace ChimeraTK { /// @brief Implements a generic userspace interface for UIO devices. class UioAccess { private: - boost::filesystem::path _deviceFilePath; + /// @brief Implements map interface for UIO devices . + class UioMap { + public: + UioMap(); + UioMap(int deviceFileDescriptor, size_t uioMapIdx, const std::string& uioMapPath); + ~UioMap(); + UioMap(const UioMap&) = delete; + UioMap& operator=(const UioMap&) = delete; + UioMap(UioMap&& other) noexcept; + UioMap& operator=(UioMap&&) noexcept; + + explicit operator bool() const noexcept; + + /// @brief Read data from the specified memory offset address. The address range starts at '0'. + /// @param address Start address of memory to read from + /// @param data Address pointer to which data is to be copied + /// @param sizeInBytes Number of bytes to copy + void read(uint64_t address, int32_t* data, size_t sizeInBytes); + + /// @brief Write data to the specified memory offset address. The address range starts at '0'. + /// @param address Start address of memory to write to + /// @param data Address pointer from which data is to be copied + /// @param sizeInBytes Number of bytes to copy + void write(uint64_t address, int32_t const* data, size_t sizeInBytes); + + /// @brief Calculate the address from the perspective of the UIO map + /// @param address Start address of memory of the request + /// @param sizeInBytes Number of bytes to copy + /// @param isWrite Determines if it is a read or a write request + /// @return Unsigned value + size_t checkMapAddress(uint64_t address, size_t sizeInBytes, bool isWrite); + + private: + size_t _deviceLowerBound = 0; + size_t _deviceHigherBound = 0; + void* _deviceUserBase = nullptr; + }; + int _deviceFileDescriptor = 0; - void* _deviceUserBase = nullptr; - void* _deviceKernelBase = nullptr; - size_t _deviceMemSize = 0; + boost::filesystem::path _deviceFilePath; + std::string _filename; + std::array _maps; uint32_t _lastInterruptCount = 0; std::atomic _opened{false}; - - /// @brief Maps user space memory range to address range of UIO device. - void UioMMap(); - - /// @brief Unmaps user space memory range for address range of UIO device. - void UioUnmap(); + uint8_t _maps_number = 0; /// @brief Subtracts uint32_t values taking overflow into account. /// @param minuend Minuend of subtraction @@ -35,12 +74,17 @@ namespace ChimeraTK { /// @brief Reads a decimal formatted value from a file as unsigned 32-bit integer. /// @param fileName File path to read from /// @return Unsigned value - uint32_t readUint32FromFile(std::string fileName); + static uint32_t readUint32FromFile(std::string fileName); /// @brief Reads a hexadecimal formatted value from a file as unsigned 64-bit integer. /// @param fileName File path to read from /// @return Unsigned value - uint64_t readUint64HexFromFile(std::string fileName); + static uint64_t readUint64HexFromFile(std::string fileName); + + /// @brief Get an UIO map handle + /// @param map The map to open + /// @return Reference to the opened map + UioAccess::UioMap& getMap(size_t map); public: explicit UioAccess(const std::string& deviceFilePath); @@ -52,6 +96,9 @@ namespace ChimeraTK { /// @brief Closes UIO device. void close(); + // @brief check whether the passed map is valid + bool mapIndexValid(uint64_t map); + /// @brief Read data from the specified memory offset address. The address range starts at '0'. /// @param map Selected UIO memory region. Only region '0' is currently supported. /// @param address Start address of memory to read from diff --git a/backends/uio/src/UioAccess.cc b/backends/uio/src/UioAccess.cc index dbc9fcc9d..a080cf6ab 100644 --- a/backends/uio/src/UioAccess.cc +++ b/backends/uio/src/UioAccess.cc @@ -7,15 +7,106 @@ #include +#include + #include #include +#include #include +#include #include #include +#include namespace ChimeraTK { + UioAccess::UioMap::UioMap() {} + + UioAccess::UioMap::UioMap(int deviceFileDescriptor, size_t uioMapIdx, const std::string& uioMapPath) + : _deviceLowerBound(readUint64HexFromFile(uioMapPath + "/addr")), + _deviceHigherBound(_deviceLowerBound + readUint64HexFromFile(uioMapPath + "/size")) { + size_t mapSize = _deviceHigherBound - _deviceLowerBound; + + void* mapped = + mmap(NULL, mapSize, PROT_READ | PROT_WRITE, MAP_SHARED, deviceFileDescriptor, uioMapIdx * getpagesize()); + + if(mapped == MAP_FAILED) { + _deviceLowerBound = 0; + _deviceHigherBound = 0; + throw ChimeraTK::runtime_error("UIO: Cannot allocate memory for UIO map '" + uioMapPath + "'"); + } + + _deviceUserBase = mapped; + } + + UioAccess::UioMap::~UioMap() { + if(*this) { + auto mapSize = _deviceHigherBound - _deviceLowerBound; + munmap(_deviceUserBase, mapSize); + } + } + + UioAccess::UioMap::UioMap(UioMap&& other) noexcept + : _deviceLowerBound(std::exchange(other._deviceLowerBound, 0)), + _deviceHigherBound(std::exchange(other._deviceHigherBound, 0)), + _deviceUserBase(std::exchange(other._deviceUserBase, nullptr)) {} + + UioAccess::UioMap& UioAccess::UioMap::operator=(UioMap&& other) noexcept { + if(this != &other) { + if(*this) { + auto mapSize = _deviceHigherBound - _deviceLowerBound; + munmap(_deviceUserBase, mapSize); + } + + this->_deviceLowerBound = std::exchange(other._deviceLowerBound, 0); + this->_deviceHigherBound = std::exchange(other._deviceHigherBound, 0); + this->_deviceUserBase = std::exchange(other._deviceUserBase, nullptr); + } + return *this; + } + + UioAccess::UioMap::operator bool() const noexcept { + return _deviceUserBase != nullptr; + } + + void UioAccess::UioMap::read(uint64_t address, int32_t* data, size_t sizeInBytes) { + volatile int32_t* rptr = static_cast(_deviceUserBase) + + checkMapAddress(address, sizeInBytes, false) / sizeof(int32_t); + + while(sizeInBytes >= sizeof(int32_t)) { + *(data++) = *(rptr++); + sizeInBytes -= sizeof(int32_t); + } + } + + void UioAccess::UioMap::write(uint64_t address, int32_t const* data, size_t sizeInBytes) { + volatile int32_t* __restrict__ wptr = + static_cast(_deviceUserBase) + checkMapAddress(address, sizeInBytes, true) / sizeof(int32_t); + + while(sizeInBytes >= sizeof(int32_t)) { + *(wptr++) = *(data++); + sizeInBytes -= sizeof(int32_t); + } + } + + size_t UioAccess::UioMap::checkMapAddress(uint64_t address, size_t sizeInBytes, bool isWrite) { + if(!*this) [[unlikely]] { + std::string requestType = isWrite ? "Write" : "Read"; + throw ChimeraTK::logic_error(std::format("UIO: {} request on unmapped memory region", requestType)); + } + + if(address < _deviceLowerBound || address + sizeInBytes > _deviceHigherBound) [[unlikely]] { + std::string requestType = isWrite ? "Write" : "Read"; + throw ChimeraTK::logic_error( + std::format("UIO: {} request (low = {}, high = {}) outside device memory region (low = {}, high = {})", + requestType, address, address + sizeInBytes, _deviceLowerBound, _deviceHigherBound)); + } + + // This is a temporary work around, because register nodes of current map use absolute bus addresses. + return address - _deviceLowerBound; + } + UioAccess::UioAccess(const std::string& deviceFilePath) : _deviceFilePath(deviceFilePath.c_str()) {} UioAccess::~UioAccess() { @@ -26,64 +117,62 @@ namespace ChimeraTK { if(boost::filesystem::is_symlink(_deviceFilePath)) { _deviceFilePath = boost::filesystem::canonical(_deviceFilePath); } - std::string fileName = _deviceFilePath.filename().string(); - _deviceKernelBase = (void*)readUint64HexFromFile("/sys/class/uio/" + fileName + "/maps/map0/addr"); - _deviceMemSize = readUint64HexFromFile("/sys/class/uio/" + fileName + "/maps/map0/size"); - _lastInterruptCount = readUint32FromFile("/sys/class/uio/" + fileName + "/event"); + _filename = _deviceFilePath.filename().string(); + _lastInterruptCount = readUint32FromFile("/sys/class/uio/" + _filename + "/event"); // Open UIO device file here, so that interrupt thread can run before calling open() _deviceFileDescriptor = ::open(_deviceFilePath.c_str(), O_RDWR); if(_deviceFileDescriptor < 0) { throw ChimeraTK::runtime_error("UIO: Failed to open device file '" + getDeviceFilePath() + "'"); } - UioMMap(); + + _maps_number = 0; + while(true) { + std::string uioMapPath = "/sys/class/uio/" + _filename + "/maps/map" + std::to_string(_maps_number); + if(!boost::filesystem::is_directory(uioMapPath)) break; + _maps_number++; + } + _opened = true; } void UioAccess::close() { if(_opened) { - UioUnmap(); + for(auto& map : _maps) map = UioMap{}; ::close(_deviceFileDescriptor); _opened = false; } } - void UioAccess::read(uint64_t map, uint64_t address, int32_t* __restrict__ data, size_t sizeInBytes) { - if(map > 0) { - throw ChimeraTK::logic_error("UIO: Multiple memory regions are not supported"); - } - - // This is a temporary work around, because register nodes of current map use absolute bus addresses. - address = address % reinterpret_cast(_deviceKernelBase); + bool UioAccess::mapIndexValid(uint64_t map) { + return map < _maps_number; + } - if(address + sizeInBytes > _deviceMemSize) { - throw ChimeraTK::logic_error("UIO: Read request exceeds device memory region"); + void UioAccess::read(uint64_t map, uint64_t address, int32_t* __restrict__ data, size_t sizeInBytes) { + if(!mapIndexValid(map)) [[unlikely]] { + throw ChimeraTK::logic_error("UIO: Attempt to access map" + std::to_string(map) + + " outside the range (registered maps = " + std::to_string(_maps.size()) + ")"); } - volatile int32_t* rptr = static_cast(_deviceUserBase) + address / sizeof(int32_t); - while(sizeInBytes >= sizeof(int32_t)) { - *(data++) = *(rptr++); - sizeInBytes -= sizeof(int32_t); - } + getMap(map).read(address, data, sizeInBytes); } void UioAccess::write(uint64_t map, uint64_t address, int32_t const* data, size_t sizeInBytes) { - if(map > 0) { - throw ChimeraTK::logic_error("UIO: Multiple memory regions are not supported"); + if(!mapIndexValid(map)) [[unlikely]] { + throw ChimeraTK::logic_error("UIO: Attempt to access map" + std::to_string(map) + + " outside the range (registered maps = " + std::to_string(_maps.size()) + ")"); } - // This is a temporary work around, because register nodes of current map use absolute bus addresses. - address = address % reinterpret_cast(_deviceKernelBase); + getMap(map).write(address, data, sizeInBytes); + } - if(address + sizeInBytes > _deviceMemSize) { - throw ChimeraTK::logic_error("UIO: Write request exceeds device memory region"); + UioAccess::UioMap& UioAccess::getMap(size_t map) { + if(!_maps[map]) [[unlikely]] { + std::string uioMapPath = "/sys/class/uio/" + _filename + "/maps/map" + std::to_string(map); + _maps[map] = UioAccess::UioMap(_deviceFileDescriptor, map, uioMapPath); } - volatile int32_t* __restrict__ wptr = static_cast(_deviceUserBase) + address / sizeof(int32_t); - while(sizeInBytes >= sizeof(int32_t)) { - *(wptr++) = *(data++); - sizeInBytes -= sizeof(int32_t); - } + return _maps[map]; } uint32_t UioAccess::waitForInterrupt(int timeoutMs) { @@ -133,19 +222,6 @@ namespace ChimeraTK { return _deviceFilePath.string(); } - void UioAccess::UioMMap() { - _deviceUserBase = mmap(NULL, _deviceMemSize, PROT_READ | PROT_WRITE, MAP_SHARED, _deviceFileDescriptor, 0); - if(_deviceUserBase == MAP_FAILED) { - ::close(_deviceFileDescriptor); - throw ChimeraTK::runtime_error("UIO: Cannot allocate memory for UIO device '" + getDeviceFilePath() + "'"); - } - return; - } - - void UioAccess::UioUnmap() { - munmap(_deviceUserBase, _deviceMemSize); - } - uint32_t UioAccess::subtractUint32OverflowSafe(uint32_t minuend, uint32_t subtrahend) { if(subtrahend > minuend) { return minuend + @@ -177,4 +253,5 @@ namespace ChimeraTK { } return value; } + } // namespace ChimeraTK diff --git a/backends/uio/src/UioBackend.cc b/backends/uio/src/UioBackend.cc index b6ad83032..c0ddeb41a 100644 --- a/backends/uio/src/UioBackend.cc +++ b/backends/uio/src/UioBackend.cc @@ -57,7 +57,7 @@ namespace ChimeraTK { } bool UioBackend::barIndexValid(uint64_t bar) { - return (bar == 0); + return _uioAccess->mapIndexValid(bar); } void UioBackend::read(uint64_t bar, uint64_t address, int32_t* data, size_t sizeInBytes) { From fc6187a8f8ea6a1a72e424caad811c61f135d631 Mon Sep 17 00:00:00 2001 From: Andrea Bellandi Date: Wed, 6 May 2026 14:43:22 +0200 Subject: [PATCH 2/7] fix: corrected code using FREI Evo Pro suggestions --- backends/uio/include/UioAccess.h | 7 +++---- backends/uio/src/UioAccess.cc | 35 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/backends/uio/include/UioAccess.h b/backends/uio/include/UioAccess.h index d0a83fb7f..06b82de11 100644 --- a/backends/uio/include/UioAccess.h +++ b/backends/uio/include/UioAccess.h @@ -44,14 +44,13 @@ namespace ChimeraTK { /// @param sizeInBytes Number of bytes to copy void write(uint64_t address, int32_t const* data, size_t sizeInBytes); + private: /// @brief Calculate the address from the perspective of the UIO map /// @param address Start address of memory of the request /// @param sizeInBytes Number of bytes to copy /// @param isWrite Determines if it is a read or a write request /// @return Unsigned value - size_t checkMapAddress(uint64_t address, size_t sizeInBytes, bool isWrite); - - private: + size_t validateAndGetMapOffset(uint64_t address, size_t sizeInBytes, bool isWrite); size_t _deviceLowerBound = 0; size_t _deviceHigherBound = 0; void* _deviceUserBase = nullptr; @@ -100,7 +99,7 @@ namespace ChimeraTK { bool mapIndexValid(uint64_t map); /// @brief Read data from the specified memory offset address. The address range starts at '0'. - /// @param map Selected UIO memory region. Only region '0' is currently supported. + /// @param map Selected UIO memory region. /// @param address Start address of memory to read from /// @param data Address pointer to which data is to be copied /// @param sizeInBytes Number of bytes to copy diff --git a/backends/uio/src/UioAccess.cc b/backends/uio/src/UioAccess.cc index a080cf6ab..169a0df6d 100644 --- a/backends/uio/src/UioAccess.cc +++ b/backends/uio/src/UioAccess.cc @@ -21,7 +21,7 @@ namespace ChimeraTK { - UioAccess::UioMap::UioMap() {} + UioAccess::UioMap::UioMap() = default; UioAccess::UioMap::UioMap(int deviceFileDescriptor, size_t uioMapIdx, const std::string& uioMapPath) : _deviceLowerBound(readUint64HexFromFile(uioMapPath + "/addr")), @@ -29,12 +29,13 @@ namespace ChimeraTK { size_t mapSize = _deviceHigherBound - _deviceLowerBound; void* mapped = - mmap(NULL, mapSize, PROT_READ | PROT_WRITE, MAP_SHARED, deviceFileDescriptor, uioMapIdx * getpagesize()); + mmap(nullptr, mapSize, PROT_READ | PROT_WRITE, MAP_SHARED, deviceFileDescriptor, + static_cast(uioMapIdx * getpagesize())); if(mapped == MAP_FAILED) { _deviceLowerBound = 0; _deviceHigherBound = 0; - throw ChimeraTK::runtime_error("UIO: Cannot allocate memory for UIO map '" + uioMapPath + "'"); + throw ChimeraTK::runtime_error(std::format("UIO: Cannot allocate memory for UIO map '{}'", uioMapPath)); } _deviceUserBase = mapped; @@ -72,7 +73,7 @@ namespace ChimeraTK { void UioAccess::UioMap::read(uint64_t address, int32_t* data, size_t sizeInBytes) { volatile int32_t* rptr = static_cast(_deviceUserBase) + - checkMapAddress(address, sizeInBytes, false) / sizeof(int32_t); + validateAndGetMapOffset(address, sizeInBytes, false) / sizeof(int32_t); while(sizeInBytes >= sizeof(int32_t)) { *(data++) = *(rptr++); @@ -81,8 +82,8 @@ namespace ChimeraTK { } void UioAccess::UioMap::write(uint64_t address, int32_t const* data, size_t sizeInBytes) { - volatile int32_t* __restrict__ wptr = - static_cast(_deviceUserBase) + checkMapAddress(address, sizeInBytes, true) / sizeof(int32_t); + volatile int32_t* __restrict__ wptr = static_cast(_deviceUserBase) + + validateAndGetMapOffset(address, sizeInBytes, true) / sizeof(int32_t); while(sizeInBytes >= sizeof(int32_t)) { *(wptr++) = *(data++); @@ -90,7 +91,7 @@ namespace ChimeraTK { } } - size_t UioAccess::UioMap::checkMapAddress(uint64_t address, size_t sizeInBytes, bool isWrite) { + size_t UioAccess::UioMap::validateAndGetMapOffset(uint64_t address, size_t sizeInBytes, bool isWrite) { if(!*this) [[unlikely]] { std::string requestType = isWrite ? "Write" : "Read"; throw ChimeraTK::logic_error(std::format("UIO: {} request on unmapped memory region", requestType)); @@ -118,19 +119,17 @@ namespace ChimeraTK { _deviceFilePath = boost::filesystem::canonical(_deviceFilePath); } _filename = _deviceFilePath.filename().string(); - _lastInterruptCount = readUint32FromFile("/sys/class/uio/" + _filename + "/event"); + _lastInterruptCount = readUint32FromFile(std::format("/sys/class/uio/{}/event", _filename)); // Open UIO device file here, so that interrupt thread can run before calling open() _deviceFileDescriptor = ::open(_deviceFilePath.c_str(), O_RDWR); if(_deviceFileDescriptor < 0) { - throw ChimeraTK::runtime_error("UIO: Failed to open device file '" + getDeviceFilePath() + "'"); + throw ChimeraTK::runtime_error(std::format("UIO: Failed to open device file '{}'", getDeviceFilePath())); } - _maps_number = 0; - while(true) { - std::string uioMapPath = "/sys/class/uio/" + _filename + "/maps/map" + std::to_string(_maps_number); + for(_maps_number = 0; _maps_number < MAX_UIO_MAPS; _maps_number++) { + std::string uioMapPath = std::format("/sys/class/uio/{}/maps/map{}", _filename, _maps_number); if(!boost::filesystem::is_directory(uioMapPath)) break; - _maps_number++; } _opened = true; @@ -150,8 +149,8 @@ namespace ChimeraTK { void UioAccess::read(uint64_t map, uint64_t address, int32_t* __restrict__ data, size_t sizeInBytes) { if(!mapIndexValid(map)) [[unlikely]] { - throw ChimeraTK::logic_error("UIO: Attempt to access map" + std::to_string(map) + - " outside the range (registered maps = " + std::to_string(_maps.size()) + ")"); + throw ChimeraTK::logic_error( + std::format("UIO: Attempt to access map{} outside the range (registered maps = {})", map, _maps_number)); } getMap(map).read(address, data, sizeInBytes); @@ -159,8 +158,8 @@ namespace ChimeraTK { void UioAccess::write(uint64_t map, uint64_t address, int32_t const* data, size_t sizeInBytes) { if(!mapIndexValid(map)) [[unlikely]] { - throw ChimeraTK::logic_error("UIO: Attempt to access map" + std::to_string(map) + - " outside the range (registered maps = " + std::to_string(_maps.size()) + ")"); + throw ChimeraTK::logic_error( + std::format("UIO: Attempt to access map{} outside the range (registered maps = {})", map, _maps_number)); } getMap(map).write(address, data, sizeInBytes); @@ -168,7 +167,7 @@ namespace ChimeraTK { UioAccess::UioMap& UioAccess::getMap(size_t map) { if(!_maps[map]) [[unlikely]] { - std::string uioMapPath = "/sys/class/uio/" + _filename + "/maps/map" + std::to_string(map); + std::string uioMapPath = std::format("/sys/class/uio/{}/maps/map{}", _filename, map); _maps[map] = UioAccess::UioMap(_deviceFileDescriptor, map, uioMapPath); } From 9c2a8bed4406d26c3c44a72b600cdc3f8596cc8a Mon Sep 17 00:00:00 2001 From: Andrea Bellandi Date: Wed, 13 May 2026 14:12:02 +0200 Subject: [PATCH 3/7] chore: fix UIO sources style --- backends/uio/include/UioAccess.h | 2 +- backends/uio/src/UioAccess.cc | 49 ++++++++++++++++++++++++++++++-- backends/uio/src/UioBackend.cc | 20 +++++++++++++ 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/backends/uio/include/UioAccess.h b/backends/uio/include/UioAccess.h index 06b82de11..6e2df1c15 100644 --- a/backends/uio/include/UioAccess.h +++ b/backends/uio/include/UioAccess.h @@ -95,7 +95,7 @@ namespace ChimeraTK { /// @brief Closes UIO device. void close(); - // @brief check whether the passed map is valid + /// @brief Check whether the passed map index is valid. bool mapIndexValid(uint64_t map); /// @brief Read data from the specified memory offset address. The address range starts at '0'. diff --git a/backends/uio/src/UioAccess.cc b/backends/uio/src/UioAccess.cc index 169a0df6d..0d0b2a0db 100644 --- a/backends/uio/src/UioAccess.cc +++ b/backends/uio/src/UioAccess.cc @@ -23,14 +23,15 @@ namespace ChimeraTK { UioAccess::UioMap::UioMap() = default; + /********************************************************************************************************************/ + UioAccess::UioMap::UioMap(int deviceFileDescriptor, size_t uioMapIdx, const std::string& uioMapPath) : _deviceLowerBound(readUint64HexFromFile(uioMapPath + "/addr")), _deviceHigherBound(_deviceLowerBound + readUint64HexFromFile(uioMapPath + "/size")) { size_t mapSize = _deviceHigherBound - _deviceLowerBound; - void* mapped = - mmap(nullptr, mapSize, PROT_READ | PROT_WRITE, MAP_SHARED, deviceFileDescriptor, - static_cast(uioMapIdx * getpagesize())); + void* mapped = mmap(nullptr, mapSize, PROT_READ | PROT_WRITE, MAP_SHARED, deviceFileDescriptor, + static_cast(uioMapIdx * getpagesize())); if(mapped == MAP_FAILED) { _deviceLowerBound = 0; @@ -41,6 +42,8 @@ namespace ChimeraTK { _deviceUserBase = mapped; } + /********************************************************************************************************************/ + UioAccess::UioMap::~UioMap() { if(*this) { auto mapSize = _deviceHigherBound - _deviceLowerBound; @@ -48,11 +51,15 @@ namespace ChimeraTK { } } + /********************************************************************************************************************/ + UioAccess::UioMap::UioMap(UioMap&& other) noexcept : _deviceLowerBound(std::exchange(other._deviceLowerBound, 0)), _deviceHigherBound(std::exchange(other._deviceHigherBound, 0)), _deviceUserBase(std::exchange(other._deviceUserBase, nullptr)) {} + /********************************************************************************************************************/ + UioAccess::UioMap& UioAccess::UioMap::operator=(UioMap&& other) noexcept { if(this != &other) { if(*this) { @@ -67,10 +74,14 @@ namespace ChimeraTK { return *this; } + /********************************************************************************************************************/ + UioAccess::UioMap::operator bool() const noexcept { return _deviceUserBase != nullptr; } + /********************************************************************************************************************/ + void UioAccess::UioMap::read(uint64_t address, int32_t* data, size_t sizeInBytes) { volatile int32_t* rptr = static_cast(_deviceUserBase) + validateAndGetMapOffset(address, sizeInBytes, false) / sizeof(int32_t); @@ -81,6 +92,8 @@ namespace ChimeraTK { } } + /********************************************************************************************************************/ + void UioAccess::UioMap::write(uint64_t address, int32_t const* data, size_t sizeInBytes) { volatile int32_t* __restrict__ wptr = static_cast(_deviceUserBase) + validateAndGetMapOffset(address, sizeInBytes, true) / sizeof(int32_t); @@ -91,6 +104,8 @@ namespace ChimeraTK { } } + /********************************************************************************************************************/ + size_t UioAccess::UioMap::validateAndGetMapOffset(uint64_t address, size_t sizeInBytes, bool isWrite) { if(!*this) [[unlikely]] { std::string requestType = isWrite ? "Write" : "Read"; @@ -108,12 +123,18 @@ namespace ChimeraTK { return address - _deviceLowerBound; } + /********************************************************************************************************************/ + UioAccess::UioAccess(const std::string& deviceFilePath) : _deviceFilePath(deviceFilePath.c_str()) {} + /********************************************************************************************************************/ + UioAccess::~UioAccess() { close(); } + /********************************************************************************************************************/ + void UioAccess::open() { if(boost::filesystem::is_symlink(_deviceFilePath)) { _deviceFilePath = boost::filesystem::canonical(_deviceFilePath); @@ -135,6 +156,8 @@ namespace ChimeraTK { _opened = true; } + /********************************************************************************************************************/ + void UioAccess::close() { if(_opened) { for(auto& map : _maps) map = UioMap{}; @@ -143,10 +166,14 @@ namespace ChimeraTK { } } + /********************************************************************************************************************/ + bool UioAccess::mapIndexValid(uint64_t map) { return map < _maps_number; } + /********************************************************************************************************************/ + void UioAccess::read(uint64_t map, uint64_t address, int32_t* __restrict__ data, size_t sizeInBytes) { if(!mapIndexValid(map)) [[unlikely]] { throw ChimeraTK::logic_error( @@ -156,6 +183,8 @@ namespace ChimeraTK { getMap(map).read(address, data, sizeInBytes); } + /********************************************************************************************************************/ + void UioAccess::write(uint64_t map, uint64_t address, int32_t const* data, size_t sizeInBytes) { if(!mapIndexValid(map)) [[unlikely]] { throw ChimeraTK::logic_error( @@ -165,6 +194,8 @@ namespace ChimeraTK { getMap(map).write(address, data, sizeInBytes); } + /********************************************************************************************************************/ + UioAccess::UioMap& UioAccess::getMap(size_t map) { if(!_maps[map]) [[unlikely]] { std::string uioMapPath = std::format("/sys/class/uio/{}/maps/map{}", _filename, map); @@ -174,6 +205,8 @@ namespace ChimeraTK { return _maps[map]; } + /********************************************************************************************************************/ + uint32_t UioAccess::waitForInterrupt(int timeoutMs) { // Represents the total interrupt count since system uptime. uint32_t totalInterruptCount = 0; @@ -208,6 +241,8 @@ namespace ChimeraTK { return occurredInterruptCount; } + /********************************************************************************************************************/ + void UioAccess::clearInterrupts() { uint32_t unmask = 1; ssize_t ret = ::write(_deviceFileDescriptor, &unmask, sizeof(unmask)); @@ -217,10 +252,14 @@ namespace ChimeraTK { } } + /********************************************************************************************************************/ + std::string UioAccess::getDeviceFilePath() { return _deviceFilePath.string(); } + /********************************************************************************************************************/ + uint32_t UioAccess::subtractUint32OverflowSafe(uint32_t minuend, uint32_t subtrahend) { if(subtrahend > minuend) { return minuend + @@ -231,6 +270,8 @@ namespace ChimeraTK { } } + /********************************************************************************************************************/ + uint32_t UioAccess::readUint32FromFile(std::string fileName) { uint64_t value = 0; std::ifstream inputFile(fileName); @@ -242,6 +283,8 @@ namespace ChimeraTK { return (uint32_t)value; } + /********************************************************************************************************************/ + uint64_t UioAccess::readUint64HexFromFile(std::string fileName) { uint64_t value = 0; std::ifstream inputFile(fileName); diff --git a/backends/uio/src/UioBackend.cc b/backends/uio/src/UioBackend.cc index c0ddeb41a..842a02bc3 100644 --- a/backends/uio/src/UioBackend.cc +++ b/backends/uio/src/UioBackend.cc @@ -12,10 +12,14 @@ namespace ChimeraTK { _uioAccess = std::make_shared("/dev/" + deviceName); } + /********************************************************************************************************************/ + UioBackend::~UioBackend() { UioBackend::closeImpl(); } + /********************************************************************************************************************/ + boost::shared_ptr UioBackend::createInstance( // FIXME #11279 Implement API breaking changes from linter warnings // NOLINTNEXTLINE(performance-unnecessary-value-param) @@ -31,6 +35,8 @@ namespace ChimeraTK { return boost::make_shared(address, parameters["map"], parameters["DataConsistencyKeys"]); } + /********************************************************************************************************************/ + void UioBackend::open() { if(_opened) { if(isFunctional()) { @@ -43,6 +49,8 @@ namespace ChimeraTK { setOpenedAndClearException(); } + /********************************************************************************************************************/ + void UioBackend::closeImpl() { if(_opened) { if(_interruptWaitingThread.joinable()) { @@ -56,10 +64,14 @@ namespace ChimeraTK { _opened = false; } + /********************************************************************************************************************/ + bool UioBackend::barIndexValid(uint64_t bar) { return _uioAccess->mapIndexValid(bar); } + /********************************************************************************************************************/ + void UioBackend::read(uint64_t bar, uint64_t address, int32_t* data, size_t sizeInBytes) { assert(_opened); checkActiveException(); @@ -67,6 +79,8 @@ namespace ChimeraTK { _uioAccess->read(bar, address, data, sizeInBytes); } + /********************************************************************************************************************/ + void UioBackend::write(uint64_t bar, uint64_t address, int32_t const* data, size_t sizeInBytes) { assert(_opened); checkActiveException(); @@ -74,6 +88,8 @@ namespace ChimeraTK { _uioAccess->write(bar, address, data, sizeInBytes); } + /********************************************************************************************************************/ + std::future UioBackend::activateSubscription( uint32_t interruptNumber, boost::shared_ptr> asyncDomain) { std::promise subscriptionDonePromise; @@ -95,6 +111,8 @@ namespace ChimeraTK { return subscriptionDoneFuture; } + /********************************************************************************************************************/ + std::string UioBackend::readDeviceInfo() { std::string result = std::string("UIO backend: Device path = " + _uioAccess->getDeviceFilePath()); if(!isOpen()) { @@ -104,6 +122,8 @@ namespace ChimeraTK { return result; } + /********************************************************************************************************************/ + void UioBackend::waitForInterruptLoop(std::promise subscriptionDonePromise) { try { // also the scope for the promiseFulfiller From b59ecba3523561c5ead8c365e3aaec86b053ee8c Mon Sep 17 00:00:00 2001 From: Andrea Bellandi Date: Thu, 14 May 2026 10:22:37 +0200 Subject: [PATCH 4/7] fix: move UIO maps enumeration to UIOAccess constructor to make barIndexValid available before opening the device --- backends/uio/src/UioAccess.cc | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/backends/uio/src/UioAccess.cc b/backends/uio/src/UioAccess.cc index 0d0b2a0db..21493cca4 100644 --- a/backends/uio/src/UioAccess.cc +++ b/backends/uio/src/UioAccess.cc @@ -125,7 +125,17 @@ namespace ChimeraTK { /********************************************************************************************************************/ - UioAccess::UioAccess(const std::string& deviceFilePath) : _deviceFilePath(deviceFilePath.c_str()) {} + UioAccess::UioAccess(const std::string& deviceFilePath) : _deviceFilePath(deviceFilePath.c_str()) { + if(boost::filesystem::is_symlink(_deviceFilePath)) { + _deviceFilePath = boost::filesystem::canonical(_deviceFilePath); + } + _filename = _deviceFilePath.filename().string(); + + for(_maps_number = 0; _maps_number < MAX_UIO_MAPS; _maps_number++) { + std::string uioMapPath = std::format("/sys/class/uio/{}/maps/map{}", _filename, _maps_number); + if(!boost::filesystem::is_directory(uioMapPath)) break; + } + } /********************************************************************************************************************/ @@ -136,10 +146,6 @@ namespace ChimeraTK { /********************************************************************************************************************/ void UioAccess::open() { - if(boost::filesystem::is_symlink(_deviceFilePath)) { - _deviceFilePath = boost::filesystem::canonical(_deviceFilePath); - } - _filename = _deviceFilePath.filename().string(); _lastInterruptCount = readUint32FromFile(std::format("/sys/class/uio/{}/event", _filename)); // Open UIO device file here, so that interrupt thread can run before calling open() @@ -148,11 +154,6 @@ namespace ChimeraTK { throw ChimeraTK::runtime_error(std::format("UIO: Failed to open device file '{}'", getDeviceFilePath())); } - for(_maps_number = 0; _maps_number < MAX_UIO_MAPS; _maps_number++) { - std::string uioMapPath = std::format("/sys/class/uio/{}/maps/map{}", _filename, _maps_number); - if(!boost::filesystem::is_directory(uioMapPath)) break; - } - _opened = true; } From 906eb3aa749e26339f13ea7a090eaee716c92245 Mon Sep 17 00:00:00 2001 From: Andrea Bellandi Date: Thu, 14 May 2026 20:08:06 +0200 Subject: [PATCH 5/7] fix: revert UIO to the original boundary check to make the test pass --- backends/uio/src/UioAccess.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/backends/uio/src/UioAccess.cc b/backends/uio/src/UioAccess.cc index 21493cca4..4a9b8b503 100644 --- a/backends/uio/src/UioAccess.cc +++ b/backends/uio/src/UioAccess.cc @@ -112,15 +112,17 @@ namespace ChimeraTK { throw ChimeraTK::logic_error(std::format("UIO: {} request on unmapped memory region", requestType)); } - if(address < _deviceLowerBound || address + sizeInBytes > _deviceHigherBound) [[unlikely]] { + // Register addresses are absolute bus addresses; convert to map offset the same way as the single-map backend. + size_t offset = address % _deviceLowerBound; + + if(offset + sizeInBytes > _deviceHigherBound - _deviceLowerBound) [[unlikely]] { std::string requestType = isWrite ? "Write" : "Read"; throw ChimeraTK::logic_error( - std::format("UIO: {} request (low = {}, high = {}) outside device memory region (low = {}, high = {})", - requestType, address, address + sizeInBytes, _deviceLowerBound, _deviceHigherBound)); + std::format("UIO: {} request exceeds device memory region of map (offset = {}, size = {})", requestType, + offset, _deviceHigherBound - _deviceLowerBound)); } - // This is a temporary work around, because register nodes of current map use absolute bus addresses. - return address - _deviceLowerBound; + return offset; } /********************************************************************************************************************/ From e62d3b9b9146df904791cdb88bd0bc1be6362f74 Mon Sep 17 00:00:00 2001 From: Andrea Bellandi Date: Thu, 14 May 2026 22:21:16 +0200 Subject: [PATCH 6/7] feat: add UIO test support for multiple maps --- .../executables_src/testUioBackendUnified.cpp | 101 +++++---- tests/uioBackendTest.mapp | 193 +++++++++--------- 2 files changed, 161 insertions(+), 133 deletions(-) diff --git a/tests/executables_src/testUioBackendUnified.cpp b/tests/executables_src/testUioBackendUnified.cpp index a4b3c34f6..c5ac1e9c9 100644 --- a/tests/executables_src/testUioBackendUnified.cpp +++ b/tests/executables_src/testUioBackendUnified.cpp @@ -7,6 +7,7 @@ #include using namespace boost::unit_test_framework; +#include "Device.h" #include "MapFileParser.h" #include "UnifiedBackendTest.h" @@ -19,6 +20,7 @@ using namespace boost::unit_test_framework; #include #include +#include using namespace ChimeraTK; @@ -43,12 +45,10 @@ struct TestLocker { } } - ~TestLocker() { - // FIXME: It would be nice to unlink the file here, so it does not unnecessarily remain in the file system. - // Unfortunately, this somehow spoils the locking. Completely unclear why. - - // unlink(lockfile.c_str()); - } + // FIXME: It would be nice to unlink the file here, so it does not unnecessarily remain in the file system. + // Unfortunately, this somehow spoils the locking. Completely unclear why. + // unlink(lockfile.c_str()); + ~TestLocker() = default; }; static TestLocker testLocker; @@ -67,8 +67,6 @@ class RawUioAccess { explicit RawUioAccess(const std::string& filePath, const std::string& mapFile); ~RawUioAccess(); void sendInterrupt() const; - [[nodiscard]] size_t getMemorySize() const; - void* data(); template T read(const std::string& name); template @@ -78,8 +76,8 @@ class RawUioAccess { int _uioFileDescriptor; int _uioProcFd; std::filesystem::path _deviceFilePath; - size_t _deviceMemSize = 0; - void* _memoryPointer{nullptr}; + std::vector _mapSizes; + std::vector _mapPointers; NumericAddressedRegisterCatalogue _catalogue; static uint64_t readUint64HexFromFile(const std::string& filePath); @@ -103,17 +101,23 @@ RawUioAccess::RawUioAccess(const std::string& filePath, const std::string& mapFi throw std::runtime_error("failed to open UIO device '" + filePath + "'"); } - // Determine size of UIO memory region std::string fileName = _deviceFilePath.filename().string(); - _deviceMemSize = readUint64HexFromFile("/sys/class/uio/" + fileName + "/maps/map0/size"); - - _memoryPointer = mmap(nullptr, _deviceMemSize, PROT_READ | PROT_WRITE, MAP_SHARED, _uioFileDescriptor, 0); - - if(_memoryPointer == MAP_FAILED) { - throw std::runtime_error("UioMmap construction failed"); + for(size_t mapIdx = 0;; ++mapIdx) { + std::string mapPath = "/sys/class/uio/" + fileName + "/maps/map" + std::to_string(mapIdx); + if(!std::filesystem::is_directory(mapPath)) { + break; + } + size_t mapSize = readUint64HexFromFile(mapPath + "/size"); + void* ptr = mmap(nullptr, mapSize, PROT_READ | PROT_WRITE, MAP_SHARED, _uioFileDescriptor, + static_cast(mapIdx * getpagesize())); + if(ptr == MAP_FAILED) { + throw std::runtime_error("UioMmap construction failed for map" + std::to_string(mapIdx)); + } + _mapSizes.push_back(mapSize); + _mapPointers.push_back(ptr); } - MapFileParser p; - auto [cat, metaCat] = p.parse(mapFile); + + auto [cat, metaCat] = MapFileParser::parse(mapFile); _catalogue = std::move(cat); } @@ -122,7 +126,9 @@ RawUioAccess::RawUioAccess(const std::string& filePath, const std::string& mapFi /**********************************************************************************************************************/ RawUioAccess::~RawUioAccess() { - munmap(_memoryPointer, _deviceMemSize); + for(size_t i = 0; i < _mapPointers.size(); ++i) { + munmap(_mapPointers[i], _mapSizes[i]); + } close(_uioFileDescriptor); close(_uioProcFd); } @@ -136,22 +142,11 @@ void RawUioAccess::sendInterrupt() const { /**********************************************************************************************************************/ -size_t RawUioAccess::getMemorySize() const { - return _deviceMemSize; -} - -/**********************************************************************************************************************/ - -void* RawUioAccess::data() { - return _memoryPointer; -} - -/**********************************************************************************************************************/ - template T RawUioAccess::read(const std::string& name) { auto r = _catalogue.getBackendRegister(name); - return *reinterpret_cast(reinterpret_cast(data()) + r.address); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return *reinterpret_cast(reinterpret_cast(_mapPointers[r.bar]) + r.address); } /**********************************************************************************************************************/ @@ -159,7 +154,8 @@ T RawUioAccess::read(const std::string& name) { template void RawUioAccess::write(const std::string& name, T value) { auto r = _catalogue.getBackendRegister(name); - *reinterpret_cast(reinterpret_cast(data()) + r.address) = value; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + *reinterpret_cast(reinterpret_cast(_mapPointers[r.bar]) + r.address) = value; sendInterrupt(); } @@ -243,7 +239,15 @@ struct ScalarDescriptor { /**********************************************************************************************************************/ -struct Scalar32 : ScalarDescriptor { +struct Scalar32Bar0 : ScalarDescriptor { + static std::string path() { return "BSP.SCRATCH"; } + static bool isReadable() { return true; } + static bool isWriteable() { return true; } +}; + +/**********************************************************************************************************************/ + +struct Scalar32Bar1 : ScalarDescriptor { static std::string path() { return "TIMING.WORD_ID"; } static bool isReadable() { return true; } static bool isWriteable() { return false; } @@ -251,7 +255,7 @@ struct Scalar32 : ScalarDescriptor { /**********************************************************************************************************************/ -struct Scalar32Async : ScalarDescriptor { +struct Scalar32Bar1Async : ScalarDescriptor { static std::string path() { return "MOTOR_CONTROL.MOTOR_POSITION"; } static bool isReadable() { return true; } static bool isWriteable() { return false; } @@ -262,8 +266,31 @@ struct Scalar32Async : ScalarDescriptor { /**********************************************************************************************************************/ +struct Scalar32Bar2 : ScalarDescriptor { + static std::string path() { return "FCM.WORD_REV_SWITCH"; } + static bool isReadable() { return true; } + static bool isWriteable() { return true; } +}; + +/**********************************************************************************************************************/ + +BOOST_AUTO_TEST_CASE(testBrokenBar) { + Device dev; + dev.open(cdd); + BOOST_CHECK_THROW(dev.getScalarRegisterAccessor("/BROKEN/REG"), + ChimeraTK::logic_error); // NOLINT(clang-diagnostic-unused-result) + dev.close(); +} + +/**********************************************************************************************************************/ + BOOST_AUTO_TEST_CASE(testUnified) { - UnifiedBackendTest<>().addRegister().addRegister().runTests(cdd); + UnifiedBackendTest<>() + .addRegister() + .addRegister() + .addRegister() + .addRegister() + .runTests(cdd); } /**********************************************************************************************************************/ diff --git a/tests/uioBackendTest.mapp b/tests/uioBackendTest.mapp index fa109a583..e553ee0f2 100644 --- a/tests/uioBackendTest.mapp +++ b/tests/uioBackendTest.mapp @@ -1,102 +1,103 @@ -FCM.WORD_SPI_DIVIDER 1 73728 4 0 16 0 0 RW -FCM.WORD_BYTES_TO_WRITE 1 73740 4 0 16 0 0 RW -FCM.WORD_BYTES_TO_READ 1 73744 4 0 16 0 0 RW -FCM.WORD_CONTROL 1 73748 4 0 8 0 0 RW -FCM.WORD_TCK 1 73752 4 0 1 0 0 RW -FCM.WORD_TMS 1 73756 4 0 1 0 0 RW -FCM.WORD_TDI 1 73760 4 0 1 0 0 RW -FCM.WORD_TDO 1 73764 4 0 1 0 0 RO -FCM.WORD_MAGIC 1 73784 4 0 32 0 0 RO -FCM.WORD_REV_SWITCH 1 73788 4 0 32 0 0 RW -FCM.WORD_REV_SEL 1 73792 4 0 2 0 0 RW -FCM.WORD_CRC_ERROR 1 73796 4 0 1 0 0 RO -FCM.WORD_CRC_ERROR_CNT 1 73800 4 0 32 0 0 RO -FCM.WORD_ECC_ERROR_CNT 1 73804 4 0 32 0 0 RO -FCM.WORD_ECC_SYNDROME 1 73824 4 0 13 0 0 RO -BSP.PRJ_ID 1 0 4 0 32 0 0 RO -BSP.PRJ_VERSION 1 4 4 0 32 0 0 RO -BSP.PRJ_SHASUM 1 8 4 0 32 0 0 RO -BSP.PRJ_TIMESTAMP 1 12 4 0 32 0 0 RO -BSP.ID 1 16 4 0 32 0 0 RO -BSP.VERSION 1 20 4 0 32 0 0 RO -BSP.SCRATCH 1 24 4 0 32 0 0 RW -BSP.MIG_INIT_DONE 1 28 4 0 1 0 0 RO -TIMING.WORD_ID 1 2684420608 4 0 32 0 0 RO -TIMING.WORD_VERSION 1 2684420612 4 0 32 0 0 RO -TIMING.WORD_ENABLE 1 2684420616 4 0 8 0 0 RW -TIMING.WORD_SOURCE_SEL 8 2684420640 32 0 8 0 0 RW -TIMING.WORD_SYNC_SEL 8 2684420672 32 0 8 0 0 RW -TIMING.WORD_DIVIDER_VALUE 8 2684420704 32 0 32 0 0 RW -TIMING.WORD_TRIGGER_CNT 8 2684420736 32 0 32 0 0 RO -TIMING.WORD_EXT_TRIGGER_CNT 8 2684420768 32 0 32 0 0 RO -TIMING.WORD_DELAY_ENABLE 8 2684420800 32 0 1 0 0 RW -TIMING.WORD_DELAY_VALUE 8 2684420832 32 0 32 0 0 RW -TIMING.WORD_MANUAL_TRG 8 2684420864 32 0 1 0 0 RW -DAQ.WORD_ID 1 2684452864 4 0 32 0 0 RO -DAQ.WORD_VERSION 1 2684452868 4 0 32 0 0 RO -DAQ.WORD_ENABLE 1 2684452872 4 0 3 0 0 RW -DAQ.WORD_MUX_SEL 3 2684452880 12 0 8 0 0 RW -DAQ.WORD_STROBE_DIV 3 2684452892 12 0 32 0 0 RW -DAQ.WORD_STROBE_COUNT 3 2684452904 12 0 32 0 0 RO -DAQ.WORD_SAMPLES 3 2684452916 12 0 32 0 0 RW -DAQ.WORD_DUB_BUF_ENA 3 2684452928 12 0 1 0 0 RW -DAQ.WORD_DUB_BUF_CURR 3 2684452940 12 0 1 0 0 RO -DAQ.WORD_DUB_BUF_PNUM 3 2684452952 12 0 32 0 0 RO -DAQ.WORD_FIFO_STATUS 3 2684452964 12 0 32 0 0 RO -DAQ.WORD_SENT_BURST_CNT 3 2684452976 12 0 32 0 0 RO -DAQ.WORD_TRG_DELAY_VAL 3 2684452988 12 0 32 0 0 RW -DAQ.WORD_TRG_DELAY_ENA 3 2684453000 12 0 1 0 0 RW -DAQ.WORD_TRG_CNT_BUF0 3 2684453012 12 0 16 0 0 RO -DAQ.WORD_TRG_CNT_BUF1 3 2684453024 12 0 16 0 0 RO -DAQ.WORD_TIMESTAMP_RST 3 2684453036 12 0 1 0 0 RW -MOTOR_CONTROL.IRQ 0 0 0 0 0 0 0 INTERRUPT0 -MOTOR_CONTROL.ID 1 2684473344 4 0 32 0 0 RO -MOTOR_CONTROL.VERSION 1 2684473348 4 0 32 0 0 RO -MOTOR_CONTROL.MOTOR_START 1 2684473352 4 0 1 0 0 RW -MOTOR_CONTROL.MOTOR_DESTINATION 1 2684473356 4 0 32 0 0 RW -MOTOR_CONTROL.MOTOR_MAX_ACC 1 2684473360 4 0 32 0 0 RW -MOTOR_CONTROL.MOTOR_MAX_VEL 1 2684473364 4 0 32 0 0 RW -MOTOR_CONTROL.MOTOR_BASE_VEL 1 2684473368 4 0 32 0 0 RW -MOTOR_CONTROL.MOTOR_PULSE_WIDTH 1 2684473372 4 0 32 0 0 RW -MOTOR_CONTROL.MOTOR_POSITION 1 2684473376 4 0 32 0 0 INTERRUPT0 -MOTOR_CONTROL.MOTOR_POSITION_RESET 1 2684473380 4 0 1 0 0 RW -UNIO.WORD_ID 1 2684473472 4 0 32 0 0 RO -UNIO.WORD_VERSION 1 2684473476 4 0 32 0 0 RO -UNIO.WORD_USER 1 2684473480 4 0 32 0 0 RW -UNIO.WORD_LED 1 2684473492 4 0 1 0 0 RW -UNIO.WORD_DAC0_DATA 1 2684473496 4 0 12 0 0 RW -UNIO.WORD_DAC1_DATA 1 2684473500 4 0 12 0 0 RW -UNIO.WORD_ADC0_DATA 1 2684473504 4 0 12 0 0 RO -UNIO.WORD_ADC1_DATA 1 2684473508 4 0 12 0 0 RO -UNIO.WORD_CPLD_RESET 1 2684473512 4 0 1 0 0 RW -UNIO.WORD_IO_INPUT 6 2684473516 24 0 8 0 0 RO -UNIO.WORD_IO_OUTPUT 6 2684473540 24 0 8 0 0 RW -UNIO.WORD_IO_DIR 1 2684473564 4 0 6 0 0 RW -UNIO.WORD_IO_VOL 1 2684473568 4 0 6 0 0 RW -UNIO.WORD_IO_ENABLE 1 2684473572 4 0 1 0 0 RW -APP.ID 1 2684420096 4 0 32 0 0 RO -APP.VERSION 1 2684420100 4 0 32 0 0 RO -APP.STATUS 1 2684420104 4 0 32 0 0 RO -APP.MLVDS_I 1 2684420108 4 0 8 0 0 RO -APP.MLVDS_O 1 2684420112 4 0 8 0 0 RW -APP.MLVDS_OE 1 2684420116 4 0 8 0 0 RW -FCM.AREA_WRITE 1024 65536 4096 0 8 0 0 RW +FCM.WORD_SPI_DIVIDER 1 73728 4 2 16 0 0 RW +FCM.WORD_BYTES_TO_WRITE 1 73740 4 2 16 0 0 RW +FCM.WORD_BYTES_TO_READ 1 73744 4 2 16 0 0 RW +FCM.WORD_CONTROL 1 73748 4 2 8 0 0 RW +FCM.WORD_TCK 1 73752 4 2 1 0 0 RW +FCM.WORD_TMS 1 73756 4 2 1 0 0 RW +FCM.WORD_TDI 1 73760 4 2 1 0 0 RW +FCM.WORD_TDO 1 73764 4 2 1 0 0 RO +FCM.WORD_MAGIC 1 73784 4 2 32 0 0 RO +FCM.WORD_REV_SWITCH 1 73788 4 2 32 0 0 RW +FCM.WORD_REV_SEL 1 73792 4 2 2 0 0 RW +FCM.WORD_CRC_ERROR 1 73796 4 2 1 0 0 RO +FCM.WORD_CRC_ERROR_CNT 1 73800 4 2 32 0 0 RO +FCM.WORD_ECC_ERROR_CNT 1 73804 4 2 32 0 0 RO +FCM.WORD_ECC_SYNDROME 1 73824 4 2 13 0 0 RO +BSP.PRJ_ID 1 0 4 0 32 0 0 RO +BSP.PRJ_VERSION 1 4 4 0 32 0 0 RO +BSP.PRJ_SHASUM 1 8 4 0 32 0 0 RO +BSP.PRJ_TIMESTAMP 1 12 4 0 32 0 0 RO +BSP.ID 1 16 4 0 32 0 0 RO +BSP.VERSION 1 20 4 0 32 0 0 RO +BSP.SCRATCH 1 24 4 0 32 0 0 RW +BSP.MIG_INIT_DONE 1 28 4 0 1 0 0 RO +TIMING.WORD_ID 1 2684420608 4 1 32 0 0 RO +TIMING.WORD_VERSION 1 2684420612 4 1 32 0 0 RO +TIMING.WORD_ENABLE 1 2684420616 4 1 8 0 0 RW +TIMING.WORD_SOURCE_SEL 8 2684420640 32 1 8 0 0 RW +TIMING.WORD_SYNC_SEL 8 2684420672 32 1 8 0 0 RW +TIMING.WORD_DIVIDER_VALUE 8 2684420704 32 1 32 0 0 RW +TIMING.WORD_TRIGGER_CNT 8 2684420736 32 1 32 0 0 RO +TIMING.WORD_EXT_TRIGGER_CNT 8 2684420768 32 1 32 0 0 RO +TIMING.WORD_DELAY_ENABLE 8 2684420800 32 1 1 0 0 RW +TIMING.WORD_DELAY_VALUE 8 2684420832 32 1 32 0 0 RW +TIMING.WORD_MANUAL_TRG 8 2684420864 32 1 1 0 0 RW +DAQ.WORD_ID 1 2684452864 4 1 32 0 0 RO +DAQ.WORD_VERSION 1 2684452868 4 1 32 0 0 RO +DAQ.WORD_ENABLE 1 2684452872 4 1 3 0 0 RW +DAQ.WORD_MUX_SEL 3 2684452880 12 1 8 0 0 RW +DAQ.WORD_STROBE_DIV 3 2684452892 12 1 32 0 0 RW +DAQ.WORD_STROBE_COUNT 3 2684452904 12 1 32 0 0 RO +DAQ.WORD_SAMPLES 3 2684452916 12 1 32 0 0 RW +DAQ.WORD_DUB_BUF_ENA 3 2684452928 12 1 1 0 0 RW +DAQ.WORD_DUB_BUF_CURR 3 2684452940 12 1 1 0 0 RO +DAQ.WORD_DUB_BUF_PNUM 3 2684452952 12 1 32 0 0 RO +DAQ.WORD_FIFO_STATUS 3 2684452964 12 1 32 0 0 RO +DAQ.WORD_SENT_BURST_CNT 3 2684452976 12 1 32 0 0 RO +DAQ.WORD_TRG_DELAY_VAL 3 2684452988 12 1 32 0 0 RW +DAQ.WORD_TRG_DELAY_ENA 3 2684453000 12 1 1 0 0 RW +DAQ.WORD_TRG_CNT_BUF0 3 2684453012 12 1 16 0 0 RO +DAQ.WORD_TRG_CNT_BUF1 3 2684453024 12 1 16 0 0 RO +DAQ.WORD_TIMESTAMP_RST 3 2684453036 12 1 1 0 0 RW +MOTOR_CONTROL.IRQ 0 0 0 0 0 0 0 INTERRUPT0 +MOTOR_CONTROL.ID 1 2684473344 4 1 32 0 0 RO +MOTOR_CONTROL.VERSION 1 2684473348 4 1 32 0 0 RO +MOTOR_CONTROL.MOTOR_START 1 2684473352 4 1 1 0 0 RW +MOTOR_CONTROL.MOTOR_DESTINATION 1 2684473356 4 1 32 0 0 RW +MOTOR_CONTROL.MOTOR_MAX_ACC 1 2684473360 4 1 32 0 0 RW +MOTOR_CONTROL.MOTOR_MAX_VEL 1 2684473364 4 1 32 0 0 RW +MOTOR_CONTROL.MOTOR_BASE_VEL 1 2684473368 4 1 32 0 0 RW +MOTOR_CONTROL.MOTOR_PULSE_WIDTH 1 2684473372 4 1 32 0 0 RW +MOTOR_CONTROL.MOTOR_POSITION 1 2684473376 4 1 32 0 1 INTERRUPT0 +MOTOR_CONTROL.MOTOR_POSITION_RESET 1 2684473380 4 1 1 0 0 RW +UNIO.WORD_ID 1 2684473472 4 1 32 0 0 RO +UNIO.WORD_VERSION 1 2684473476 4 1 32 0 0 RO +UNIO.WORD_USER 1 2684473480 4 1 32 0 0 RW +UNIO.WORD_LED 1 2684473492 4 1 1 0 0 RW +UNIO.WORD_DAC0_DATA 1 2684473496 4 1 12 0 0 RW +UNIO.WORD_DAC1_DATA 1 2684473500 4 1 12 0 0 RW +UNIO.WORD_ADC0_DATA 1 2684473504 4 1 12 0 0 RO +UNIO.WORD_ADC1_DATA 1 2684473508 4 1 12 0 0 RO +UNIO.WORD_CPLD_RESET 1 2684473512 4 1 1 0 0 RW +UNIO.WORD_IO_INPUT 6 2684473516 24 1 8 0 0 RO +UNIO.WORD_IO_OUTPUT 6 2684473540 24 1 8 0 0 RW +UNIO.WORD_IO_DIR 1 2684473564 4 1 6 0 0 RW +UNIO.WORD_IO_VOL 1 2684473568 4 1 6 0 0 RW +UNIO.WORD_IO_ENABLE 1 2684473572 4 1 1 0 0 RW +APP.ID 1 2684420096 4 1 32 0 0 RO +APP.VERSION 1 2684420100 4 1 32 0 0 RO +APP.STATUS 1 2684420104 4 1 32 0 0 RO +APP.MLVDS_I 1 2684420108 4 1 8 0 0 RO +APP.MLVDS_O 1 2684420112 4 1 8 0 0 RW +APP.MLVDS_OE 1 2684420116 4 1 8 0 0 RW +FCM.AREA_WRITE 1024 65536 4096 2 8 0 0 RW -FCM.AREA_READ 1024 69632 4096 0 8 0 0 RW +FCM.AREA_READ 1024 69632 4096 2 8 0 0 RW -DAQ.AREA_DAQ_TIMES_0 1024 2684456960 4096 0 32 0 0 RO +DAQ.AREA_DAQ_TIMES_0 1024 2684456960 4096 1 32 0 0 RO -DAQ.AREA_DAQ_TIMES_1 1024 2684461056 4096 0 32 0 0 RO +DAQ.AREA_DAQ_TIMES_1 1024 2684461056 4096 1 32 0 0 RO -DAQ.AREA_DAQ_TIMES_2 1024 2684469248 4096 0 32 0 0 RO +DAQ.AREA_DAQ_TIMES_2 1024 2684469248 4096 1 32 0 0 RO -BSP.FCM 2073 65536 8292 0 32 0 0 RW -ch0_BSP.BSP 18457 0 73828 0 32 0 0 RW -APP.TIMING 72 2684420608 288 0 32 0 0 RW -APP.DAQ 5120 2684452864 20480 0 32 0 0 RW -APP.MOTOR_CONTROL 10 2684473344 40 0 32 0 0 RW -APP.UNIO 26 2684473472 104 0 32 0 0 RW -ch0_APP.APP 13370 2684420096 53480 0 32 0 0 RW -ch0_top.ch0_BSP 18457 0 73828 0 32 0 0 RW -ch0_top.ch0_APP 13370 2684420096 53480 0 32 0 0 RW +BSP.FCM 2073 65536 8292 0 32 0 0 RW +ch0_BSP.BSP 18457 0 73828 0 32 0 0 RW +APP.TIMING 72 2684420608 288 1 32 0 0 RW +APP.DAQ 5120 2684452864 20480 1 32 0 0 RW +APP.MOTOR_CONTROL 10 2684473344 40 1 32 0 0 RW +APP.UNIO 26 2684473472 104 1 32 0 0 RW +ch0_APP.APP 13370 2684420096 53480 1 32 0 0 RW +ch0_top.ch0_BSP 18457 0 73828 0 32 0 0 RW +ch0_top.ch0_APP 13370 2684420096 53480 1 32 0 0 RW +BROKEN.REG 1 0 4 3 32 0 0 RW From 3ab424b39c07cb3b202cdcbbe78d70f673eda13b Mon Sep 17 00:00:00 2001 From: Andrea Bellandi Date: Sat, 16 May 2026 09:46:05 +0200 Subject: [PATCH 7/7] chore: rename UIO test classes from Scalar32BarX to Scalar32MapX --- .../executables_src/testUioBackendUnified.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/executables_src/testUioBackendUnified.cpp b/tests/executables_src/testUioBackendUnified.cpp index c5ac1e9c9..2bf76ee0c 100644 --- a/tests/executables_src/testUioBackendUnified.cpp +++ b/tests/executables_src/testUioBackendUnified.cpp @@ -239,7 +239,7 @@ struct ScalarDescriptor { /**********************************************************************************************************************/ -struct Scalar32Bar0 : ScalarDescriptor { +struct Scalar32Map0 : ScalarDescriptor { static std::string path() { return "BSP.SCRATCH"; } static bool isReadable() { return true; } static bool isWriteable() { return true; } @@ -247,7 +247,7 @@ struct Scalar32Bar0 : ScalarDescriptor { /**********************************************************************************************************************/ -struct Scalar32Bar1 : ScalarDescriptor { +struct Scalar32Map1 : ScalarDescriptor { static std::string path() { return "TIMING.WORD_ID"; } static bool isReadable() { return true; } static bool isWriteable() { return false; } @@ -255,7 +255,7 @@ struct Scalar32Bar1 : ScalarDescriptor { /**********************************************************************************************************************/ -struct Scalar32Bar1Async : ScalarDescriptor { +struct Scalar32Map1Async : ScalarDescriptor { static std::string path() { return "MOTOR_CONTROL.MOTOR_POSITION"; } static bool isReadable() { return true; } static bool isWriteable() { return false; } @@ -266,7 +266,7 @@ struct Scalar32Bar1Async : ScalarDescriptor { /**********************************************************************************************************************/ -struct Scalar32Bar2 : ScalarDescriptor { +struct Scalar32Map2 : ScalarDescriptor { static std::string path() { return "FCM.WORD_REV_SWITCH"; } static bool isReadable() { return true; } static bool isWriteable() { return true; } @@ -274,7 +274,7 @@ struct Scalar32Bar2 : ScalarDescriptor { /**********************************************************************************************************************/ -BOOST_AUTO_TEST_CASE(testBrokenBar) { +BOOST_AUTO_TEST_CASE(testBrokenMap) { Device dev; dev.open(cdd); BOOST_CHECK_THROW(dev.getScalarRegisterAccessor("/BROKEN/REG"), @@ -286,10 +286,10 @@ BOOST_AUTO_TEST_CASE(testBrokenBar) { BOOST_AUTO_TEST_CASE(testUnified) { UnifiedBackendTest<>() - .addRegister() - .addRegister() - .addRegister() - .addRegister() + .addRegister() + .addRegister() + .addRegister() + .addRegister() .runTests(cdd); }