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