CIRCT 23.0.0git
Loading...
Searching...
No Matches
Cosim.cpp
Go to the documentation of this file.
1//===- Cosim.cpp - Connection to ESI simulation ---------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// DO NOT EDIT!
10// This file is distributed as part of an ESI package. The source for this file
11// should always be modified within CIRCT
12// (lib/dialect/ESI/runtime/cpp/lib/backends/Cosim.cpp).
13//
14//===----------------------------------------------------------------------===//
15
16#include "esi/backends/Cosim.h"
17#include "esi/Engines.h"
18#include "esi/Ports.h"
19#include "esi/Services.h"
20#include "esi/Utils.h"
22
23#include <cstring>
24#include <format>
25#include <fstream>
26#include <iostream>
27#include <set>
28
29using namespace esi;
30using namespace esi::services;
31using namespace esi::backends::cosim;
32
33namespace {
34
35//===----------------------------------------------------------------------===//
36// WriteCosimChannelPort
37//===----------------------------------------------------------------------===//
38
39/// Cosim client implementation of a write channel port.
40class WriteCosimChannelPort : public WriteChannelPort {
41public:
42 WriteCosimChannelPort(AcceleratorConnection &conn, RpcClient &client,
43 const RpcClient::ChannelDesc &desc, const Type *type,
44 std::string name)
45 : WriteChannelPort(type), conn(conn), client(client), desc(desc),
46 name(std::move(name)) {}
47 ~WriteCosimChannelPort() = default;
48
49 void connectImpl(const ChannelPort::ConnectOptions &options) override {
50 if (desc.dir != RpcClient::ChannelDirection::ToServer)
51 throw std::runtime_error("Channel '" + name +
52 "' is not a to server channel");
53 }
54
55protected:
56 void writeImpl(const MessageData &data) override {
57 auto frames = getMessageFrames(data);
58 for (const auto &frame : frames) {
59 conn.getLogger().trace(
60 [this,
61 &data](std::string &subsystem, std::string &msg,
62 std::unique_ptr<std::map<std::string, std::any>> &details) {
63 subsystem = "cosim_write";
64 msg = "Writing message to channel '" + name + "'";
65 details = std::make_unique<std::map<std::string, std::any>>();
66 (*details)["channel"] = name;
67 (*details)["data_size"] = data.getSize();
68 (*details)["message_data"] = data.toHex();
69 });
70
71 client.writeToServer(name, frame);
72 }
73 }
74 bool tryWriteImpl(const MessageData &data) override {
75 // For simplicity, this implementation does not support backpressure and
76 // always returns true. A more complex implementation could track pending
77 // messages and return false if there are too many.
78 writeImpl(data);
79 return true;
80 }
81
82private:
84 RpcClient &client;
86 std::string name;
87};
88
89//===----------------------------------------------------------------------===//
90// ReadCosimChannelPort
91//===----------------------------------------------------------------------===//
92
93/// Cosim client implementation of a read channel port. The wire transport
94/// (see `CosimRpc`) delivers messages via callback, so this class just
95/// forwards them to the registered `ReadChannelPort` consumer.
96class ReadCosimChannelPort : public ReadChannelPort {
97public:
98 ReadCosimChannelPort(AcceleratorConnection &conn, RpcClient &client,
99 const RpcClient::ChannelDesc &desc, const Type *type,
100 std::string name)
101 : ReadChannelPort(type), conn(conn), client(client), desc(desc),
102 name(std::move(name)) {}
103
104 ~ReadCosimChannelPort() = default;
105
106 void connectImpl(const ChannelPort::ConnectOptions &options) override {
107 if (desc.dir != RpcClient::ChannelDirection::ToClient)
108 throw std::runtime_error("Channel '" + name +
109 "' is not a to client channel");
110
111 // Connect to the channel and set up callback.
112 connection = client.connectClientReceiver(
113 name, [this](std::unique_ptr<SegmentedMessageData> &data) {
114 // Add trace logging for the received message.
115 conn.getLogger().trace(
116 [this, &data](
117 std::string &subsystem, std::string &msg,
118 std::unique_ptr<std::map<std::string, std::any>> &details) {
119 subsystem = "cosim_read";
120 msg = "Received message from channel '" + name + "'";
121 details = std::make_unique<std::map<std::string, std::any>>();
122 MessageData flat = data->toMessageData();
123 (*details)["channel"] = name;
124 (*details)["data_size"] = flat.getSize();
125 (*details)["message_data"] = flat.toHex();
126 });
127
128 bool consumed = invokeCallback(data);
129
130 if (consumed) {
131 // Log the message consumption.
132 conn.getLogger().trace(
133 [this](
134 std::string &subsystem, std::string &msg,
135 std::unique_ptr<std::map<std::string, std::any>> &details) {
136 subsystem = "cosim_read";
137 msg = "Message from channel '" + name + "' consumed";
138 });
139 }
140
141 return consumed;
142 });
143 }
144
145 void disconnect() override {
146 conn.getLogger().debug("cosim_read", "Disconnecting channel " + name);
147 if (connection) {
148 connection->disconnect();
149 connection.reset();
150 }
152 }
153
154private:
156 RpcClient &client;
158 std::string name;
159 std::unique_ptr<RpcClient::ReadChannelConnection> connection;
160};
161
162} // anonymous namespace
163
164//===----------------------------------------------------------------------===//
165// CosimAccelerator
166//===----------------------------------------------------------------------===//
167
168/// Parse the connection std::string and instantiate the accelerator. Support
169/// the traditional 'host:port' syntax and a path to 'cosim.cfg' which is output
170/// by the cosimulation when it starts (which is useful when it chooses its own
171/// port).
172std::unique_ptr<AcceleratorConnection>
173CosimAccelerator::connect(Context &ctxt, std::string connectionString) {
174 std::string portStr;
175 std::string host = "localhost";
176
177 size_t colon;
178 if ((colon = connectionString.find(':')) != std::string::npos) {
179 portStr = connectionString.substr(colon + 1);
180 host = connectionString.substr(0, colon);
181 } else if (connectionString.ends_with("cosim.cfg")) {
182 std::ifstream cfg(connectionString);
183 std::string line, key, value;
184
185 while (getline(cfg, line))
186 if ((colon = line.find(":")) != std::string::npos) {
187 key = line.substr(0, colon);
188 value = line.substr(colon + 1);
189 if (key == "port")
190 portStr = value;
191 else if (key == "host")
192 host = value;
193 }
194
195 if (portStr.size() == 0)
196 throw std::runtime_error("port line not found in file");
197 } else if (connectionString == "env") {
198 char *hostEnv = getenv("ESI_COSIM_HOST");
199 if (hostEnv)
200 host = hostEnv;
201 else
202 host = "localhost";
203 char *portEnv = getenv("ESI_COSIM_PORT");
204 if (portEnv)
205 portStr = portEnv;
206 else
207 throw std::runtime_error("ESI_COSIM_PORT environment variable not set");
208 } else {
209 throw std::runtime_error("Invalid connection std::string '" +
210 connectionString + "'");
211 }
212 uint16_t port = stoul(portStr);
213 auto conn = make_unique<CosimAccelerator>(ctxt, host, port);
214
215 // Using the MMIO manifest method is really only for internal debugging, so it
216 // doesn't need to be part of the connection string.
217 char *manifestMethod = getenv("ESI_COSIM_MANIFEST_MMIO");
218 if (manifestMethod != nullptr)
219 conn->setManifestMethod(ManifestMethod::MMIO);
220
221 return conn;
222}
223
224/// Construct and connect to a cosim server.
225CosimAccelerator::CosimAccelerator(Context &ctxt, std::string hostname,
226 uint16_t port)
227 : AcceleratorConnection(ctxt) {
228 // Connect to the simulation.
229 rpcClient = std::make_unique<RpcClient>(getLogger(), hostname, port);
230}
236
237namespace {
238class CosimSysInfo : public SysInfo {
239#pragma pack(push, 1)
240 struct CycleInfo {
241 uint64_t freq;
242 uint64_t cycle;
243 };
244#pragma pack(pop)
245
246public:
247 CosimSysInfo(CosimAccelerator &conn, RpcClient *rpcClient)
248 : SysInfo(conn), rpcClient(rpcClient) {
249 // This is an optional interface; if the channels aren't present, we simply
250 // report no cycle/frequency information.
251 RpcClient::ChannelDesc argDesc, resultDesc;
252 if (!rpcClient->getChannelDesc("__cosim_cycle_count.arg", argDesc) ||
253 !rpcClient->getChannelDesc("__cosim_cycle_count.result", resultDesc))
254 return;
255
256 Context &ctxt = conn.getCtxt();
257 const esi::Type *i1Type = getType(ctxt, new BitsType("i1", 1));
258 const esi::Type *i64Type = getType(ctxt, new BitsType("i64", 64));
259 const esi::Type *resultType =
260 getType(ctxt, new StructType(resultDesc.type,
261 {{"cycle", i64Type}, {"freq", i64Type}}));
262
263 reqPort = std::make_unique<WriteCosimChannelPort>(
264 conn, *rpcClient, argDesc, i1Type, "__cosim_cycle_count.arg");
265 respPort = std::make_unique<ReadCosimChannelPort>(
266 conn, *rpcClient, resultDesc, resultType, "__cosim_cycle_count.result");
267 auto *bundleType =
268 new BundleType("cosimCycleCount",
269 {{"arg", BundleType::Direction::To, i1Type},
270 {"result", BundleType::Direction::From, resultType}});
271 func.reset(FuncService::Function::get(AppID("__cosim_cycle_count"),
272 bundleType, *reqPort, *respPort));
273 func->connect();
274 }
275
276 uint32_t getEsiVersion() const override { return rpcClient->getEsiVersion(); }
277 std::optional<uint64_t> getCycleCount() const override {
278 if (!func)
279 return std::nullopt;
280 return getCycleInfo().cycle;
281 }
282 std::optional<uint64_t> getCoreClockFrequency() const override {
283 if (!func)
284 return std::nullopt;
285 return getCycleInfo().freq;
286 }
287
288 std::vector<uint8_t> getCompressedManifest() const override {
289 return rpcClient->getCompressedManifest();
290 }
291
292private:
293 const esi::Type *getType(Context &ctxt, esi::Type *type) {
294 if (auto t = ctxt.getType(type->getID())) {
295 delete type;
296 return *t;
297 }
298 ctxt.registerType(type);
299 return type;
300 }
301
302 RpcClient *rpcClient;
303 std::unique_ptr<WriteCosimChannelPort> reqPort;
304 std::unique_ptr<ReadCosimChannelPort> respPort;
305 std::unique_ptr<FuncService::Function> func;
306
307 CycleInfo getCycleInfo() const {
308 MessageData arg({1}); // 1-bit trigger message
309 std::future<MessageData> result = func->call(arg);
310 result.wait();
311 MessageData respMsg = result.get();
312 return *respMsg.as<CycleInfo>();
313 }
314};
315} // namespace
316
317namespace {
318class CosimMMIO : public MMIO {
319public:
320 CosimMMIO(CosimAccelerator &conn, Context &ctxt, const AppIDPath &idPath,
321 RpcClient *rpcClient, const HWClientDetails &clients)
322 : MMIO(conn, idPath, clients) {
323 // We have to locate the channels ourselves since this service might be used
324 // to retrieve the manifest.
325 RpcClient::ChannelDesc cmdArg, cmdResp;
326 if (!rpcClient->getChannelDesc("__cosim_mmio_read_write.arg", cmdArg) ||
327 !rpcClient->getChannelDesc("__cosim_mmio_read_write.result", cmdResp))
328 throw std::runtime_error("Could not find MMIO channels");
329
330 const esi::Type *i64Type = getType(ctxt, new UIntType(cmdResp.type, 64));
331 const esi::Type *cmdType = getType(
332 ctxt, new StructType(cmdArg.type, {{"write", new BitsType("i1", 1)},
333 {"offset", new UIntType("ui32", 32)},
334 {"data", new BitsType("i64", 64)}}));
335
336 // Get ports, create the function, then connect to it.
337 cmdArgPort = std::make_unique<WriteCosimChannelPort>(
338 conn, *rpcClient, cmdArg, cmdType, "__cosim_mmio_read_write.arg");
339 cmdRespPort = std::make_unique<ReadCosimChannelPort>(
340 conn, *rpcClient, cmdResp, i64Type, "__cosim_mmio_read_write.result");
341 auto *bundleType = new BundleType(
342 "cosimMMIO", {{"arg", BundleType::Direction::To, cmdType},
343 {"result", BundleType::Direction::From, i64Type}});
344 cmdMMIO.reset(FuncService::Function::get(AppID("__cosim_mmio"), bundleType,
345 *cmdArgPort, *cmdRespPort));
346 cmdMMIO->connect();
347 }
348
349#pragma pack(push, 1)
350 struct MMIOCmd {
351 uint64_t data;
352 uint32_t offset;
353 bool write;
354 };
355#pragma pack(pop)
356
357 // Call the read function and wait for a response.
358 uint64_t read(uint32_t addr) const override {
359 MMIOCmd cmd{.data = 0, .offset = addr, .write = false};
360 auto arg = MessageData::from(cmd);
361 std::future<MessageData> result = cmdMMIO->call(arg);
362 result.wait();
363 uint64_t ret = *result.get().as<uint64_t>();
364 conn.getLogger().trace(
365 [addr, ret](std::string &subsystem, std::string &msg,
366 std::unique_ptr<std::map<std::string, std::any>> &details) {
367 subsystem = "cosim_mmio";
368 msg = "MMIO[0x" + toHex(addr) + "] = 0x" + toHex(ret);
369 });
370 return ret;
371 }
372
373 void write(uint32_t addr, uint64_t data) override {
374 conn.getLogger().trace(
375 [addr,
376 data](std::string &subsystem, std::string &msg,
377 std::unique_ptr<std::map<std::string, std::any>> &details) {
378 subsystem = "cosim_mmio";
379 msg = "MMIO[0x" + toHex(addr) + "] <- 0x" + toHex(data);
380 });
381 MMIOCmd cmd{.data = data, .offset = addr, .write = true};
382 auto arg = MessageData::from(cmd);
383 std::future<MessageData> result = cmdMMIO->call(arg);
384 result.wait();
385 }
386
387private:
388 const esi::Type *getType(Context &ctxt, esi::Type *type) {
389 if (auto t = ctxt.getType(type->getID())) {
390 delete type;
391 return *t;
392 }
393 ctxt.registerType(type);
394 return type;
395 }
396 std::unique_ptr<WriteCosimChannelPort> cmdArgPort;
397 std::unique_ptr<ReadCosimChannelPort> cmdRespPort;
398 std::unique_ptr<FuncService::Function> cmdMMIO;
399};
400
401#pragma pack(push, 1)
402struct HostMemReadReq {
403 uint8_t tag;
404 uint32_t length;
405 uint64_t address;
406};
407
408struct HostMemReadResp {
409 uint64_t data;
410 uint8_t tag;
411};
412
413struct HostMemWriteReq {
414 uint8_t valid_bytes;
415 uint64_t data;
416 uint8_t tag;
417 uint64_t address;
418};
419
420using HostMemWriteResp = uint8_t;
421#pragma pack(pop)
422
423class CosimHostMem : public HostMem {
424public:
425 CosimHostMem(AcceleratorConnection &acc, Context &ctxt, RpcClient *rpcClient)
426 : HostMem(acc), acc(acc), ctxt(ctxt), rpcClient(rpcClient) {}
427
428 void start() override {
429 // We have to locate the channels ourselves since this service might be used
430 // to retrieve the manifest.
431
432 if (writeRespPort)
433 return;
434
435 // TODO: The types here are WRONG. They need to be wrapped in Channels! Fix
436 // this in a subsequent PR.
437
438 // Setup the read side callback.
439 RpcClient::ChannelDesc readArg, readResp;
440 if (!rpcClient->getChannelDesc("__cosim_hostmem_read_req.data", readArg) ||
441 !rpcClient->getChannelDesc("__cosim_hostmem_read_resp.data", readResp))
442 throw std::runtime_error("Could not find HostMem read channels");
443
444 const esi::Type *readRespType =
445 getType(ctxt, new StructType(readResp.type,
446 {{"tag", new UIntType("ui8", 8)},
447 {"data", new BitsType("i64", 64)}}));
448 const esi::Type *readReqType =
449 getType(ctxt, new StructType(readArg.type,
450 {{"address", new UIntType("ui64", 64)},
451 {"length", new UIntType("ui32", 32)},
452 {"tag", new UIntType("ui8", 8)}}));
453
454 // Get ports. Unfortunately, we can't model this as a callback since there
455 // will sometimes be multiple responses per request.
456 readRespPort = std::make_unique<WriteCosimChannelPort>(
457 conn, *rpcClient, readResp, readRespType,
458 "__cosim_hostmem_read_resp.data");
459 readReqPort = std::make_unique<ReadCosimChannelPort>(
460 conn, *rpcClient, readArg, readReqType,
461 "__cosim_hostmem_read_req.data");
462 readReqPort->connect(
463 [this](const MessageData &req) { return serviceRead(req); });
464
465 // Setup the write side callback.
466 RpcClient::ChannelDesc writeArg, writeResp;
467 if (!rpcClient->getChannelDesc("__cosim_hostmem_write.arg", writeArg) ||
468 !rpcClient->getChannelDesc("__cosim_hostmem_write.result", writeResp))
469 throw std::runtime_error("Could not find HostMem write channels");
470
471 const esi::Type *writeRespType =
472 getType(ctxt, new UIntType(writeResp.type, 8));
473 const esi::Type *writeReqType =
474 getType(ctxt, new StructType(writeArg.type,
475 {{"address", new UIntType("ui64", 64)},
476 {"tag", new UIntType("ui8", 8)},
477 {"data", new BitsType("i64", 64)}}));
478
479 // Get ports, create the function, then connect to it.
480 writeRespPort = std::make_unique<WriteCosimChannelPort>(
481 conn, *rpcClient, writeResp, writeRespType,
482 "__cosim_hostmem_write.result");
483 writeReqPort = std::make_unique<ReadCosimChannelPort>(
484 conn, *rpcClient, writeArg, writeReqType, "__cosim_hostmem_write.arg");
485 auto *bundleType = new BundleType(
486 "cosimHostMem",
487 {{"arg", BundleType::Direction::To, writeReqType},
488 {"result", BundleType::Direction::From, writeRespType}});
489 write.reset(CallService::Callback::get(acc, AppID("__cosim_hostmem_write"),
490 bundleType, *writeRespPort,
491 *writeReqPort));
492 write->connect([this](const MessageData &req) { return serviceWrite(req); },
493 true);
494 }
495
496 // Service the read request as a callback. Simply reads the data from the
497 // location specified. TODO: check that the memory has been mapped.
498 bool serviceRead(const MessageData &reqBytes) {
499 const HostMemReadReq *req = reqBytes.as<HostMemReadReq>();
500 acc.getLogger().trace(
501 [&](std::string &subsystem, std::string &msg,
502 std::unique_ptr<std::map<std::string, std::any>> &details) {
503 subsystem = "hostmem";
504 msg = "Read request: addr=0x" + toHex(req->address) +
505 " len=" + std::to_string(req->length) +
506 " tag=" + std::to_string(req->tag);
507 });
508 // Send one response per 8 bytes. Zero-length reads (e.g. void / zero-width
509 // types) indicates a bug in the hardware and we log an error, but we still
510 // send a single response.
511 uint64_t *dataPtr = reinterpret_cast<uint64_t *>(req->address);
512 uint32_t numDataResps = (req->length + 7) / 8;
513 if (numDataResps == 0)
514 acc.getLogger().error(
515 "hostmem",
516 std::format("Read request with length=0 from addr=0x{} tag={}. "
517 "Reads of length 0 are not valid and indicate a bug "
518 "in the requester.",
519 toHex(req->address), req->tag));
520 uint32_t numResps = std::max(numDataResps, 1u);
521 for (uint32_t i = 0; i < numResps; ++i) {
522 HostMemReadResp resp{.data = i < numDataResps ? dataPtr[i] : 0,
523 .tag = req->tag};
524 acc.getLogger().trace(
525 [&](std::string &subsystem, std::string &msg,
526 std::unique_ptr<std::map<std::string, std::any>> &details) {
527 subsystem = "HostMem";
528 msg = "Read result: data=0x" + toHex(resp.data) +
529 " tag=" + std::to_string(resp.tag);
530 });
531 readRespPort->write(MessageData::from(resp));
532 }
533 return true;
534 }
535
536 // Service a write request as a callback. Simply write the data to the
537 // location specified. TODO: check that the memory has been mapped.
538 MessageData serviceWrite(const MessageData &reqBytes) {
539 const HostMemWriteReq *req = reqBytes.as<HostMemWriteReq>();
540 acc.getLogger().trace(
541 [&](std::string &subsystem, std::string &msg,
542 std::unique_ptr<std::map<std::string, std::any>> &details) {
543 subsystem = "hostmem";
544 msg = "Write request: addr=0x" + toHex(req->address) + " data=0x" +
545 toHex(req->data) +
546 " valid_bytes=" + std::to_string(req->valid_bytes) +
547 " tag=" + std::to_string(req->tag);
548 });
549 uint8_t *dataPtr = reinterpret_cast<uint8_t *>(req->address);
550 for (uint8_t i = 0; i < req->valid_bytes; ++i)
551 dataPtr[i] = (req->data >> (i * 8)) & 0xFF;
552 HostMemWriteResp resp = req->tag;
553 return MessageData::from(resp);
554 }
555
556 struct CosimHostMemRegion : public HostMemRegion {
557 CosimHostMemRegion(std::size_t size) {
558 ptr = malloc(size);
559 memset(ptr, 0xFF, size);
560 this->size = size;
561 }
562 virtual ~CosimHostMemRegion() { free(ptr); }
563 virtual void *getPtr() const override { return ptr; }
564 virtual std::size_t getSize() const override { return size; }
565
566 private:
567 void *ptr;
568 std::size_t size;
569 };
570
571 virtual std::unique_ptr<HostMemRegion>
572 allocate(std::size_t size, HostMem::Options opts) const override {
573 auto ret = std::unique_ptr<HostMemRegion>(new CosimHostMemRegion(size));
574 acc.getLogger().debug(
575 [&](std::string &subsystem, std::string &msg,
576 std::unique_ptr<std::map<std::string, std::any>> &details) {
577 subsystem = "HostMem";
578 msg = "Allocated host memory region at 0x" + toHex(ret->getPtr()) +
579 " of size " + std::to_string(size);
580 });
581 return ret;
582 }
583 virtual bool mapMemory(void *ptr, std::size_t size,
584 HostMem::Options opts) const override {
585 return true;
586 }
587 virtual void unmapMemory(void *ptr) const override {}
588
589private:
590 const esi::Type *getType(Context &ctxt, esi::Type *type) {
591 if (auto t = ctxt.getType(type->getID())) {
592 delete type;
593 return *t;
594 }
595 ctxt.registerType(type);
596 return type;
597 }
599 Context &ctxt;
600 RpcClient *rpcClient;
601 std::unique_ptr<WriteCosimChannelPort> readRespPort;
602 std::unique_ptr<ReadCosimChannelPort> readReqPort;
603 std::unique_ptr<CallService::Callback> read;
604 std::unique_ptr<WriteCosimChannelPort> writeRespPort;
605 std::unique_ptr<ReadCosimChannelPort> writeReqPort;
606 std::unique_ptr<CallService::Callback> write;
607};
608} // namespace
609
610namespace esi::backends::cosim {
611/// Implement the magic cosim channel communication.
612class CosimEngine : public Engine {
613public:
615 const ServiceImplDetails &details, const HWClientDetails &clients)
616 : Engine(conn), conn(conn) {
617 // Compute our parents idPath path.
618 AppIDPath prefix = std::move(idPath);
619 if (prefix.size() > 0)
620 prefix.pop_back();
621
622 for (auto client : clients) {
623 AppIDPath fullClientPath = prefix + client.relPath;
624 std::map<std::string, std::string> channelAssignments;
625 for (auto assignment : client.channelAssignments)
626 if (assignment.second.type == "cosim")
627 channelAssignments[assignment.first] = std::any_cast<std::string>(
628 assignment.second.implOptions.at("name"));
629 clientChannelAssignments[fullClientPath] = std::move(channelAssignments);
630 }
631 }
632
633 std::unique_ptr<ChannelPort> createPort(AppIDPath idPath,
634 const std::string &channelName,
636 const Type *type) override;
637
638private:
640 std::map<AppIDPath, std::map<std::string, std::string>>
642};
643} // namespace esi::backends::cosim
644
645std::unique_ptr<ChannelPort>
646CosimEngine::createPort(AppIDPath idPath, const std::string &channelName,
647 BundleType::Direction dir, const Type *type) {
648
649 // Find the client details for the port at 'fullPath'.
650 auto f = clientChannelAssignments.find(idPath);
651 if (f == clientChannelAssignments.end())
652 throw std::runtime_error("Could not find port for '" + idPath.toStr() +
653 "." + channelName + "'");
654 const std::map<std::string, std::string> &channelAssignments = f->second;
655 auto cosimChannelNameIter = channelAssignments.find(channelName);
656 if (cosimChannelNameIter == channelAssignments.end())
657 throw std::runtime_error("Could not find channel '" + idPath.toStr() + "." +
658 channelName + "' in cosimulation");
659
660 // Get the endpoint, which may or may not exist. Construct the port.
661 // Everything is validated when the client calls 'connect()' on the port.
663 if (!conn.rpcClient->getChannelDesc(cosimChannelNameIter->second, chDesc))
664 throw std::runtime_error("Could not find channel '" + idPath.toStr() + "." +
665 channelName + "' in cosimulation");
666
667 std::unique_ptr<ChannelPort> port;
668 std::string fullChannelName = idPath.toStr() + "." + channelName;
669 if (BundlePort::isWrite(dir))
670 port = std::make_unique<WriteCosimChannelPort>(
671 conn, *conn.rpcClient, chDesc, type, fullChannelName);
672 else
673 port = std::make_unique<ReadCosimChannelPort>(conn, *conn.rpcClient, chDesc,
674 type, fullChannelName);
675 return port;
676}
677
678void CosimAccelerator::createEngine(const std::string &engineTypeName,
679 AppIDPath idPath,
680 const ServiceImplDetails &details,
681 const HWClientDetails &clients) {
682
683 std::unique_ptr<Engine> engine = nullptr;
684 if (engineTypeName == "cosim")
685 engine = std::make_unique<CosimEngine>(*this, idPath, details, clients);
686 else
687 engine = ::esi::registry::createEngine(*this, engineTypeName, idPath,
688 details, clients);
689 registerEngine(idPath, std::move(engine), clients);
690}
692 AppIDPath idPath, std::string implName,
693 const ServiceImplDetails &details,
694 const HWClientDetails &clients) {
695 if (svcType == typeid(services::MMIO)) {
696 return new CosimMMIO(*this, getCtxt(), idPath, rpcClient.get(), clients);
697 } else if (svcType == typeid(services::HostMem)) {
698 return new CosimHostMem(*this, getCtxt(), rpcClient.get());
699 } else if (svcType == typeid(SysInfo)) {
700 switch (manifestMethod) {
702 return new CosimSysInfo(*this, rpcClient.get());
704 return new MMIOSysInfo(getService<services::MMIO>());
705 }
706 }
707 return nullptr;
708}
709
713
#define REGISTER_ACCELERATOR(Name, TAccelerator)
Abstract class representing a connection to an accelerator.
Definition Accelerator.h:96
Context & getCtxt() const
void clearOwnedObjects()
Drop accelerator-owned objects before a derived backend destroys resources that those objects may ref...
Context & ctxt
ESI accelerator context.
void registerEngine(AppIDPath idPath, std::unique_ptr< Engine > engine, const HWClientDetails &clients)
If createEngine is overridden, this method should be called to register the engine and all of the cha...
virtual void disconnect()
Disconnect from the accelerator cleanly.
Logger & getLogger() const
std::string toStr() const
Definition Manifest.cpp:814
Bits are just an array of bits.
Definition Types.h:206
static bool isWrite(BundleType::Direction bundleDir)
Compute the direction of a channel given the bundle direction and the bundle port's direction.
Definition Ports.h:615
Bundles represent a collection of channels.
Definition Types.h:104
virtual void connectImpl(const ConnectOptions &options)
Called by all connect methods to let backends initiate the underlying connections.
Definition Ports.h:304
AcceleratorConnections, Accelerators, and Manifests must all share a context.
Definition Context.h:34
std::optional< const Type * > getType(Type::ID id) const
Resolve a type id to the type.
Definition Context.h:50
void registerType(Type *type)
Register a type with the context. Takes ownership of the pointer type.
Definition Context.cpp:33
Engines implement the actual channel communication between the host and the accelerator.
Definition Engines.h:42
A concrete flat message backed by a single vector of bytes.
Definition Common.h:155
const T * as() const
Cast to a type.
Definition Common.h:190
static MessageData from(T &t)
Cast from a type to its raw bytes.
Definition Common.h:200
A ChannelPort which reads data from the accelerator.
Definition Ports.h:453
virtual void disconnect() override
Disconnect the channel.
Definition Ports.cpp:70
bool invokeCallback(std::unique_ptr< SegmentedMessageData > &msg)
Invoke the currently registered callback.
Definition Ports.cpp:87
Structs are an ordered collection of fields, each with a name and a type.
Definition Types.h:246
Root class of the ESI type system.
Definition Types.h:36
ID getID() const
Definition Types.h:42
Unsigned integer.
Definition Types.h:235
A ChannelPort which sends data to the accelerator.
Definition Ports.h:308
virtual bool tryWriteImpl(const MessageData &data)=0
Implementation for tryWrite(). Subclasses must implement this.
std::vector< MessageData > getMessageFrames(const MessageData &data)
Break a message into its frames.
Definition Ports.cpp:680
virtual void writeImpl(const MessageData &)=0
Implementation for write(). Subclasses must implement this.
Connect to an ESI simulation.
Definition Cosim.h:36
void createEngine(const std::string &engineTypeName, AppIDPath idPath, const ServiceImplDetails &details, const HWClientDetails &clients) override
Create a new engine for channel communication with the accelerator.
Definition Cosim.cpp:678
std::unique_ptr< RpcClient > rpcClient
Definition Cosim.h:65
void setManifestMethod(ManifestMethod method)
Definition Cosim.cpp:710
static std::unique_ptr< AcceleratorConnection > connect(Context &, std::string connectionString)
Parse the connection std::string and instantiate the accelerator.
Definition Cosim.cpp:173
virtual Service * createService(Service::Type service, AppIDPath path, std::string implName, const ServiceImplDetails &details, const HWClientDetails &clients) override
Called by getServiceImpl exclusively.
Definition Cosim.cpp:691
CosimAccelerator(Context &, std::string hostname, uint16_t port)
Construct and connect to a cosim server.
Definition Cosim.cpp:225
std::set< std::unique_ptr< ChannelPort > > channels
Definition Cosim.h:69
Implement the magic cosim channel communication.
Definition Cosim.cpp:612
CosimEngine(CosimAccelerator &conn, AppIDPath idPath, const ServiceImplDetails &details, const HWClientDetails &clients)
Definition Cosim.cpp:614
std::map< AppIDPath, std::map< std::string, std::string > > clientChannelAssignments
Definition Cosim.cpp:641
std::unique_ptr< ChannelPort > createPort(AppIDPath idPath, const std::string &channelName, BundleType::Direction dir, const Type *type) override
Each engine needs to know how to create a ports.
Definition Cosim.cpp:646
A client for the cosim RPC server.
Definition RpcClient.h:34
std::vector< uint8_t > getCompressedManifest() const
Get the compressed manifest from the server.
uint32_t getEsiVersion() const
Get the ESI version from the manifest.
bool getChannelDesc(const std::string &channelName, ChannelDesc &desc) const
Get the channel description for a channel name.
static Callback * get(AcceleratorConnection &acc, AppID id, const BundleType *type, WriteChannelPort &result, ReadChannelPort &arg)
Definition Services.cpp:335
static Function * get(AppID id, BundleType *type, WriteChannelPort &arg, ReadChannelPort &result)
Definition Services.cpp:286
Implement the SysInfo API for a standard MMIO protocol.
Definition Services.h:207
Parent class of all APIs modeled as 'services'.
Definition Services.h:59
const std::type_info & Type
Definition Services.h:61
Information about the Accelerator system.
Definition Services.h:113
std::unique_ptr< Engine > createEngine(AcceleratorConnection &conn, const std::string &dmaEngineName, AppIDPath idPath, const ServiceImplDetails &details, const HWClientDetails &clients)
Create an engine by name.
Definition Engines.cpp:555
Definition esi.py:1
std::map< std::string, std::any > ServiceImplDetails
Definition Common.h:108
std::string toHex(void *val)
Definition Common.cpp:37
std::vector< HWClientDetail > HWClientDetails
Definition Common.h:107
write(addr, data)
Definition xrt_cosim.py:30
read(addr)
Definition xrt_cosim.py:23
Description of a channel from the server.
Definition RpcClient.h:53
Options for allocating host memory.
Definition Services.h:255