CIRCT 23.0.0git
Loading...
Searching...
No Matches
loopback.cpp
Go to the documentation of this file.
1#include "loopback/LoopbackIP.h"
2
3#include "esi/Accelerator.h"
4#include "esi/CLI.h"
5#include "esi/Manifest.h"
6#include "esi/Services.h"
7#include "esi/TypedPorts.h"
8
9#include <cstdint>
10#include <iostream>
11#include <random>
12#include <stdexcept>
13
14using namespace esi;
15
16static void runLoopbackI8(Accelerator *accel) {
17 AppIDPath lastLookup;
18 BundlePort *inPort = accel->resolvePort(
19 {AppID("loopback_inst", 0), AppID("loopback_tohw")}, lastLookup);
20 if (!inPort)
21 throw std::runtime_error("No loopback_tohw port found");
22 BundlePort *outPort = accel->resolvePort(
23 {AppID("loopback_inst", 0), AppID("loopback_fromhw")}, lastLookup);
24 if (!outPort)
25 throw std::runtime_error("No loopback_fromhw port found");
26
27 WriteChannelPort &toHw = inPort->getRawWrite("recv");
28 ReadChannelPort &fromHw = outPort->getRawRead("send");
29 toHw.connect();
30 fromHw.connect();
31
32 uint8_t sendVal = 0x5a;
33 toHw.write(MessageData(&sendVal, sizeof(sendVal)));
34
35 MessageData recvMsg;
36 fromHw.read(recvMsg);
37 if (recvMsg.getSize() != sizeof(uint8_t))
38 throw std::runtime_error("Unexpected loopback recv size");
39 uint8_t got = *recvMsg.as<uint8_t>();
40 if (got != sendVal)
41 throw std::runtime_error("Loopback byte mismatch");
42
43 std::cout << "loopback i8 ok: 0x" << std::hex << (int)got << std::dec << "\n";
44}
45
46static void runStructFunc(Accelerator *accel) {
47 AppIDPath lastLookup;
48 BundlePort *port = accel->resolvePort({AppID("structFunc")}, lastLookup);
49 if (!port)
50 throw std::runtime_error("No structFunc port found");
51
52 auto *func = port->getAs<services::FuncService::Function>();
53 if (!func)
54 throw std::runtime_error("structFunc not a FuncService::Function");
55 func->connect();
56
57 esi_system::ArgStruct arg{};
58 arg.a = 0x1234;
59 arg.b = static_cast<int8_t>(-7);
60
61 MessageData resMsg = func->call(MessageData::from(arg)).get();
62 const auto *res = resMsg.as<esi_system::ResultStruct>();
63
64 int8_t expectedX = static_cast<int8_t>(arg.b + 1);
65 if (res->x != expectedX || res->y != arg.b)
66 throw std::runtime_error("Struct func result mismatch");
67
68 std::cout << "struct func ok: b=" << (int)arg.b << " x=" << (int)res->x
69 << " y=" << (int)res->y << "\n";
70}
71
72static void runOddStructFunc(Accelerator *accel) {
73 AppIDPath lastLookup;
74 BundlePort *port = accel->resolvePort({AppID("oddStructFunc")}, lastLookup);
75 if (!port)
76 throw std::runtime_error("No oddStructFunc port found");
77
78 auto *func = port->getAs<services::FuncService::Function>();
79 if (!func)
80 throw std::runtime_error("oddStructFunc not a FuncService::Function");
81 func->connect();
82
83 esi_system::OddStruct arg{};
84 arg.a = 0xabc;
85 arg.b = static_cast<int8_t>(-17);
86 arg.inner.p = 5;
87 arg.inner.q = static_cast<int8_t>(-7);
88 arg.inner.r[0] = 3;
89 arg.inner.r[1] = 4;
90
91 MessageData resMsg = func->call(MessageData::from(arg)).get();
92 const auto *res = resMsg.as<esi_system::OddStruct>();
93
94 uint16_t expectA = static_cast<uint16_t>(arg.a + 1);
95 int8_t expectB = static_cast<int8_t>(arg.b - 3);
96 uint8_t expectP = static_cast<uint8_t>(arg.inner.p + 5);
97 int8_t expectQ = static_cast<int8_t>(arg.inner.q + 2);
98 uint8_t expectR0 = static_cast<uint8_t>(arg.inner.r[0] + 1);
99 uint8_t expectR1 = static_cast<uint8_t>(arg.inner.r[1] + 2);
100 if (res->a != expectA || res->b != expectB || res->inner.p != expectP ||
101 res->inner.q != expectQ || res->inner.r[0] != expectR0 ||
102 res->inner.r[1] != expectR1)
103 throw std::runtime_error("Odd struct func result mismatch");
104
105 std::cout << "odd struct func ok: a=" << res->a << " b=" << (int)res->b
106 << " p=" << (int)res->inner.p << " q=" << (int)res->inner.q
107 << " r0=" << (int)res->inner.r[0] << " r1=" << (int)res->inner.r[1]
108 << "\n";
109}
110
111static void runArrayFunc(Accelerator *accel) {
112 AppIDPath lastLookup;
113 BundlePort *port = accel->resolvePort({AppID("arrayFunc")}, lastLookup);
114 if (!port)
115 throw std::runtime_error("No arrayFunc port found");
116
117 auto *func = port->getAs<services::FuncService::Function>();
118 if (!func)
119 throw std::runtime_error("arrayFunc not a FuncService::Function");
120 func->connect();
121
122 int8_t argArray[1] = {static_cast<int8_t>(-3)};
123 MessageData resMsg =
124 func->call(MessageData(reinterpret_cast<const uint8_t *>(argArray),
125 sizeof(argArray)))
126 .get();
127
128 const auto *res = resMsg.as<esi_system::ResultArray>();
129 int8_t a = (*res)[0];
130 int8_t b = (*res)[1];
131 int8_t expect0 = argArray[0];
132 int8_t expect1 = static_cast<int8_t>(argArray[0] + 1);
133
134 bool ok = (a == expect0 && b == expect1) || (a == expect1 && b == expect0);
135 if (!ok)
136 throw std::runtime_error("Array func result mismatch");
137
138 int8_t low = a;
139 int8_t high = b;
140 if (low > high) {
141 int8_t tmp = low;
142 low = high;
143 high = tmp;
144 }
145 std::cout << "array func ok: " << (int)low << " " << (int)high << "\n";
146}
147
148//
149// SerialCoordTranslator test
150//
151
152struct Coord {
153 uint32_t y; // SV ordering: last declared field first in memory
154 uint32_t x;
155};
156#pragma pack(push, 1)
157struct SerialCoordHeader {
158 uint16_t coordsCount;
159 uint32_t yTranslation;
160 uint32_t xTranslation;
161};
162static_assert(sizeof(SerialCoordHeader) == 10, "Size mismatch");
163struct SerialCoordData {
164 SerialCoordData(uint32_t x, uint32_t y) : _pad_head(0), y(y), x(x) {}
165 uint16_t _pad_head;
166 uint32_t y;
167 uint32_t x;
168};
169static_assert(sizeof(SerialCoordData) == sizeof(SerialCoordHeader),
170 "Size mismatch");
171#pragma pack(pop)
172
173// Note: this application is intended to test hardware. As such, we need
174// to be able to send batches. So this is not the typical way one would define a
175// message struct. It's closer to a streaming style.
177private:
179 std::vector<SerialCoordData> coords;
181
182public:
183 SerialCoordInput(uint32_t xTrans, uint32_t yTrans,
184 std::vector<SerialCoordData> &coords)
185 : coords(coords) {
186 header.coordsCount = (uint16_t)coords.size();
187 header.xTranslation = xTrans;
188 header.yTranslation = yTrans;
190 }
191
192 size_t numSegments() const override { return 3; }
193 Segment segment(size_t idx) const override {
194 if (idx == 0)
195 return {reinterpret_cast<const uint8_t *>(&header), sizeof(header)};
196 else if (idx == 1)
197 return {reinterpret_cast<const uint8_t *>(coords.data()),
198 coords.size() * sizeof(SerialCoordData)};
199 else if (idx == 2)
200 return {reinterpret_cast<const uint8_t *>(&footer), sizeof(footer)};
201 else
202 throw std::out_of_range("SerialCoordInput: invalid segment index");
203 }
204};
205
206#pragma pack(push, 1)
208 uint8_t _pad[6];
209 uint16_t coordsCount;
210};
212 uint32_t y;
213 uint32_t x;
214};
218};
219#pragma pack(pop)
220static_assert(sizeof(SerialCoordOutputFrame) == 8, "Size mismatch");
221
223 size_t numCoords = 100;
224 uint32_t xTrans = 10, yTrans = 20;
225
226 // Generate random coordinates.
227 std::mt19937 rng(0xDEADBEEF);
228 std::uniform_int_distribution<uint32_t> dist(0, 1000000);
229 std::vector<Coord> inputCoords;
230 inputCoords.reserve(numCoords);
231 for (uint32_t i = 0; i < numCoords; ++i)
232 inputCoords.push_back({dist(rng), dist(rng)});
233
234 auto child = accel->getChildren().find(AppID("coord_translator_serial"));
235 if (child == accel->getChildren().end())
236 throw std::runtime_error("Serial coord translate test: no "
237 "'coord_translator_serial' child found");
238
239 auto &ports = child->second->getPorts();
240 auto portIter = ports.find(AppID("translate_coords_serial"));
241 if (portIter == ports.end())
242 throw std::runtime_error(
243 "Serial coord translate test: no 'translate_coords_serial' port found");
244
246 /*SkipTypeCheck=*/true>
247 translateCoords(
248 portIter->second.getAs<services::FuncService::Function>());
249
250 translateCoords.connect();
251
252 std::vector<SerialCoordData> coords;
253 for (auto &c : inputCoords)
254 coords.emplace_back(c.x, c.y);
255 SerialCoordInput batch(xTrans, yTrans, coords);
256 // TODO: List results are currently not supported so we cannot compare the
257 // results.
258 (void)translateCoords.call(batch).get();
259}
260
261int main(int argc, const char *argv[]) {
262 CliParser cli("loopback-cpp");
263 cli.description("Loopback cosim test using generated ESI headers.");
264 if (int rc = cli.esiParse(argc, argv))
265 return rc;
266 if (!cli.get_help_ptr()->empty())
267 return 0;
268
269 Context &ctxt = cli.getContext();
270 AcceleratorConnection *conn = cli.connect();
271 try {
272 const auto &info = *conn->getService<services::SysInfo>();
273 Manifest manifest(ctxt, info.getJsonManifest());
274 Accelerator *accel = manifest.buildAccelerator(*conn);
275 conn->getServiceThread()->addPoll(*accel);
276
277 std::cout << "depth: 0x" << std::hex << esi_system::LoopbackIP::depth
278 << std::dec << "\n";
279
280 runLoopbackI8(accel);
281 runStructFunc(accel);
282 runOddStructFunc(accel);
283 runArrayFunc(accel);
285
286 conn->disconnect();
287 } catch (std::exception &e) {
288 ctxt.getLogger().error("loopback-cpp", e.what());
289 conn->disconnect();
290 return 1;
291 }
292
293 return 0;
294}
Abstract class representing a connection to an accelerator.
Definition Accelerator.h:89
Top level accelerator class.
Definition Accelerator.h:70
Services provide connections to 'bundles' – collections of named, unidirectional communication channe...
Definition Ports.h:456
T * getAs() const
Cast this Bundle port to a subclass which is actually useful.
Definition Ports.h:484
ReadChannelPort & getRawRead(const std::string &name) const
Definition Ports.cpp:52
WriteChannelPort & getRawWrite(const std::string &name) const
Get access to the raw byte streams of a channel.
Definition Ports.cpp:42
Common options and code for ESI runtime tools.
Definition CLI.h:29
Context & getContext()
Get the context.
Definition CLI.h:69
AcceleratorConnection * connect()
Connect to the accelerator using the specified backend and connection.
Definition CLI.h:66
int esiParse(int argc, const char **argv)
Run the parser.
Definition CLI.h:52
AcceleratorConnections, Accelerators, and Manifests must all share a context.
Definition Context.h:34
Logger & getLogger()
Definition Context.h:69
BundlePort * resolvePort(const AppIDPath &path, AppIDPath &lastLookup) const
Attempt to resolve a path to a port.
Definition Design.cpp:72
const std::map< AppID, Instance * > & getChildren() const
Access the module's children by ID.
Definition Design.h:71
virtual void error(const std::string &subsystem, const std::string &msg, const std::map< std::string, std::any > *details=nullptr)
Report an error.
Definition Logging.h:64
Class to parse a manifest.
Definition Manifest.h:39
A logical chunk of data representing serialized data.
Definition Common.h:113
const T * as() const
Cast to a type.
Definition Common.h:148
size_t getSize() const
Get the size of the data in bytes.
Definition Common.h:138
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
virtual void connect(std::function< bool(MessageData)> callback, const ConnectOptions &options={})
Definition Ports.cpp:69
virtual void read(MessageData &outData)
Specify a buffer to read into.
Definition Ports.h:381
Abstract multi-segment message.
Definition Common.h:190
std::future< ResultT > call(const ArgT &arg)
Definition TypedPorts.h:444
A ChannelPort which sends data to the accelerator.
Definition Ports.h:215
void write(const MessageData &data)
A very basic blocking write API.
Definition Ports.h:231
virtual void connect(const ConnectOptions &options={}) override
Set up a connection to the accelerator.
Definition Ports.h:219
A function call which gets attached to a service port.
Definition Services.h:353
Information about the Accelerator system.
Definition Services.h:113
static void runOddStructFunc(Accelerator *accel)
Definition loopback.cpp:72
int main(int argc, const char *argv[])
Definition loopback.cpp:261
static void serialCoordTranslateTest(Accelerator *accel)
Definition loopback.cpp:222
static void runLoopbackI8(Accelerator *accel)
Definition loopback.cpp:16
static void runStructFunc(Accelerator *accel)
Definition loopback.cpp:46
static void runArrayFunc(Accelerator *accel)
Definition loopback.cpp:111
Definition esi.py:1
Test the CoordTranslator module using message translation.
uint32_t x
uint32_t y
SerialCoordData(uint32_t x, uint32_t y)
Definition loopback.cpp:164
size_t numSegments() const override
Number of segments in the message.
Definition loopback.cpp:192
SerialCoordHeader header
SerialCoordHeader footer
Definition loopback.cpp:180
Segment segment(size_t idx) const override
Get a segment by index.
Definition loopback.cpp:193
SerialCoordInput(uint32_t xTrans, uint32_t yTrans, std::vector< SerialCoordData > &coords)
Definition loopback.cpp:183
std::vector< SerialCoordData > coords
A contiguous, non-owning view of bytes within a SegmentedMessageData.
Definition Common.h:175
size_t size
Definition Common.h:177
SerialCoordOutputData data
SerialCoordOutputHeader header