ChimeraTK-DeviceAccess 03.25.00
Loading...
Searching...
No Matches
XdmaBackend.cc
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4#include "XdmaBackend.h"
5
6#include <boost/make_shared.hpp>
7
8#include <functional>
9#include <iomanip>
10#include <utility>
11
12namespace ChimeraTK {
13
15 std::string devicePath, const std::string& mapFileName, const std::string& dataConsistencyKeyDescriptor)
17 mapFileName, std::make_unique<NumericAddressedRegisterCatalogue>(), dataConsistencyKeyDescriptor),
18 _devicePath(std::move(devicePath)) {}
19
21#ifdef _DEBUG
22 std::cout << "XDMA: opening dev: " << _devicePath << std::endl;
23#endif
24 if(_ctrlIntf) {
25 if(isFunctional()) {
26 return;
27 }
28 }
29
30 _ctrlIntf.emplace(_devicePath);
31
32 std::ranges::for_each(_eventFiles, [](auto& eventFile) { eventFile = nullptr; });
33
34 // Build vector of DMA channels
35 _dmaChannels.clear();
36 for(size_t i = 0; i < _maxDmaChannels; i++) {
37 try {
38 _dmaChannels.emplace_back(_devicePath, i);
39 }
40 catch(const runtime_error&) {
41 break;
42 }
43 }
44
45#ifdef _DEBUG
46 std::cout << "XDMA: opened interface with " << _dmaChannels.size() << " DMA channels and " << _eventFiles.size()
47 << " interrupt sources\n";
48#endif
50 }
51
53 std::ranges::for_each(_eventFiles, [](auto& eventFile) { eventFile = nullptr; });
54 _ctrlIntf.reset();
55 _dmaChannels.clear();
56 _opened = false;
57 }
58
60 return _opened;
61 }
62
63 XdmaIntfAbstract& XdmaBackend::intfFromBar(uint64_t bar) {
64 if(bar == 0 && _ctrlIntf.has_value()) {
65 return _ctrlIntf.value();
66 }
67 // 13 is magic value for DMA channel (by convention)
68 // We provide N DMA channels starting from there
69 if(bar >= 13) {
70 const size_t dmaChIdx = bar - 13;
71 if(dmaChIdx < _dmaChannels.size()) {
72 return _dmaChannels[dmaChIdx];
73 }
74 }
75 throw ChimeraTK::logic_error("Couldn't find XDMA channel for BAR value " + std::to_string(bar));
76 }
77
78#ifdef _DEBUG
79 void XdmaBackend::dump(const int32_t* data, size_t nbytes) {
80 constexpr size_t wordsPerLine = 8;
81 size_t n;
82 for(n = 0; n < (nbytes / sizeof(*data)) && n < 64; n++) {
83 if(!(n % wordsPerLine)) {
84 std::cout << std::hex << std::setw(4) << std::setfill('0') << n * sizeof(*data) << ":";
85 }
86 std::cout << " " << std::hex << std::setw(8) << std::setfill('0') << data[n];
87 if((n % wordsPerLine) == wordsPerLine - 1) {
88 std::cout << "\n";
89 }
90 }
91 if((n % wordsPerLine) != wordsPerLine) {
92 std::cout << "\n";
93 }
94 }
95#endif
96
97 void XdmaBackend::read(uint64_t bar, uint64_t address, int32_t* data, size_t sizeInBytes) {
98#ifdef _DEBUGDUMP
99 std::cout << "XDMA: read " << sizeInBytes << " bytes @ BAR" << bar << ", 0x" << std::hex << address << std::endl;
100#endif
101 auto& intf = intfFromBar(bar);
102 intf.read(address, data, sizeInBytes);
103#ifdef _DEBUGDUMP
104 dump(data, sizeInBytes);
105#endif
106 }
107
108 void XdmaBackend::write(uint64_t bar, uint64_t address, const int32_t* data, size_t sizeInBytes) {
109#ifdef _DEBUGDUMP
110 std::cout << "XDMA: write " << sizeInBytes << " bytes @ BAR" << bar << ", 0x" << std::hex << address << std::endl;
111#endif
112 auto& intf = intfFromBar(bar);
113 intf.write(address, data, sizeInBytes);
114#ifdef _DEBUGDUMP
115 dump(data, sizeInBytes);
116#endif
117 }
118
120 uint32_t interruptNumber, boost::shared_ptr<async::DomainImpl<std::nullptr_t>> asyncDomain) {
121 std::promise<void> subscriptionDonePromise;
122 auto subscriptionDoneFuture = subscriptionDonePromise.get_future();
123 if(interruptNumber >= _maxInterrupts) {
124 setException("XDMA interrupt " + std::to_string(interruptNumber) + " out of range, only 0.." +
125 std::to_string(_maxInterrupts - 1) + " available\n");
126 subscriptionDonePromise.set_value();
127 return subscriptionDoneFuture;
128 }
129
130 if(!_eventFiles[interruptNumber]) {
131 _eventFiles[interruptNumber] = std::make_unique<EventFile>(this, _devicePath, interruptNumber, asyncDomain);
132 _eventFiles[interruptNumber]->startThread(std::move(subscriptionDonePromise));
133 }
134 else {
135 // thread is already running, just fulfil the promise
136 subscriptionDonePromise.set_value();
137 }
138 return subscriptionDoneFuture;
139 }
140
142 std::string result = "XDMA backend: Device path = " + _devicePath + ", number of DMA channels = ";
143 if(isOpen()) {
144 result += std::to_string(_dmaChannels.size());
145 }
146 else {
147 result += "unknown (device closed)";
148 }
149
150 // TODO: retrieve other interesting stuff (driver version...) via ioctl
151 return result;
152 }
153
154 boost::shared_ptr<DeviceBackend> XdmaBackend::createInstance(
155 // FIXME #11279 Implement API breaking changes from linter warnings
156 // NOLINTNEXTLINE(performance-unnecessary-value-param)
157 std::string address, std::map<std::string, std::string> parameters) {
158 if(address.empty()) {
159 throw ChimeraTK::logic_error("XDMA device address not specified.");
160 }
161
162 if(parameters["map"].empty()) {
163 throw ChimeraTK::logic_error("XDMA: No map file name given.");
164 }
165
166 return boost::make_shared<XdmaBackend>("/dev/" + address, parameters["map"], parameters["DataConsistencyKeys"]);
167 }
168
169} // namespace ChimeraTK
bool isFunctional() const noexcept final
Return whether a device is working as intended, usually this means it is opened and does not have any...
void setOpenedAndClearException() noexcept
Backends should call this function at the end of a (successful) open() call.
void setException(const std::string &message) noexcept final
Set the backend into an exception state.
std::atomic< bool > _opened
flag if backend is opened
Base class for address-based device backends (e.g.
void dump(const int32_t *data, size_t nbytes)
static boost::shared_ptr< DeviceBackend > createInstance(std::string address, std::map< std::string, std::string > parameters)
XdmaBackend(std::string devicePath, const std::string &mapFileName="", const std::string &dataConsistencyKeyDescriptor="")
void write(uint64_t bar, uint64_t address, const int32_t *data, size_t sizeInBytes) override
Write function to be implemented by backends.
void open() override
Open the device.
void closeImpl() override
All backends derrived from NumericAddressedBackend must implement closeImpl() instead of close.
void read(uint64_t bar, uint64_t address, int32_t *data, size_t sizeInBytes) override
Read function to be implemented by backends.
std::string readDeviceInfo() override
Return a device information string containing hardware details like the firmware version number or th...
std::future< void > activateSubscription(uint32_t interruptNumber, boost::shared_ptr< async::DomainImpl< std::nullptr_t > > asyncDomain) override
Activate/create the subscription for a given interrupt (for instance by starting the according interr...
bool isOpen() override
Return whether a device has been opened or not.
Exception thrown when a logic error has occured.
Definition Exception.h:51
Exception thrown when a runtime error has occured.
Definition Exception.h:18
STL namespace.
std::string to_string(const std::string &v)