CIRCT  19.0.0git
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 "cosim/Server.h"
20 #include "cosim/dpi.h"
21 
22 #include <algorithm>
23 #include <cassert>
24 #include <cstdlib>
25 
26 using namespace esi::cosim;
27 
28 /// If non-null, log to this file. Protected by 'serverMutex`.
29 static FILE *logFile;
30 static RpcServer *server = nullptr;
31 static std::mutex serverMutex;
32 
33 // ---- Helper functions ----
34 
35 /// Emit the contents of 'msg' to the log file in hex.
36 static void log(char *epId, bool toClient, const Endpoint::BlobPtr &msg) {
37  std::lock_guard<std::mutex> g(serverMutex);
38  if (!logFile)
39  return;
40 
41  fprintf(logFile, "[ep: %50s to: %4s]", epId, toClient ? "host" : "sim");
42  size_t msgSize = msg->size();
43  for (size_t i = 0; i < msgSize; ++i) {
44  auto b = (*msg)[i];
45  // Separate 32-bit words.
46  if (i % 4 == 0 && i > 0)
47  fprintf(logFile, " ");
48  // Separate 64-bit words (capnp word size)
49  if (i % 8 == 0 && i > 0)
50  fprintf(logFile, " ");
51  fprintf(logFile, " %02x", b);
52  }
53  fprintf(logFile, "\n");
54  fflush(logFile);
55 }
56 
57 /// Get the TCP port on which to listen. If the port isn't specified via an
58 /// environment variable, return 0 to allow automatic selection.
59 static int findPort() {
60  const char *portEnv = getenv("COSIM_PORT");
61  if (portEnv == nullptr) {
62  printf("[COSIM] RPC server port not found. Letting CapnpRPC select one\n");
63  return 0;
64  }
65  printf("[COSIM] Opening RPC server on port %s\n", portEnv);
66  return std::strtoull(portEnv, nullptr, 10);
67 }
68 
69 /// Check that an array is an array of bytes and has some size.
70 // NOLINTNEXTLINE(misc-misplaced-const)
71 static int validateSvOpenArray(const svOpenArrayHandle data,
72  int expectedElemSize) {
73  if (svDimensions(data) != 1) {
74  printf("DPI-C: ERROR passed array argument that doesn't have expected 1D "
75  "dimensions\n");
76  return -1;
77  }
78  if (svGetArrayPtr(data) == NULL) {
79  printf("DPI-C: ERROR passed array argument that doesn't have C layout "
80  "(ptr==NULL)\n");
81  return -2;
82  }
83  int totalBytes = svSizeOfArray(data);
84  if (totalBytes == 0) {
85  printf("DPI-C: ERROR passed array argument that doesn't have C layout "
86  "(total_bytes==0)\n");
87  return -3;
88  }
89  int numElems = svSize(data, 1);
90  int elemSize = numElems == 0 ? 0 : (totalBytes / numElems);
91  if (numElems * expectedElemSize != totalBytes) {
92  printf("DPI-C: ERROR: passed array argument that doesn't have expected "
93  "element-size: expected=%d actual=%d numElems=%d totalBytes=%d\n",
94  expectedElemSize, elemSize, numElems, totalBytes);
95  return -4;
96  }
97  return 0;
98 }
99 
100 // ---- Traditional cosim DPI entry points ----
101 
102 // Register simulated device endpoints.
103 // - return 0 on success, non-zero on failure (duplicate EP registered).
104 DPI int sv2cCosimserverEpRegister(char *endpointId, char *fromHostTypeId,
105  int fromHostTypeSize, char *toHostTypeId,
106  int toHostTypeSize) {
107  // Ensure the server has been constructed.
109  // Then register with it.
110  if (server->endpoints.registerEndpoint(endpointId, fromHostTypeId,
111  fromHostTypeSize, toHostTypeId,
112  toHostTypeSize))
113  return 0;
114  return -1;
115 }
116 
117 // Attempt to recieve data from a client.
118 // - Returns negative when call failed (e.g. EP not registered).
119 // - If no message, return 0 with dataSize == 0.
120 // - Assumes buffer is large enough to contain entire message. Fails if not
121 // large enough. (In the future, will add support for getting the message
122 // into a fixed-size buffer over multiple calls.)
123 DPI int sv2cCosimserverEpTryGet(char *endpointId,
124  // NOLINTNEXTLINE(misc-misplaced-const)
125  const svOpenArrayHandle data,
126  unsigned int *dataSize) {
127  if (server == nullptr)
128  return -1;
129 
130  Endpoint *ep = server->endpoints[endpointId];
131  if (!ep) {
132  fprintf(stderr, "Endpoint not found in registry!\n");
133  return -4;
134  }
135 
136  Endpoint::BlobPtr msg;
137  // Poll for a message.
138  if (!ep->getMessageToSim(msg)) {
139  // No message.
140  *dataSize = 0;
141  return 0;
142  }
143  // Do the validation only if there's a message available. Since the
144  // simulator is going to poll up to every tick and there's not going to be
145  // a message most of the time, this is important for performance.
146 
147  log(endpointId, false, msg);
148 
149  if (validateSvOpenArray(data, sizeof(int8_t)) != 0) {
150  printf("ERROR: DPI-func=%s line=%d event=invalid-sv-array\n", __func__,
151  __LINE__);
152  return -2;
153  }
154 
155  // Detect or verify size of buffer.
156  if (*dataSize == ~0u) {
157  *dataSize = svSizeOfArray(data);
158  } else if (*dataSize > (unsigned)svSizeOfArray(data)) {
159  printf("ERROR: DPI-func=%s line %d event=invalid-size (max %d)\n", __func__,
160  __LINE__, (unsigned)svSizeOfArray(data));
161  return -3;
162  }
163  // Verify it'll fit.
164  size_t msgSize = msg->size();
165  if (msgSize > *dataSize) {
166  printf("ERROR: Message size too big to fit in HW buffer\n");
167  return -5;
168  }
169 
170  // Copy the message data.
171  size_t i;
172  for (i = 0; i < msgSize; ++i) {
173  auto b = (*msg)[i];
174  *(char *)svGetArrElemPtr1(data, i) = b;
175  }
176  // Zero out the rest of the buffer.
177  for (; i < *dataSize; ++i) {
178  *(char *)svGetArrElemPtr1(data, i) = 0;
179  }
180  // Set the output data size.
181  *dataSize = msg->size();
182  return 0;
183 }
184 
185 // Attempt to send data to a client.
186 // - return 0 on success, negative on failure (unregistered EP).
187 // - if dataSize is negative, attempt to dynamically determine the size of
188 // 'data'.
189 DPI int sv2cCosimserverEpTryPut(char *endpointId,
190  // NOLINTNEXTLINE(misc-misplaced-const)
191  const svOpenArrayHandle data, int dataSize) {
192  if (server == nullptr)
193  return -1;
194 
195  if (validateSvOpenArray(data, sizeof(int8_t)) != 0) {
196  printf("ERROR: DPI-func=%s line=%d event=invalid-sv-array\n", __func__,
197  __LINE__);
198  return -2;
199  }
200 
201  // Detect or verify size.
202  if (dataSize < 0) {
203  dataSize = svSizeOfArray(data);
204  } else if (dataSize > svSizeOfArray(data)) { // not enough data
205  printf("ERROR: DPI-func=%s line %d event=invalid-size limit %d array %d\n",
206  __func__, __LINE__, dataSize, svSizeOfArray(data));
207  return -3;
208  }
209 
210  Endpoint::BlobPtr blob = std::make_unique<Endpoint::Blob>(dataSize);
211  // Copy the message data into 'blob'.
212  for (int i = 0; i < dataSize; ++i) {
213  (*blob)[i] = *(char *)svGetArrElemPtr1(data, i);
214  }
215  // Queue the blob.
216  Endpoint *ep = server->endpoints[endpointId];
217  if (!ep) {
218  fprintf(stderr, "Endpoint not found in registry!\n");
219  return -4;
220  }
221  log(endpointId, true, blob);
222  ep->pushMessageToClient(std::move(blob));
223  return 0;
224 }
225 
226 // Teardown cosimserver (disconnects from primary server port, stops connections
227 // from active clients).
229  std::lock_guard<std::mutex> g(serverMutex);
230  printf("[cosim] Tearing down RPC server.\n");
231  if (server != nullptr) {
232  server->stop();
233  server = nullptr;
234 
235  fclose(logFile);
236  logFile = nullptr;
237  }
238 }
239 
240 // Start cosimserver (spawns server for HW-initiated work, listens for
241 // connections from new SW-clients).
243  std::lock_guard<std::mutex> g(serverMutex);
244  if (server == nullptr) {
245  // Open log file if requested.
246  const char *logFN = getenv("COSIM_DEBUG_FILE");
247  if (logFN != nullptr) {
248  printf("[cosim] Opening debug log: %s\n", logFN);
249  logFile = fopen(logFN, "w");
250  }
251 
252  // Find the port and run.
253  printf("[cosim] Starting RPC server.\n");
254  server = new RpcServer();
255  server->run(findPort());
256  }
257  return 0;
258 }
259 
260 // ---- Manifest DPI entry points ----
261 
262 DPI void
263 sv2cCosimserverSetManifest(unsigned int esiVersion,
264  const svOpenArrayHandle compressedManifest) {
265  if (server == nullptr)
267 
268  if (validateSvOpenArray(compressedManifest, sizeof(int8_t)) != 0) {
269  printf("ERROR: DPI-func=%s line=%d event=invalid-sv-array\n", __func__,
270  __LINE__);
271  return;
272  }
273 
274  // Copy the message data into 'blob'.
275  int size = svSizeOfArray(compressedManifest);
276  std::vector<uint8_t> blob(size);
277  for (int i = 0; i < size; ++i) {
278  blob[size - i - 1] = *(char *)svGetArrElemPtr1(compressedManifest, i);
279  }
280  printf("[cosim] Setting manifest (esiVersion=%d, size=%d)\n", esiVersion,
281  size);
282  server->setManifest(esiVersion, blob);
283 }
284 
285 // ---- Low-level cosim DPI entry points ----
286 
287 static bool mmioRegistered = false;
289  if (mmioRegistered) {
290  printf("ERROR: DPI MMIO master already registered!");
291  return -1;
292  }
294  mmioRegistered = true;
295  return 0;
296 }
297 
298 DPI int sv2cCosimserverMMIOReadTryGet(uint32_t *address) {
299  assert(server);
300  std::optional<int> reqAddress = server->lowLevelBridge.readReqs.pop();
301  if (!reqAddress.has_value())
302  return -1;
303  *address = reqAddress.value();
305  return 0;
306 }
307 
308 DPI void sv2cCosimserverMMIOReadRespond(uint32_t data, char error) {
309  assert(server);
311  printf("ERROR: More read responses than requests! Not queuing response.\n");
312  return;
313  }
315  server->lowLevelBridge.readResps.push(data, error);
316 }
317 
319  assert(server);
321  printf(
322  "ERROR: More write responses than requests! Not queuing response.\n");
323  return;
324  }
327 }
328 
329 DPI int sv2cCosimserverMMIOWriteTryGet(uint32_t *address, uint32_t *data) {
330  assert(server);
331  auto req = server->lowLevelBridge.writeReqs.pop();
332  if (!req.has_value())
333  return -1;
334  *address = req.value().first;
335  *data = req.value().second;
337  return 0;
338 }
assert(baseType &&"element must be base type")
static FILE * logFile
If non-null, log to this file. Protected by 'serverMutex`.
DPI void sv2cCosimserverMMIOWriteRespond(char error)
static int findPort()
Get the TCP port on which to listen.
DPI int sv2cCosimserverMMIORegister()
Register an MMIO module. Just checks that there is only one instantiated.
static bool mmioRegistered
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 RpcServer * server
DPI int sv2cCosimserverEpRegister(char *endpointId, char *fromHostTypeId, int fromHostTypeSize, char *toHostTypeId, int toHostTypeSize)
Register an endpoint.
static void log(char *epId, bool toClient, const Endpoint::BlobPtr &msg)
Emit the contents of 'msg' to the log file in hex.
static std::mutex serverMutex
DPI int sv2cCosimserverEpTryPut(char *endpointId, const svOpenArrayHandle data, int dataSize)
Send a message to a client.
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)
DPI void sv2cCosimserverSetManifest(unsigned int esiVersion, const svOpenArrayHandle compressedManifest)
Set the system zlib-compressed manifest.
bool registerEndpoint(std::string epId, std::string fromHostTypeId, int sendTypeMaxSize, std::string toHostTypeId, int recvTypeMaxSize)
Register an Endpoint.
Definition: Endpoint.cpp:38
Implements a bi-directional, thread-safe bridge between the RPC server and DPI functions.
Definition: Endpoint.h:33
void pushMessageToClient(BlobPtr msg)
Queue message to the RPC client.
Definition: Endpoint.h:75
std::unique_ptr< Blob > BlobPtr
Definition: Endpoint.h:39
bool getMessageToSim(BlobPtr &msg)
Pop from the to-simulator queue.
Definition: Endpoint.h:65
TSQueue< uint32_t > readReqs
Definition: LowLevel.h:29
std::atomic< unsigned > writesOutstanding
Definition: LowLevel.h:35
TSQueue< std::pair< uint64_t, uint8_t > > readResps
Definition: LowLevel.h:30
TSQueue< uint8_t > writeResps
Definition: LowLevel.h:34
std::atomic< unsigned > readsOutstanding
Definition: LowLevel.h:31
TSQueue< std::pair< uint32_t, uint64_t > > writeReqs
Definition: LowLevel.h:33
The main RpcServer.
Definition: Server.h:30
void setManifest(unsigned int esiVersion, const std::vector< uint8_t > &manifest)
Definition: Server.h:42
EndpointRegistry endpoints
Definition: Server.h:32
void stop()
Signal the RPC server thread to stop. Wait for it to exit.
Definition: Server.cpp:299
void run(uint16_t port)
Start and stop the server thread.
Definition: Server.cpp:289
LowLevel lowLevelBridge
Definition: Server.h:33
void push(E... t)
Push onto the queue.
Definition: Utils.h:30
std::optional< T > pop()
Pop something off the queue but return nullopt if the queue is empty.
Definition: Utils.h:37
#define DPI
Definition: dpi.h:23
XXTERN int svSizeOfArray(const svOpenArrayHandle)
Definition: DummySvDpi.cpp:89
XXTERN void * svGetArrElemPtr1(const svOpenArrayHandle, int indx1)
Definition: DummySvDpi.cpp:97
XXTERN int svDimensions(const svOpenArrayHandle h)
Definition: DummySvDpi.cpp:81
XXTERN int svSize(const svOpenArrayHandle h, int d)
Definition: DummySvDpi.cpp:77
XXTERN void * svGetArrayPtr(const svOpenArrayHandle)
Definition: DummySvDpi.cpp:85
XXTERN typedef void * svOpenArrayHandle
Definition: svdpi.h:133