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}
235
236namespace {
237class CosimSysInfo : public SysInfo {
238#pragma pack(push, 1)
239 struct CycleInfo {
240 uint64_t freq;
241 uint64_t cycle;
242 };
243#pragma pack(pop)
244
245public:
246 CosimSysInfo(CosimAccelerator &conn, RpcClient *rpcClient)
247 : SysInfo(conn), rpcClient(rpcClient) {
248 // This is an optional interface; if the channels aren't present, we simply
249 // report no cycle/frequency information.
250 RpcClient::ChannelDesc argDesc, resultDesc;
251 if (!rpcClient->getChannelDesc("__cosim_cycle_count.arg", argDesc) ||
252 !rpcClient->getChannelDesc("__cosim_cycle_count.result", resultDesc))
253 return;
254
255 Context &ctxt = conn.getCtxt();
256 const esi::Type *i1Type = getType(ctxt, new BitsType("i1", 1));
257 const esi::Type *i64Type = getType(ctxt, new BitsType("i64", 64));
258 const esi::Type *resultType =
259 getType(ctxt, new StructType(resultDesc.type,
260 {{"cycle", i64Type}, {"freq", i64Type}}));
261
262 reqPort = std::make_unique<WriteCosimChannelPort>(
263 conn, *rpcClient, argDesc, i1Type, "__cosim_cycle_count.arg");
264 respPort = std::make_unique<ReadCosimChannelPort>(
265 conn, *rpcClient, resultDesc, resultType, "__cosim_cycle_count.result");
266 auto *bundleType =
267 new BundleType("cosimCycleCount",
268 {{"arg", BundleType::Direction::To, i1Type},
269 {"result", BundleType::Direction::From, resultType}});
270 func.reset(FuncService::Function::get(AppID("__cosim_cycle_count"),
271 bundleType, *reqPort, *respPort));
272 func->connect();
273 }
274
275 uint32_t getEsiVersion() const override { return rpcClient->getEsiVersion(); }
276 std::optional<uint64_t> getCycleCount() const override {
277 if (!func)
278 return std::nullopt;
279 return getCycleInfo().cycle;
280 }
281 std::optional<uint64_t> getCoreClockFrequency() const override {
282 if (!func)
283 return std::nullopt;
284 return getCycleInfo().freq;
285 }
286
287 std::vector<uint8_t> getCompressedManifest() const override {
288 return rpcClient->getCompressedManifest();
289 }
290
291private:
292 const esi::Type *getType(Context &ctxt, esi::Type *type) {
293 if (auto t = ctxt.getType(type->getID())) {
294 delete type;
295 return *t;
296 }
297 ctxt.registerType(type);
298 return type;
299 }
300
301 RpcClient *rpcClient;
302 std::unique_ptr<WriteCosimChannelPort> reqPort;
303 std::unique_ptr<ReadCosimChannelPort> respPort;
304 std::unique_ptr<FuncService::Function> func;
305
306 CycleInfo getCycleInfo() const {
307 MessageData arg({1}); // 1-bit trigger message
308 std::future<MessageData> result = func->call(arg);
309 result.wait();
310 MessageData respMsg = result.get();
311 return *respMsg.as<CycleInfo>();
312 }
313};
314} // namespace
315
316namespace {
317class CosimMMIO : public MMIO {
318public:
319 CosimMMIO(CosimAccelerator &conn, Context &ctxt, const AppIDPath &idPath,
320 RpcClient *rpcClient, const HWClientDetails &clients)
321 : MMIO(conn, idPath, clients) {
322 // We have to locate the channels ourselves since this service might be used
323 // to retrieve the manifest.
324 RpcClient::ChannelDesc cmdArg, cmdResp;
325 if (!rpcClient->getChannelDesc("__cosim_mmio_read_write.arg", cmdArg) ||
326 !rpcClient->getChannelDesc("__cosim_mmio_read_write.result", cmdResp))
327 throw std::runtime_error("Could not find MMIO channels");
328
329 const esi::Type *i64Type = getType(ctxt, new UIntType(cmdResp.type, 64));
330 const esi::Type *cmdType = getType(
331 ctxt, new StructType(cmdArg.type, {{"write", new BitsType("i1", 1)},
332 {"offset", new UIntType("ui32", 32)},
333 {"data", new BitsType("i64", 64)}}));
334
335 // Get ports, create the function, then connect to it.
336 cmdArgPort = std::make_unique<WriteCosimChannelPort>(
337 conn, *rpcClient, cmdArg, cmdType, "__cosim_mmio_read_write.arg");
338 cmdRespPort = std::make_unique<ReadCosimChannelPort>(
339 conn, *rpcClient, cmdResp, i64Type, "__cosim_mmio_read_write.result");
340 auto *bundleType = new BundleType(
341 "cosimMMIO", {{"arg", BundleType::Direction::To, cmdType},
342 {"result", BundleType::Direction::From, i64Type}});
343 cmdMMIO.reset(FuncService::Function::get(AppID("__cosim_mmio"), bundleType,
344 *cmdArgPort, *cmdRespPort));
345 cmdMMIO->connect();
346 }
347
348#pragma pack(push, 1)
349 struct MMIOCmd {
350 uint64_t data;
351 uint32_t offset;
352 bool write;
353 };
354#pragma pack(pop)
355
356 // Call the read function and wait for a response.
357 uint64_t read(uint32_t addr) const override {
358 MMIOCmd cmd{.data = 0, .offset = addr, .write = false};
359 auto arg = MessageData::from(cmd);
360 std::future<MessageData> result = cmdMMIO->call(arg);
361 result.wait();
362 uint64_t ret = *result.get().as<uint64_t>();
363 conn.getLogger().trace(
364 [addr, ret](std::string &subsystem, std::string &msg,
365 std::unique_ptr<std::map<std::string, std::any>> &details) {
366 subsystem = "cosim_mmio";
367 msg = "MMIO[0x" + toHex(addr) + "] = 0x" + toHex(ret);
368 });
369 return ret;
370 }
371
372 void write(uint32_t addr, uint64_t data) override {
373 conn.getLogger().trace(
374 [addr,
375 data](std::string &subsystem, std::string &msg,
376 std::unique_ptr<std::map<std::string, std::any>> &details) {
377 subsystem = "cosim_mmio";
378 msg = "MMIO[0x" + toHex(addr) + "] <- 0x" + toHex(data);
379 });
380 MMIOCmd cmd{.data = data, .offset = addr, .write = true};
381 auto arg = MessageData::from(cmd);
382 std::future<MessageData> result = cmdMMIO->call(arg);
383 result.wait();
384 }
385
386private:
387 const esi::Type *getType(Context &ctxt, esi::Type *type) {
388 if (auto t = ctxt.getType(type->getID())) {
389 delete type;
390 return *t;
391 }
392 ctxt.registerType(type);
393 return type;
394 }
395 std::unique_ptr<WriteCosimChannelPort> cmdArgPort;
396 std::unique_ptr<ReadCosimChannelPort> cmdRespPort;
397 std::unique_ptr<FuncService::Function> cmdMMIO;
398};
399
400#pragma pack(push, 1)
401struct HostMemReadReq {
402 uint8_t tag;
403 uint32_t length;
404 uint64_t address;
405};
406
407struct HostMemReadResp {
408 uint64_t data;
409 uint8_t tag;
410};
411
412struct HostMemWriteReq {
413 uint8_t valid_bytes;
414 uint64_t data;
415 uint8_t tag;
416 uint64_t address;
417};
418
419using HostMemWriteResp = uint8_t;
420#pragma pack(pop)
421
422class CosimHostMem : public HostMem {
423public:
424 CosimHostMem(AcceleratorConnection &acc, Context &ctxt, RpcClient *rpcClient)
425 : HostMem(acc), acc(acc), ctxt(ctxt), rpcClient(rpcClient) {}
426
427 void start() override {
428 // We have to locate the channels ourselves since this service might be used
429 // to retrieve the manifest.
430
431 if (writeRespPort)
432 return;
433
434 // TODO: The types here are WRONG. They need to be wrapped in Channels! Fix
435 // this in a subsequent PR.
436
437 // Setup the read side callback.
438 RpcClient::ChannelDesc readArg, readResp;
439 if (!rpcClient->getChannelDesc("__cosim_hostmem_read_req.data", readArg) ||
440 !rpcClient->getChannelDesc("__cosim_hostmem_read_resp.data", readResp))
441 throw std::runtime_error("Could not find HostMem read channels");
442
443 const esi::Type *readRespType =
444 getType(ctxt, new StructType(readResp.type,
445 {{"tag", new UIntType("ui8", 8)},
446 {"data", new BitsType("i64", 64)}}));
447 const esi::Type *readReqType =
448 getType(ctxt, new StructType(readArg.type,
449 {{"address", new UIntType("ui64", 64)},
450 {"length", new UIntType("ui32", 32)},
451 {"tag", new UIntType("ui8", 8)}}));
452
453 // Get ports. Unfortunately, we can't model this as a callback since there
454 // will sometimes be multiple responses per request.
455 readRespPort = std::make_unique<WriteCosimChannelPort>(
456 conn, *rpcClient, readResp, readRespType,
457 "__cosim_hostmem_read_resp.data");
458 readReqPort = std::make_unique<ReadCosimChannelPort>(
459 conn, *rpcClient, readArg, readReqType,
460 "__cosim_hostmem_read_req.data");
461 readReqPort->connect(
462 [this](const MessageData &req) { return serviceRead(req); });
463
464 // Setup the write side callback.
465 RpcClient::ChannelDesc writeArg, writeResp;
466 if (!rpcClient->getChannelDesc("__cosim_hostmem_write.arg", writeArg) ||
467 !rpcClient->getChannelDesc("__cosim_hostmem_write.result", writeResp))
468 throw std::runtime_error("Could not find HostMem write channels");
469
470 const esi::Type *writeRespType =
471 getType(ctxt, new UIntType(writeResp.type, 8));
472 const esi::Type *writeReqType =
473 getType(ctxt, new StructType(writeArg.type,
474 {{"address", new UIntType("ui64", 64)},
475 {"tag", new UIntType("ui8", 8)},
476 {"data", new BitsType("i64", 64)}}));
477
478 // Get ports, create the function, then connect to it.
479 writeRespPort = std::make_unique<WriteCosimChannelPort>(
480 conn, *rpcClient, writeResp, writeRespType,
481 "__cosim_hostmem_write.result");
482 writeReqPort = std::make_unique<ReadCosimChannelPort>(
483 conn, *rpcClient, writeArg, writeReqType, "__cosim_hostmem_write.arg");
484 auto *bundleType = new BundleType(
485 "cosimHostMem",
486 {{"arg", BundleType::Direction::To, writeReqType},
487 {"result", BundleType::Direction::From, writeRespType}});
488 write.reset(CallService::Callback::get(acc, AppID("__cosim_hostmem_write"),
489 bundleType, *writeRespPort,
490 *writeReqPort));
491 write->connect([this](const MessageData &req) { return serviceWrite(req); },
492 true);
493 }
494
495 // Service the read request as a callback. Simply reads the data from the
496 // location specified. TODO: check that the memory has been mapped.
497 bool serviceRead(const MessageData &reqBytes) {
498 const HostMemReadReq *req = reqBytes.as<HostMemReadReq>();
499 acc.getLogger().trace(
500 [&](std::string &subsystem, std::string &msg,
501 std::unique_ptr<std::map<std::string, std::any>> &details) {
502 subsystem = "hostmem";
503 msg = "Read request: addr=0x" + toHex(req->address) +
504 " len=" + std::to_string(req->length) +
505 " tag=" + std::to_string(req->tag);
506 });
507 // Send one response per 8 bytes. Zero-length reads (e.g. void / zero-width
508 // types) indicates a bug in the hardware and we log an error, but we still
509 // send a single response.
510 uint64_t *dataPtr = reinterpret_cast<uint64_t *>(req->address);
511 uint32_t numDataResps = (req->length + 7) / 8;
512 if (numDataResps == 0)
513 acc.getLogger().error(
514 "hostmem",
515 std::format("Read request with length=0 from addr=0x{} tag={}. "
516 "Reads of length 0 are not valid and indicate a bug "
517 "in the requester.",
518 toHex(req->address), req->tag));
519 uint32_t numResps = std::max(numDataResps, 1u);
520 for (uint32_t i = 0; i < numResps; ++i) {
521 HostMemReadResp resp{.data = i < numDataResps ? dataPtr[i] : 0,
522 .tag = req->tag};
523 acc.getLogger().trace(
524 [&](std::string &subsystem, std::string &msg,
525 std::unique_ptr<std::map<std::string, std::any>> &details) {
526 subsystem = "HostMem";
527 msg = "Read result: data=0x" + toHex(resp.data) +
528 " tag=" + std::to_string(resp.tag);
529 });
530 readRespPort->write(MessageData::from(resp));
531 }
532 return true;
533 }
534
535 // Service a write request as a callback. Simply write the data to the
536 // location specified. TODO: check that the memory has been mapped.
537 MessageData serviceWrite(const MessageData &reqBytes) {
538 const HostMemWriteReq *req = reqBytes.as<HostMemWriteReq>();
539 acc.getLogger().trace(
540 [&](std::string &subsystem, std::string &msg,
541 std::unique_ptr<std::map<std::string, std::any>> &details) {
542 subsystem = "hostmem";
543 msg = "Write request: addr=0x" + toHex(req->address) + " data=0x" +
544 toHex(req->data) +
545 " valid_bytes=" + std::to_string(req->valid_bytes) +
546 " tag=" + std::to_string(req->tag);
547 });
548 uint8_t *dataPtr = reinterpret_cast<uint8_t *>(req->address);
549 for (uint8_t i = 0; i < req->valid_bytes; ++i)
550 dataPtr[i] = (req->data >> (i * 8)) & 0xFF;
551 HostMemWriteResp resp = req->tag;
552 return MessageData::from(resp);
553 }
554
555 struct CosimHostMemRegion : public HostMemRegion {
556 CosimHostMemRegion(std::size_t size) {
557 ptr = malloc(size);
558 memset(ptr, 0xFF, size);
559 this->size = size;
560 }
561 virtual ~CosimHostMemRegion() { free(ptr); }
562 virtual void *getPtr() const override { return ptr; }
563 virtual std::size_t getSize() const override { return size; }
564
565 private:
566 void *ptr;
567 std::size_t size;
568 };
569
570 virtual std::unique_ptr<HostMemRegion>
571 allocate(std::size_t size, HostMem::Options opts) const override {
572 auto ret = std::unique_ptr<HostMemRegion>(new CosimHostMemRegion(size));
573 acc.getLogger().debug(
574 [&](std::string &subsystem, std::string &msg,
575 std::unique_ptr<std::map<std::string, std::any>> &details) {
576 subsystem = "HostMem";
577 msg = "Allocated host memory region at 0x" + toHex(ret->getPtr()) +
578 " of size " + std::to_string(size);
579 });
580 return ret;
581 }
582 virtual bool mapMemory(void *ptr, std::size_t size,
583 HostMem::Options opts) const override {
584 return true;
585 }
586 virtual void unmapMemory(void *ptr) const override {}
587
588private:
589 const esi::Type *getType(Context &ctxt, esi::Type *type) {
590 if (auto t = ctxt.getType(type->getID())) {
591 delete type;
592 return *t;
593 }
594 ctxt.registerType(type);
595 return type;
596 }
598 Context &ctxt;
599 RpcClient *rpcClient;
600 std::unique_ptr<WriteCosimChannelPort> readRespPort;
601 std::unique_ptr<ReadCosimChannelPort> readReqPort;
602 std::unique_ptr<CallService::Callback> read;
603 std::unique_ptr<WriteCosimChannelPort> writeRespPort;
604 std::unique_ptr<ReadCosimChannelPort> writeReqPort;
605 std::unique_ptr<CallService::Callback> write;
606};
607} // namespace
608
609namespace esi::backends::cosim {
610/// Implement the magic cosim channel communication.
611class CosimEngine : public Engine {
612public:
614 const ServiceImplDetails &details, const HWClientDetails &clients)
615 : Engine(conn), conn(conn) {
616 // Compute our parents idPath path.
617 AppIDPath prefix = std::move(idPath);
618 if (prefix.size() > 0)
619 prefix.pop_back();
620
621 for (auto client : clients) {
622 AppIDPath fullClientPath = prefix + client.relPath;
623 std::map<std::string, std::string> channelAssignments;
624 for (auto assignment : client.channelAssignments)
625 if (assignment.second.type == "cosim")
626 channelAssignments[assignment.first] = std::any_cast<std::string>(
627 assignment.second.implOptions.at("name"));
628 clientChannelAssignments[fullClientPath] = std::move(channelAssignments);
629 }
630 }
631
632 std::unique_ptr<ChannelPort> createPort(AppIDPath idPath,
633 const std::string &channelName,
635 const Type *type) override;
636
637private:
639 std::map<AppIDPath, std::map<std::string, std::string>>
641};
642} // namespace esi::backends::cosim
643
644std::unique_ptr<ChannelPort>
645CosimEngine::createPort(AppIDPath idPath, const std::string &channelName,
646 BundleType::Direction dir, const Type *type) {
647
648 // Find the client details for the port at 'fullPath'.
649 auto f = clientChannelAssignments.find(idPath);
650 if (f == clientChannelAssignments.end())
651 throw std::runtime_error("Could not find port for '" + idPath.toStr() +
652 "." + channelName + "'");
653 const std::map<std::string, std::string> &channelAssignments = f->second;
654 auto cosimChannelNameIter = channelAssignments.find(channelName);
655 if (cosimChannelNameIter == channelAssignments.end())
656 throw std::runtime_error("Could not find channel '" + idPath.toStr() + "." +
657 channelName + "' in cosimulation");
658
659 // Get the endpoint, which may or may not exist. Construct the port.
660 // Everything is validated when the client calls 'connect()' on the port.
662 if (!conn.rpcClient->getChannelDesc(cosimChannelNameIter->second, chDesc))
663 throw std::runtime_error("Could not find channel '" + idPath.toStr() + "." +
664 channelName + "' in cosimulation");
665
666 std::unique_ptr<ChannelPort> port;
667 std::string fullChannelName = idPath.toStr() + "." + channelName;
668 if (BundlePort::isWrite(dir))
669 port = std::make_unique<WriteCosimChannelPort>(
670 conn, *conn.rpcClient, chDesc, type, fullChannelName);
671 else
672 port = std::make_unique<ReadCosimChannelPort>(conn, *conn.rpcClient, chDesc,
673 type, fullChannelName);
674 return port;
675}
676
677void CosimAccelerator::createEngine(const std::string &engineTypeName,
678 AppIDPath idPath,
679 const ServiceImplDetails &details,
680 const HWClientDetails &clients) {
681
682 std::unique_ptr<Engine> engine = nullptr;
683 if (engineTypeName == "cosim")
684 engine = std::make_unique<CosimEngine>(*this, idPath, details, clients);
685 else
686 engine = ::esi::registry::createEngine(*this, engineTypeName, idPath,
687 details, clients);
688 registerEngine(idPath, std::move(engine), clients);
689}
691 AppIDPath idPath, std::string implName,
692 const ServiceImplDetails &details,
693 const HWClientDetails &clients) {
694 if (svcType == typeid(services::MMIO)) {
695 return new CosimMMIO(*this, getCtxt(), idPath, rpcClient.get(), clients);
696 } else if (svcType == typeid(services::HostMem)) {
697 return new CosimHostMem(*this, getCtxt(), rpcClient.get());
698 } else if (svcType == typeid(SysInfo)) {
699 switch (manifestMethod) {
701 return new CosimSysInfo(*this, rpcClient.get());
703 return new MMIOSysInfo(getService<services::MMIO>());
704 }
705 }
706 return nullptr;
707}
708
712
#define REGISTER_ACCELERATOR(Name, TAccelerator)
Abstract class representing a connection to an accelerator.
Definition Accelerator.h:89
Context & getCtxt() const
Definition Accelerator.h:93
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
Definition Accelerator.h:94
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:677
std::unique_ptr< RpcClient > rpcClient
Definition Cosim.h:65
void setManifestMethod(ManifestMethod method)
Definition Cosim.cpp:709
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:690
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:611
CosimEngine(CosimAccelerator &conn, AppIDPath idPath, const ServiceImplDetails &details, const HWClientDetails &clients)
Definition Cosim.cpp:613
std::map< AppIDPath, std::map< std::string, std::string > > clientChannelAssignments
Definition Cosim.cpp:640
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:645
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