This repository was archived by the owner on Feb 29, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcore.cpp
349 lines (288 loc) · 10.7 KB
/
core.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
/** FPGA IP component.
*
* @author Steffen Vogel <[email protected]>
* @copyright 2017-2022, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASfpga
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
#include <string>
#include <memory>
#include <utility>
#include <villas/exceptions.hpp>
#include <villas/log.hpp>
#include <villas/memory.hpp>
#include <villas/utils.hpp>
#include <villas/fpga/card.hpp>
#include <villas/fpga/vlnv.hpp>
#include <villas/fpga/core.hpp>
#include <villas/fpga/ips/pcie.hpp>
#include <villas/fpga/ips/intc.hpp>
#include <villas/fpga/ips/switch.hpp>
using namespace villas::fpga;
using namespace villas::fpga::ip;
// Special IPs that have to be initialized first. Will be initialized in the
// same order as they appear in this list, i.e. first here will be initialized
// first.
static std::list<Vlnv>
vlnvInitializationOrder = {
Vlnv(AxiPciExpressBridgeFactory::getCompatibleVlnvString()),
Vlnv(InterruptControllerFactory::getCompatibleVlnvString()),
Vlnv(AxiStreamSwitchFactory::getCompatibleVlnvString()),
};
Core::List
CoreFactory::make(PCIeCard* card, json_t *json_ips)
{
// We only have this logger until we know the factory to build an IP with
auto loggerStatic = getStaticLogger();
std::list<IpIdentifier> allIps; // All IPs available in config
std::list<IpIdentifier> orderedIps; // IPs ordered in initialization order
Core::List configuredIps; // Successfully configured IPs
Core::List initializedIps; // Initialized, i.e. ready-to-use IPs
// Parse all IP instance names and their VLNV into list `allIps`
const char* ipName;
json_t* json_ip;
json_object_foreach(json_ips, ipName, json_ip) {
const char* vlnv;
json_error_t err;
int ret = json_unpack_ex(json_ip, &err, 0, "{ s: s }",
"vlnv", &vlnv
);
if (ret != 0)
throw ConfigError(json_ip, err, "", "IP {} has no VLNV", ipName);
allIps.push_back({vlnv, ipName});
}
// Pick out IPs to be initialized first.
//
// Reverse walktrough, because we push to the
// front of the output list, so that the first element will also be the
// first to be initialized.
for (auto viIt = vlnvInitializationOrder.rbegin(); viIt != vlnvInitializationOrder.rend(); ++viIt) {
// Iterate over IPs, if VLNV matches, push to front and remove from list
for (auto it = allIps.begin(); it != allIps.end(); ++it) {
if (*viIt == it->getVlnv()) {
orderedIps.push_front(*it);
it = allIps.erase(it);
}
}
}
// Insert all other IPs at the end
orderedIps.splice(orderedIps.end(), allIps);
loggerStatic->debug("IP initialization order:");
for (auto &id : orderedIps) {
loggerStatic->debug(" " CLR_BLD("{}"), id.getName());
}
// Configure all IPs
for (auto &id : orderedIps) {
loggerStatic->info("Configuring {}", id);
// Find the appropriate factory that can create the specified VLNV
// Note:
// This is the magic part! Factories automatically register as a
// plugin as soon as they are instantiated. If there are multiple
// candidates, the first suitable factory will be used.
CoreFactory* CoreFactory = lookup(id.getVlnv());
if (CoreFactory == nullptr) {
loggerStatic->warn("No plugin found to handle {}", id.getVlnv());
continue;
}
else
loggerStatic->debug("Using {} for IP {}", CoreFactory->getName(), id.getVlnv());
auto logger = CoreFactory->getLogger();
// Create new IP instance. Since this function is virtual, it will
// construct the right, specialized type without knowing it here
// because we have already picked the right factory.
// If something goes wrong with initialization, the shared_ptr will
// take care to desctruct the Core again as it is not pushed to
// the list and will run out of scope.
auto ip = std::unique_ptr<Core>(CoreFactory->create());
if (ip == nullptr) {
logger->warn("Cannot create an instance of {}",
CoreFactory->getName());
continue;
}
// Setup generic IP type properties
ip->card = card;
ip->id = id;
ip->logger = villas::logging.get(id.getName());
json_t* json_ip = json_object_get(json_ips, id.getName().c_str());
json_t* json_irqs = json_object_get(json_ip, "irqs");
if (json_is_object(json_irqs)) {
logger->debug("Parse IRQs of {}", *ip);
const char* irqName;
json_t* json_irq;
json_object_foreach(json_irqs, irqName, json_irq) {
const char* irqEntry = json_string_value(json_irq);
auto tokens = utils::tokenize(irqEntry, ":");
if (tokens.size() != 2) {
logger->warn("Cannot parse IRQ '{}' of " CLR_BLD("{}"),
irqEntry, id.getName());
continue;
}
const std::string &irqControllerName = tokens[0];
InterruptController* intc = nullptr;
for (auto &configuredIp : configuredIps) {
if (*configuredIp == irqControllerName) {
intc = dynamic_cast<InterruptController*>(configuredIp.get());
break;
}
}
if (intc == nullptr) {
logger->error("Interrupt Controller {} for IRQ {} not found",
irqControllerName, irqName);
continue;
}
int num;
try {
num = std::stoi(tokens[1]);
} catch (const std::invalid_argument&) {
logger->warn("IRQ number is not an integer: '{}'", irqEntry);
continue;
}
logger->debug("IRQ: {} -> {}:{}", irqName, irqControllerName, num);
ip->irqs[irqName] = {num, intc, ""};
}
}
json_t* json_memory_view = json_object_get(json_ip, "memory-view");
if (json_is_object(json_memory_view)) {
logger->debug("Parse memory view of {}", *ip);
// Now find all slave address spaces this master can access
const char* bus_name;
json_t* json_bus;
json_object_foreach(json_memory_view, bus_name, json_bus) {
// This IP has a memory view => it is a bus master somewhere
// Assemble name for master address space
const std::string myAddrSpaceName =
MemoryManager::getMasterAddrSpaceName(ip->getInstanceName(),
bus_name);
// Create a master address space
const MemoryManager::AddressSpaceId myAddrSpaceId =
MemoryManager::get().getOrCreateAddressSpace(myAddrSpaceName);
ip->busMasterInterfaces[bus_name] = myAddrSpaceId;
const char* instance_name;
json_t* json_instance;
json_object_foreach(json_bus, instance_name, json_instance) {
const char* block_name;
json_t* json_block;
json_object_foreach(json_instance, block_name, json_block) {
json_int_t base, high, size;
json_error_t err;
int ret = json_unpack_ex(json_block, &err, 0, "{ s: I, s: I, s: I }",
"baseaddr", &base,
"highaddr", &high,
"size", &size
);
if (ret != 0)
throw ConfigError(json_block, err, "", "Cannot parse address block {}/{}/{}/{}",
ip->getInstanceName(),
bus_name, instance_name, block_name);
// Get or create the slave address space
const std::string slaveAddrSpace =
MemoryManager::getSlaveAddrSpaceName(instance_name, block_name);
const MemoryManager::AddressSpaceId slaveAddrSpaceId =
MemoryManager::get().getOrCreateAddressSpace(slaveAddrSpace);
// Create a new mapping to the slave address space
MemoryManager::get().createMapping(static_cast<uintptr_t>(base),
0,
static_cast<uintptr_t>(size),
bus_name,
myAddrSpaceId,
slaveAddrSpaceId);
}
}
}
}
// IP-specific setup via JSON config
CoreFactory->parse(*ip, json_ip);
// IP has been configured now
configuredIps.push_back(std::move(ip));
}
// Start and check IPs now
for (auto &ip : configuredIps) {
loggerStatic->info("Initializing {}", *ip);
// Translate all memory blocks that the IP needs to be accessible from
// the process and cache in the instance, so this has not to be done at
// runtime.
for (auto &memoryBlock : ip->getMemoryBlocks()) {
// Construct the global name of this address block
const auto addrSpaceName =
MemoryManager::getSlaveAddrSpaceName(ip->getInstanceName(),
memoryBlock);
// Retrieve its address space identifier
const auto addrSpaceId =
MemoryManager::get().findAddressSpace(addrSpaceName);
// ... and save it in IP
ip->slaveAddressSpaces.emplace(memoryBlock, addrSpaceId);
// Get the translation to the address space
const auto &translation =
MemoryManager::get().getTranslationFromProcess(addrSpaceId);
// Cache it in the IP instance only with local name
ip->addressTranslations.emplace(memoryBlock, translation);
}
if (not ip->init()) {
loggerStatic->error("Cannot start IP {}", *ip);
continue;
}
if (not ip->check()) {
loggerStatic->error("Checking failed for IP {}", *ip);
continue;
}
// Will only be reached if the IP successfully was initialized
initializedIps.push_back(std::move(ip));
}
loggerStatic->debug("Initialized IPs:");
for (auto &ip : initializedIps) {
loggerStatic->debug(" {}", *ip);
}
return initializedIps;
}
void
Core::dump()
{
logger->info("IP: {}", *this);
for (auto& [num, irq] : irqs) {
logger->info(" IRQ {}: {}:{}",
num, irq.irqController->getInstanceName(), irq.num);
}
for (auto& [block, translation] : addressTranslations) {
logger->info(" Memory {}: {}", block, translation);
}
}
CoreFactory*
CoreFactory::lookup(const Vlnv &vlnv)
{
for (auto &ip : plugin::registry->lookup<CoreFactory>()) {
if (ip->getCompatibleVlnv() == vlnv)
return ip;
}
return nullptr;
}
uintptr_t
Core::getLocalAddr(const MemoryBlockName &block, uintptr_t address) const
{
// Throws exception if block not present
auto &translation = addressTranslations.at(block);
return translation.getLocalAddr(address);
}
InterruptController*
Core::getInterruptController(const std::string &interruptName) const
{
try {
const IrqPort irq = irqs.at(interruptName);
return irq.irqController;
} catch (const std::out_of_range&) {
return nullptr;
}
}