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