CIRCT 20.0.0git
Loading...
Searching...
No Matches
DpiEntryPoints.cpp
Go to the documentation of this file.
1//===- DpiEntryPoints.cpp - ESI cosim DPI calls -----------------*- C++ -*-===//
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// Cosim DPI function implementations. Mostly C-C++ gaskets to the C++
10// RpcServer.
11//
12// These function signatures were generated by an HW simulator (see dpi.h) so
13// we don't change them to be more rational here. The resulting code gets
14// dynamically linked in and I'm concerned about maintaining binary
15// compatibility with all the simulators.
16//
17//===----------------------------------------------------------------------===//
18
19#include "dpi.h"
20#include "esi/Ports.h"
22
23#include <algorithm>
24#include <cassert>
25#include <cstdlib>
26
27using namespace esi;
28using namespace esi::cosim;
29
30/// If non-null, log to this file. Protected by 'serverMutex`.
31static FILE *logFile;
32static std::unique_ptr<RpcServer> server = nullptr;
33static std::mutex serverMutex;
34
35// ---- Helper functions ----
36
37/// Emit the contents of 'msg' to the log file in hex.
38static void log(char *epId, bool toClient, const MessageData &msg) {
39 std::lock_guard<std::mutex> g(serverMutex);
40 if (!logFile)
41 return;
42
43 fprintf(logFile, "[ep: %50s to: %4s]", epId, toClient ? "host" : "sim");
44 size_t msgSize = msg.getSize();
45 auto bytes = msg.getBytes();
46 for (size_t i = 0; i < msgSize; ++i) {
47 auto b = bytes[i];
48 // Separate 32-bit words.
49 if (i % 4 == 0 && i > 0)
50 fprintf(logFile, " ");
51 // Separate 64-bit words
52 if (i % 8 == 0 && i > 0)
53 fprintf(logFile, " ");
54 fprintf(logFile, " %02x", b);
55 }
56 fprintf(logFile, "\n");
57 fflush(logFile);
58}
59
60/// Get the TCP port on which to listen. If the port isn't specified via an
61/// environment variable, return 0 to allow automatic selection.
62static int findPort() {
63 const char *portEnv = getenv("COSIM_PORT");
64 if (portEnv == nullptr) {
65 printf(
66 "[COSIM] RPC server port not found. Letting RPC server select one\n");
67 return 0;
68 }
69 printf("[COSIM] Opening RPC server on port %s\n", portEnv);
70 return std::strtoull(portEnv, nullptr, 10);
71}
72
73/// Check that an array is an array of bytes and has some size.
74// NOLINTNEXTLINE(misc-misplaced-const)
76 int expectedElemSize) {
77 if (svDimensions(data) != 1) {
78 printf("DPI-C: ERROR passed array argument that doesn't have expected 1D "
79 "dimensions\n");
80 return -1;
81 }
82 if (svGetArrayPtr(data) == NULL) {
83 printf("DPI-C: ERROR passed array argument that doesn't have C layout "
84 "(ptr==NULL)\n");
85 return -2;
86 }
87 int totalBytes = svSizeOfArray(data);
88 if (totalBytes == 0) {
89 printf("DPI-C: ERROR passed array argument that doesn't have C layout "
90 "(total_bytes==0)\n");
91 return -3;
92 }
93 int numElems = svSize(data, 1);
94 int elemSize = numElems == 0 ? 0 : (totalBytes / numElems);
95 if (numElems * expectedElemSize != totalBytes) {
96 printf("DPI-C: ERROR: passed array argument that doesn't have expected "
97 "element-size: expected=%d actual=%d numElems=%d totalBytes=%d\n",
98 expectedElemSize, elemSize, numElems, totalBytes);
99 return -4;
100 }
101 return 0;
102}
103
104// ---- Traditional cosim DPI entry points ----
105
106// Lookups for registered ports. As a future optimization, change the DPI API to
107// return a handle when registering wherein said handle is a pointer to a port.
108std::map<std::string, ReadChannelPort &> readPorts;
109std::map<ReadChannelPort *, std::future<MessageData>> readFutures;
110std::map<std::string, WriteChannelPort &> writePorts;
111
112// Register simulated device endpoints.
113// - return 0 on success, non-zero on failure (duplicate EP registered).
114// TODO: Change this by breaking it in two functions, one for read and one for
115// write. Also return the pointer as a handle.
116DPI int sv2cCosimserverEpRegister(char *endpointId, char *fromHostTypeIdC,
117 int fromHostTypeSize, char *toHostTypeIdC,
118 int toHostTypeSize) {
119 // Ensure the server has been constructed.
121 std::string fromHostTypeId(fromHostTypeIdC), toHostTypeId(toHostTypeIdC);
122
123 // Both only one type allowed.
124 if (!(fromHostTypeId.empty() ^ toHostTypeId.empty())) {
125 printf("ERROR: Only one of fromHostTypeId and toHostTypeId can be set!\n");
126 return -2;
127 }
128 if (readPorts.contains(endpointId)) {
129 printf("ERROR: Endpoint already registered!\n");
130 return -3;
131 }
132
133 if (!fromHostTypeId.empty()) {
134 ReadChannelPort &port =
135 server->registerReadPort(endpointId, fromHostTypeId);
136 readPorts.emplace(endpointId, port);
137 readFutures.emplace(&port, port.readAsync());
138 } else {
139 writePorts.emplace(endpointId,
140 server->registerWritePort(endpointId, toHostTypeId));
141 }
142 return 0;
143}
144
145// Attempt to recieve data from a client.
146// - Returns negative when call failed (e.g. EP not registered).
147// - If no message, return 0 with dataSize == 0.
148// - Assumes buffer is large enough to contain entire message. Fails if not
149// large enough. (In the future, will add support for getting the message
150// into a fixed-size buffer over multiple calls.)
151DPI int sv2cCosimserverEpTryGet(char *endpointId,
152 // NOLINTNEXTLINE(misc-misplaced-const)
153 const svOpenArrayHandle data,
154 unsigned int *dataSize) {
155 if (server == nullptr)
156 return -1;
157
158 auto portIt = readPorts.find(endpointId);
159 if (portIt == readPorts.end()) {
160 fprintf(stderr, "Endpoint not found in registry!\n");
161 return -4;
162 }
163
164 ReadChannelPort &port = portIt->second;
165 std::future<MessageData> &f = readFutures.at(&port);
166 // Poll for a message.
167 if (f.wait_for(std::chrono::milliseconds(0)) != std::future_status::ready) {
168 // No message.
169 *dataSize = 0;
170 return 0;
171 }
172 MessageData msg = f.get();
173 f = port.readAsync();
174 log(endpointId, false, msg);
175
176 // Do the validation only if there's a message available. Since the
177 // simulator is going to poll up to every tick and there's not going to be
178 // a message most of the time, this is important for performance.
179 if (validateSvOpenArray(data, sizeof(int8_t)) != 0) {
180 printf("ERROR: DPI-func=%s line=%d event=invalid-sv-array\n", __func__,
181 __LINE__);
182 return -2;
183 }
184
185 // Detect or verify size of buffer.
186 if (*dataSize == ~0u) {
187 *dataSize = svSizeOfArray(data);
188 } else if (*dataSize > (unsigned)svSizeOfArray(data)) {
189 printf("ERROR: DPI-func=%s line %d event=invalid-size (max %d)\n", __func__,
190 __LINE__, (unsigned)svSizeOfArray(data));
191 return -3;
192 }
193 // Verify it'll fit.
194 size_t msgSize = msg.getSize();
195 if (msgSize > *dataSize) {
196 printf("ERROR: Message size too big to fit in HW buffer\n");
197 return -5;
198 }
199
200 // Copy the message data.
201 size_t i;
202 auto bytes = msg.getBytes();
203 for (i = 0; i < msgSize; ++i) {
204 auto b = bytes[i];
205 *(char *)svGetArrElemPtr1(data, i) = b;
206 }
207 // Zero out the rest of the buffer.
208 for (; i < *dataSize; ++i) {
209 *(char *)svGetArrElemPtr1(data, i) = 0;
210 }
211 // Set the output data size.
212 *dataSize = msg.getSize();
213 return 0;
214}
215
216// Attempt to send data to a client.
217// - return 0 on success, negative on failure (unregistered EP).
218// - if dataSize is negative, attempt to dynamically determine the size of
219// 'data'.
220DPI int sv2cCosimserverEpTryPut(char *endpointId,
221 // NOLINTNEXTLINE(misc-misplaced-const)
222 const svOpenArrayHandle data, int dataSize) {
223 if (server == nullptr)
224 return -1;
225
226 if (validateSvOpenArray(data, sizeof(int8_t)) != 0) {
227 printf("ERROR: DPI-func=%s line=%d event=invalid-sv-array\n", __func__,
228 __LINE__);
229 return -2;
230 }
231
232 // Detect or verify size.
233 if (dataSize < 0) {
234 dataSize = svSizeOfArray(data);
235 } else if (dataSize > svSizeOfArray(data)) { // not enough data
236 printf("ERROR: DPI-func=%s line %d event=invalid-size limit %d array %d\n",
237 __func__, __LINE__, dataSize, svSizeOfArray(data));
238 return -3;
239 }
240
241 // Copy the message data into 'blob'.
242 std::vector<uint8_t> dataVec(dataSize);
243 for (int i = 0; i < dataSize; ++i) {
244 dataVec[i] = *(char *)svGetArrElemPtr1(data, i);
245 }
246 auto blob = std::make_unique<esi::MessageData>(dataVec);
247
248 // Queue the blob.
249 auto portIt = writePorts.find(endpointId);
250 if (portIt == writePorts.end()) {
251 fprintf(stderr, "Endpoint not found in registry!\n");
252 return -4;
253 }
254 log(endpointId, true, *blob);
255 WriteChannelPort &port = portIt->second;
256 port.write(*blob);
257 return 0;
258}
259
260// Teardown cosimserver (disconnects from primary server port, stops connections
261// from active clients).
263 std::lock_guard<std::mutex> g(serverMutex);
264 printf("[cosim] Tearing down RPC server.\n");
265 if (server != nullptr) {
266 server->stop();
267 server = nullptr;
268
269 fclose(logFile);
270 logFile = nullptr;
271 }
272}
273
274// Start cosimserver (spawns server for HW-initiated work, listens for
275// connections from new SW-clients).
277 std::lock_guard<std::mutex> g(serverMutex);
278 if (server == nullptr) {
279 // Open log file if requested.
280 const char *logFN = getenv("COSIM_DEBUG_FILE");
281 if (logFN != nullptr) {
282 printf("[cosim] Opening debug log: %s\n", logFN);
283 logFile = fopen(logFN, "w");
284 }
285
286 // Find the port and run.
287 printf("[cosim] Starting RPC server.\n");
288 server = std::make_unique<RpcServer>();
289 server->run(findPort());
290 }
291 return 0;
292}
293
294// ---- Manifest DPI entry points ----
295
296DPI void
298 const svOpenArrayHandle compressedManifest) {
299 if (server == nullptr)
301
302 if (validateSvOpenArray(compressedManifest, sizeof(int8_t)) != 0) {
303 printf("ERROR: DPI-func=%s line=%d event=invalid-sv-array\n", __func__,
304 __LINE__);
305 return;
306 }
307
308 // Copy the message data into 'blob'.
309 int size = svSizeOfArray(compressedManifest);
310 std::vector<uint8_t> blob(size);
311 for (int i = 0; i < size; ++i) {
312 blob[size - i - 1] = *(char *)svGetArrElemPtr1(compressedManifest, i);
313 }
314 printf("[cosim] Setting manifest (esiVersion=%d, size=%d)\n", esiVersion,
315 size);
316 server->setManifest(esiVersion, blob);
317}
318
319// ---- Low-level cosim DPI entry points ----
320
321// TODO: These had the shit broken outta them in the gRPC conversion. We're not
322// actively using them at the moment, but they'll have to be revived again in
323// the future.
324
325static bool mmioRegistered = false;
327 if (mmioRegistered) {
328 printf("ERROR: DPI MMIO master already registered!");
329 return -1;
330 }
332 mmioRegistered = true;
333 return 0;
334}
335
336DPI int sv2cCosimserverMMIOReadTryGet(uint32_t *address) {
337 // assert(server);
338 // LowLevel *ll = server->getLowLevel();
339 // std::optional<int> reqAddress = ll->readReqs.pop();
340 // if (!reqAddress.has_value())
341 return -1;
342 // *address = reqAddress.value();
343 // ll->readsOutstanding++;
344 // return 0;
345}
346
347DPI void sv2cCosimserverMMIOReadRespond(uint32_t data, char error) {
348 assert(false && "unimplemented");
349 // assert(server);
350 // LowLevel *ll = server->getLowLevel();
351 // if (ll->readsOutstanding == 0) {
352 // printf("ERROR: More read responses than requests! Not queuing
353 // response.\n"); return;
354 // }
355 // ll->readsOutstanding--;
356 // ll->readResps.push(data, error);
357}
358
360 assert(false && "unimplemented");
361 // assert(server);
362 // LowLevel *ll = server->getLowLevel();
363 // if (ll->writesOutstanding == 0) {
364 // printf(
365 // "ERROR: More write responses than requests! Not queuing
366 // response.\n");
367 // return;
368 // }
369 // ll->writesOutstanding--;
370 // ll->writeResps.push(error);
371}
372
373DPI int sv2cCosimserverMMIOWriteTryGet(uint32_t *address, uint32_t *data) {
374 // assert(server);
375 // LowLevel *ll = server->getLowLevel();
376 // auto req = ll->writeReqs.pop();
377 // if (!req.has_value())
378 return -1;
379 // *address = req.value().first;
380 // *data = req.value().second;
381 // ll->writesOutstanding++;
382 // return 0;
383}
assert(baseType &&"element must be base type")
static std::unique_ptr< RpcServer > server
std::map< std::string, ReadChannelPort & > readPorts
static FILE * logFile
If non-null, log to this file. Protected by 'serverMutex`.
DPI void sv2cCosimserverMMIOWriteRespond(char error)
std::map< std::string, WriteChannelPort & > writePorts
static int findPort()
Get the TCP port on which to listen.
DPI void sv2cCosimserverSetManifest(int esiVersion, const svOpenArrayHandle compressedManifest)
Set the system zlib-compressed manifest.
DPI int sv2cCosimserverMMIORegister()
Register an MMIO module. Just checks that there is only one instantiated.
static bool mmioRegistered
static void log(char *epId, bool toClient, const MessageData &msg)
Emit the contents of 'msg' to the log file in hex.
DPI void sv2cCosimserverFinish()
Shutdown the RPC server.
static int validateSvOpenArray(const svOpenArrayHandle data, int expectedElemSize)
Check that an array is an array of bytes and has some size.
static std::mutex serverMutex
std::map< ReadChannelPort *, std::future< MessageData > > readFutures
DPI int sv2cCosimserverEpTryPut(char *endpointId, const svOpenArrayHandle data, int dataSize)
Send a message to a client.
DPI int sv2cCosimserverEpRegister(char *endpointId, char *fromHostTypeIdC, int fromHostTypeSize, char *toHostTypeIdC, int toHostTypeSize)
Register an endpoint.
DPI int sv2cCosimserverInit()
Start the server.
DPI int sv2cCosimserverEpTryGet(char *endpointId, const svOpenArrayHandle data, unsigned int *dataSize)
Try to get a message from a client.
DPI int sv2cCosimserverMMIOWriteTryGet(uint32_t *address, uint32_t *data)
Write MMIO function pair.
DPI int sv2cCosimserverMMIOReadTryGet(uint32_t *address)
Read MMIO function pair.
DPI void sv2cCosimserverMMIOReadRespond(uint32_t data, char error)
int svSizeOfArray(const svOpenArrayHandle)
void * svGetArrElemPtr1(const svOpenArrayHandle, int indx1)
int svSize(const svOpenArrayHandle h, int d)
void * svGetArrayPtr(const svOpenArrayHandle)
int svDimensions(const svOpenArrayHandle h)
A logical chunk of data representing serialized data.
Definition Common.h:103
const uint8_t * getBytes() const
Definition Common.h:111
size_t getSize() const
Get the size of the data in bytes.
Definition Common.h:113
A ChannelPort which reads data from the accelerator.
Definition Ports.h:103
virtual std::future< MessageData > readAsync()
Asynchronous read.
Definition Ports.cpp:77
A ChannelPort which sends data to the accelerator.
Definition Ports.h:74
virtual void write(const MessageData &)=0
A very basic blocking write API.
#define DPI
Definition dpi.h:23
Definition esi.py:1
XXTERN typedef void * svOpenArrayHandle
Definition svdpi.h:133