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
11namespace ChimeraTK {
12
13 XdmaBackend::XdmaBackend(std::string devicePath, std::string mapFileName)
14 : NumericAddressedBackend(mapFileName), _devicePath(devicePath) {}
15
17
19#ifdef _DEBUG
20 std::cout << "XDMA: opening dev: " << _devicePath << std::endl;
21#endif
22 if(_ctrlIntf) {
23 if(isFunctional()) {
24 return;
25 }
26 }
27
28 _ctrlIntf.emplace(_devicePath);
29
30 std::for_each(_eventFiles.begin(), _eventFiles.end(), [](auto& eventFile) { eventFile = nullptr; });
31
32 // Build vector of DMA channels
33 _dmaChannels.clear();
34 for(size_t i = 0; i < _maxDmaChannels; i++) {
35 try {
36 _dmaChannels.emplace_back(_devicePath, i);
37 }
38 catch(const runtime_error&) {
39 break;
40 }
41 }
42
43#ifdef _DEBUG
44 std::cout << "XDMA: opened interface with " << _dmaChannels.size() << " DMA channels and " << _eventFiles.size()
45 << " interrupt sources\n";
46#endif
48 }
49
51 std::for_each(_eventFiles.begin(), _eventFiles.end(), [](auto& eventFile) { eventFile = nullptr; });
52 _ctrlIntf.reset();
53 _dmaChannels.clear();
54 _opened = false;
55 }
56
58 return _opened;
59 }
60
61 XdmaIntfAbstract& XdmaBackend::_intfFromBar(uint64_t bar) {
62 if(bar == 0 && _ctrlIntf.has_value()) {
63 return _ctrlIntf.value();
64 }
65 // 13 is magic value for DMA channel (by convention)
66 // We provide N DMA channels starting from there
67 if(bar >= 13) {
68 const size_t dmaChIdx = bar - 13;
69 if(dmaChIdx < _dmaChannels.size()) {
70 return _dmaChannels[dmaChIdx];
71 }
72 }
73 throw ChimeraTK::logic_error("Couldn't find XDMA channel for BAR value " + std::to_string(bar));
74 }
75
76#ifdef _DEBUG
77 void XdmaBackend::dump(const int32_t* data, size_t nbytes) {
78 constexpr size_t wordsPerLine = 8;
79 size_t n;
80 for(n = 0; n < (nbytes / sizeof(*data)) && n < 64; n++) {
81 if(!(n % wordsPerLine)) {
82 std::cout << std::hex << std::setw(4) << std::setfill('0') << n * sizeof(*data) << ":";
83 }
84 std::cout << " " << std::hex << std::setw(8) << std::setfill('0') << data[n];
85 if((n % wordsPerLine) == wordsPerLine - 1) {
86 std::cout << "\n";
87 }
88 }
89 if((n % wordsPerLine) != wordsPerLine) {
90 std::cout << "\n";
91 }
92 }
93#endif
94
95 void XdmaBackend::read(uint64_t bar, uint64_t address, int32_t* data, size_t sizeInBytes) {
96#ifdef _DEBUGDUMP
97 std::cout << "XDMA: read " << sizeInBytes << " bytes @ BAR" << bar << ", 0x" << std::hex << address << std::endl;
98#endif
99 auto& intf = _intfFromBar(bar);
100 intf.read(address, data, sizeInBytes);
101#ifdef _DEBUGDUMP
102 dump(data, sizeInBytes);
103#endif
104 }
105
106 void XdmaBackend::write(uint64_t bar, uint64_t address, const int32_t* data, size_t sizeInBytes) {
107#ifdef _DEBUGDUMP
108 std::cout << "XDMA: write " << sizeInBytes << " bytes @ BAR" << bar << ", 0x" << std::hex << address << std::endl;
109#endif
110 auto& intf = _intfFromBar(bar);
111 intf.write(address, data, sizeInBytes);
112#ifdef _DEBUGDUMP
113 dump(data, sizeInBytes);
114#endif
115 }
116
118 uint32_t interruptNumber, boost::shared_ptr<async::DomainImpl<std::nullptr_t>> asyncDomain) {
119 std::promise<void> subscriptionDonePromise;
120 auto subscriptionDoneFuture = subscriptionDonePromise.get_future();
121 if(interruptNumber >= _maxInterrupts) {
122 setException("XDMA interrupt " + std::to_string(interruptNumber) + " out of range, only 0.." +
123 std::to_string(_maxInterrupts - 1) + " available\n");
124 subscriptionDonePromise.set_value();
125 return subscriptionDoneFuture;
126 }
127
128 if(!_eventFiles[interruptNumber]) {
129 _eventFiles[interruptNumber] = std::make_unique<EventFile>(this, _devicePath, interruptNumber, asyncDomain);
130 _eventFiles[interruptNumber]->startThread(std::move(subscriptionDonePromise));
131 }
132 else {
133 // thread is already running, just fulfil the promise
134 subscriptionDonePromise.set_value();
135 }
136 return subscriptionDoneFuture;
137 }
138
140 std::string result = "XDMA backend: Device path = " + _devicePath + ", number of DMA channels = ";
141 if(isOpen()) {
142 result += std::to_string(_dmaChannels.size());
143 }
144 else {
145 result += "unknown (device closed)";
146 }
147
148 // TODO: retrieve other interesting stuff (driver version...) via ioctl
149 return result;
150 }
151
152 boost::shared_ptr<DeviceBackend> XdmaBackend::createInstance(
153 std::string address, std::map<std::string, std::string> parameters) {
154 if(address.empty()) {
155 throw ChimeraTK::logic_error("XDMA device address not specified.");
156 }
157
158 return boost::make_shared<XdmaBackend>("/dev/" + address, parameters["map"]);
159 }
160
161} // 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)
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.
XdmaBackend(std::string devicePath, std::string mapFileName="")
Exception thrown when a logic error has occured.
Definition Exception.h:51
Exception thrown when a runtime error has occured.
Definition Exception.h:18
std::string to_string(const std::string &v)