29 using namespace esi::backends::cosim;
35 unique_ptr<AcceleratorConnection>
38 string host =
"localhost";
41 if ((colon = connectionString.find(
':')) != string::npos) {
42 portStr = connectionString.substr(colon + 1);
43 host = connectionString.substr(0, colon);
44 }
else if (connectionString.ends_with(
"cosim.cfg")) {
45 ifstream cfg(connectionString);
46 string line, key, value;
48 while (getline(cfg, line))
49 if ((colon = line.find(
":")) != string::npos) {
50 key = line.substr(0, colon);
51 value = line.substr(colon + 1);
54 else if (key ==
"host")
58 if (portStr.size() == 0)
59 throw runtime_error(
"port line not found in file");
60 }
else if (connectionString ==
"env") {
61 char *hostEnv = getenv(
"ESI_COSIM_HOST");
66 char *portEnv = getenv(
"ESI_COSIM_PORT");
70 throw runtime_error(
"ESI_COSIM_PORT environment variable not set");
72 throw runtime_error(
"Invalid connection string '" + connectionString +
"'");
74 uint16_t port = stoul(portStr);
75 return make_unique<CosimAccelerator>(
ctxt, host, port);
79 CosimAccelerator::CosimAccelerator(
Context &
ctxt,
string hostname,
83 rpcClient = std::make_unique<esi::cosim::RpcClient>();
84 rpcClient->run(hostname, port);
88 class CosimMMIO :
public MMIO {
94 uint32_t
read(uint32_t addr)
const override {
95 lowLevel->readReqs.push(addr);
97 std::optional<std::pair<uint64_t, uint8_t>> resp;
98 while (resp = lowLevel->readResps.pop(), !resp.has_value())
99 std::this_thread::sleep_for(std::chrono::microseconds(10));
100 if (resp->second != 0)
101 throw runtime_error(
"MMIO read error" + to_string(resp->second));
107 void write(uint32_t addr, uint32_t data)
override {
108 lowLevel->writeReqs.push(make_pair(addr, data));
110 std::optional<uint8_t> resp;
111 while (resp = lowLevel->writeResps.pop(), !resp.has_value())
112 std::this_thread::sleep_for(std::chrono::microseconds(10));
114 throw runtime_error(
"MMIO write error" + to_string(*resp));
123 class CosimSysInfo :
public SysInfo {
125 CosimSysInfo(
const std::unique_ptr<esi::cosim::RpcClient> &rpcClient)
126 : rpcClient(rpcClient) {}
129 unsigned int esiVersion;
130 std::vector<uint8_t> compressedManifest;
131 if (!rpcClient->getCompressedManifest(esiVersion, compressedManifest))
132 throw runtime_error(
"Could not get ESI version from cosim");
137 unsigned int esiVersion;
138 std::vector<uint8_t> compressedManifest;
139 if (!rpcClient->getCompressedManifest(esiVersion, compressedManifest))
140 throw runtime_error(
"Could not get ESI version from cosim");
141 return compressedManifest;
145 const std::unique_ptr<esi::cosim::RpcClient> &rpcClient;
154 virtual ~WriteCosimChannelPort() =
default;
157 virtual void connect()
override {
159 throw runtime_error(
"Could not find channel '" + name +
160 "' in cosimulation");
161 if (ep->getSendTypeId() ==
"")
162 throw runtime_error(
"Channel '" + name +
"' is not a read channel");
163 if (ep->getSendTypeId() != getType()->getID())
164 throw runtime_error(
"Channel '" + name +
"' has wrong type. Expected " +
165 getType()->getID() +
", got " + ep->getSendTypeId());
180 void WriteCosimChannelPort::write(
const MessageData &data) {
189 virtual ~ReadCosimChannelPort() =
default;
192 virtual void connect()
override {
194 throw runtime_error(
"Could not find channel '" + name +
195 "' in cosimulation");
196 if (ep->getRecvTypeId() ==
"")
197 throw runtime_error(
"Channel '" + name +
"' is not a read channel");
198 if (ep->getRecvTypeId() != getType()->getID())
199 throw runtime_error(
"Channel '" + name +
"' has wrong type. Expected " +
200 getType()->getID() +
", got " + ep->getRecvTypeId());
216 bool ReadCosimChannelPort::read(
MessageData &data) {
218 if (!ep->getMessageToClient(msg))
224 map<string, ChannelPort &>
225 CosimAccelerator::requestChannelsFor(
AppIDPath idPath,
227 map<string, ChannelPort &> channelResults;
230 auto f = clientChannelAssignments.find(idPath);
231 if (f == clientChannelAssignments.end())
232 return channelResults;
233 const map<string, string> &channelAssignments = f->second;
236 for (
auto [name, dir, type] : bundleType->
getChannels()) {
237 auto f = channelAssignments.find(name);
238 if (f == channelAssignments.end())
239 throw runtime_error(
"Could not find channel assignment for '" +
240 idPath.
toStr() +
"." + name +
"'");
241 string channelName = f->second;
247 if (BundlePort::isWrite(dir))
248 port =
new WriteCosimChannelPort(ep, type, channelName);
250 port =
new ReadCosimChannelPort(ep, type, channelName);
251 channels.emplace(port);
252 channelResults.emplace(name, *port);
254 return channelResults;
263 if (prefix.size() > 0)
266 if (implName ==
"cosim") {
268 for (
auto client : clients) {
269 AppIDPath fullClientPath = prefix + client.relPath;
270 map<string, string> channelAssignments;
271 for (
auto assignment : any_cast<map<string, any>>(
272 client.implOptions.at(
"channel_assignments")))
273 channelAssignments[assignment.first] =
274 any_cast<string>(assignment.second);
275 clientChannelAssignments[fullClientPath] = std::move(channelAssignments);
280 return new CosimMMIO(rpcClient->getLowLevel());
281 }
else if (svcType ==
typeid(
SysInfo)) {
282 switch (manifestMethod) {
283 case ManifestMethod::Cosim:
284 return new CosimSysInfo(rpcClient);
285 case ManifestMethod::MMIO:
286 return new MMIOSysInfo(getService<services::MMIO>());
288 }
else if (svcType ==
typeid(
CustomService) && implName ==
"cosim") {
294 void CosimAccelerator::setManifestMethod(ManifestMethod method) {
295 manifestMethod = method;
REGISTER_ACCELERATOR("cosim", backends::cosim::CosimAccelerator)
Abstract class representing a connection to an accelerator.
std::string toStr() const
Bundles represent a collection of channels.
const ChannelVector & getChannels() const
Unidirectional channels are the basic communication primitive between the host and accelerator.
virtual void disconnect()
const Type * getType() const
AcceleratorConnections, Accelerators, and Manifests must all share a context.
A logical chunk of data representing serialized data.
A ChannelPort which reads data from the accelerator.
virtual bool read(MessageData &)=0
Specify a buffer to read into.
Root class of the ESI type system.
A ChannelPort which sends data to the accelerator.
virtual void write(const MessageData &)=0
A very basic write API. Will likely change for performance reasons.
Implements a bi-directional, thread-safe bridge between the RPC server and DPI functions.
std::unique_ptr< MessageData > MessageDataPtr
Representing messages as shared pointers to vectors may be a performance issue in the future but it i...
void pushMessageToSim(MessageDataPtr msg)
Queue message to the simulation.
A service for which there are no standard services registered.
Implement the SysInfo API for a standard MMIO protocol.
virtual void write(uint32_t addr, uint32_t data)=0
virtual uint32_t read(uint32_t addr) const =0
Parent class of all APIs modeled as 'services'.
const std::type_info & Type
Information about the Accelerator system.
virtual uint32_t getEsiVersion() const =0
Get the ESI version number to check version compatibility.
virtual std::vector< uint8_t > getCompressedManifest() const =0
Return the zlib compressed JSON system manifest.
def connect(destination, source)
std::map< std::string, std::any > ServiceImplDetails
std::vector< HWClientDetail > HWClientDetails