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 "probe_runner.h"
4
5#include "esi/Accelerator.h"
6#include "esi/Manifest.h"
7#include "esi/Services.h"
8#include "esi/TypedPorts.h"
9
10#include <cstdint>
11#include <iostream>
12#include <random>
13#include <stdexcept>
14
15using namespace esi;
16
17static int runLoopbackI8(Accelerator *accel) {
18 AppIDPath lastLookup;
19 BundlePort *inPort = accel->resolvePort(
20 {AppID("loopback_inst", 0), AppID("loopback_tohw")}, lastLookup);
21 if (!inPort)
22 throw std::runtime_error("No loopback_tohw port found");
23 BundlePort *outPort = accel->resolvePort(
24 {AppID("loopback_inst", 0), AppID("loopback_fromhw")}, lastLookup);
25 if (!outPort)
26 throw std::runtime_error("No loopback_fromhw port found");
27
28 WriteChannelPort &toHw = inPort->getRawWrite("recv");
29 ReadChannelPort &fromHw = outPort->getRawRead("send");
30 toHw.connect();
31 fromHw.connect();
32
33 uint8_t sendVal = 0x5a;
34 toHw.write(MessageData(&sendVal, sizeof(sendVal)));
35
36 MessageData recvMsg;
37 fromHw.read(recvMsg);
38 if (recvMsg.getSize() != sizeof(uint8_t))
39 throw std::runtime_error("Unexpected loopback recv size");
40 uint8_t got = *recvMsg.as<uint8_t>();
41 if (got != sendVal)
42 throw std::runtime_error("Loopback byte mismatch");
43
44 std::cout << "loopback_i8 ok: 0x" << std::hex << (int)got << std::dec << "\n";
45 return 0;
46}
47
48static int runStructFunc(Accelerator *accel) {
49 AppIDPath lastLookup;
50 BundlePort *port = accel->resolvePort({AppID("structFunc")}, lastLookup);
51 if (!port)
52 throw std::runtime_error("No structFunc port found");
53
54 auto *func = port->getAs<services::FuncService::Function>();
55 if (!func)
56 throw std::runtime_error("structFunc not a FuncService::Function");
57 func->connect();
58
59 esi_system::ArgStruct arg{};
60 arg.a = 0x1234;
61 arg.b = static_cast<int8_t>(-7);
62
63 MessageData resMsg = func->call(MessageData::from(arg)).get();
64 const auto *res = resMsg.as<esi_system::ResultStruct>();
65
66 int8_t expectedX = static_cast<int8_t>(arg.b + 1);
67 if (res->x != expectedX || res->y != arg.b)
68 throw std::runtime_error("Struct func result mismatch");
69
70 std::cout << "struct_func ok: b=" << (int)arg.b << " x=" << (int)res->x
71 << " y=" << (int)res->y << "\n";
72 return 0;
73}
74
75static int runOddStructFunc(Accelerator *accel) {
76 AppIDPath lastLookup;
77 BundlePort *port = accel->resolvePort({AppID("oddStructFunc")}, lastLookup);
78 if (!port)
79 throw std::runtime_error("No oddStructFunc port found");
80
81 auto *func = port->getAs<services::FuncService::Function>();
82 if (!func)
83 throw std::runtime_error("oddStructFunc not a FuncService::Function");
84 func->connect();
85
86 esi_system::OddStruct arg{};
87 arg.a = 0xabc;
88 arg.b = static_cast<int8_t>(-17);
89 arg.inner.p = 5;
90 arg.inner.q = static_cast<int8_t>(-7);
91 arg.inner.r[0] = 3;
92 arg.inner.r[1] = 4;
93
94 MessageData resMsg = func->call(MessageData::from(arg)).get();
95 const auto *res = resMsg.as<esi_system::OddStruct>();
96
97 uint16_t expectA = static_cast<uint16_t>(arg.a + 1);
98 int8_t expectB = static_cast<int8_t>(arg.b - 3);
99 uint8_t expectP = static_cast<uint8_t>(arg.inner.p + 5);
100 int8_t expectQ = static_cast<int8_t>(arg.inner.q + 2);
101 uint8_t expectR0 = static_cast<uint8_t>(arg.inner.r[0] + 1);
102 uint8_t expectR1 = static_cast<uint8_t>(arg.inner.r[1] + 2);
103 if (res->a != expectA || res->b != expectB || res->inner.p != expectP ||
104 res->inner.q != expectQ || res->inner.r[0] != expectR0 ||
105 res->inner.r[1] != expectR1)
106 throw std::runtime_error("Odd struct func result mismatch");
107
108 std::cout << "odd_struct_func ok: a=" << res->a << " b=" << (int)res->b
109 << " p=" << (int)res->inner.p << " q=" << (int)res->inner.q
110 << " r0=" << (int)res->inner.r[0] << " r1=" << (int)res->inner.r[1]
111 << "\n";
112 return 0;
113}
114
115static int runArrayFunc(Accelerator *accel) {
116 AppIDPath lastLookup;
117 BundlePort *port = accel->resolvePort({AppID("arrayFunc")}, lastLookup);
118 if (!port)
119 throw std::runtime_error("No arrayFunc port found");
120
121 auto *func = port->getAs<services::FuncService::Function>();
122 if (!func)
123 throw std::runtime_error("arrayFunc not a FuncService::Function");
124 func->connect();
125
126 int8_t argArray[1] = {static_cast<int8_t>(-3)};
127 MessageData resMsg =
128 func->call(MessageData(reinterpret_cast<const uint8_t *>(argArray),
129 sizeof(argArray)))
130 .get();
131
132 const auto *res = resMsg.as<esi_system::ResultArray>();
133 int8_t a = (*res)[0];
134 int8_t b = (*res)[1];
135 int8_t expect0 = argArray[0];
136 int8_t expect1 = static_cast<int8_t>(argArray[0] + 1);
137
138 bool ok = (a == expect0 && b == expect1) || (a == expect1 && b == expect0);
139 if (!ok)
140 throw std::runtime_error("Array func result mismatch");
141
142 int8_t low = a;
143 int8_t high = b;
144 if (low > high) {
145 int8_t tmp = low;
146 low = high;
147 high = tmp;
148 }
149 std::cout << "array_func ok: " << (int)low << " " << (int)high << "\n";
150 return 0;
151}
152
153//
154// SerialCoordTranslator test
155//
156
157struct Coord {
158 uint32_t y; // SV ordering: last declared field first in memory
159 uint32_t x;
160};
161#pragma pack(push, 1)
162struct SerialCoordHeader {
163 uint16_t coordsCount;
164 uint32_t yTranslation;
165 uint32_t xTranslation;
166};
167static_assert(sizeof(SerialCoordHeader) == 10, "Size mismatch");
168struct SerialCoordData {
169 SerialCoordData(uint32_t x, uint32_t y) : _pad_head(0), y(y), x(x) {}
170 uint16_t _pad_head;
171 uint32_t y;
172 uint32_t x;
173};
174static_assert(sizeof(SerialCoordData) == sizeof(SerialCoordHeader),
175 "Size mismatch");
176#pragma pack(pop)
177
178// Note: this application is intended to test hardware. As such, we need
179// to be able to send batches. So this is not the typical way one would define a
180// message struct. It's closer to a streaming style.
182private:
184 std::vector<SerialCoordData> coords;
186
187public:
188 SerialCoordInput(uint32_t xTrans, uint32_t yTrans,
189 std::vector<SerialCoordData> &coords)
190 : coords(coords) {
191 header.coordsCount = (uint16_t)coords.size();
192 header.xTranslation = xTrans;
193 header.yTranslation = yTrans;
195 }
196
197 size_t numSegments() const override { return 3; }
198 Segment segment(size_t idx) const override {
199 if (idx == 0)
200 return {reinterpret_cast<const uint8_t *>(&header), sizeof(header)};
201 else if (idx == 1)
202 return {reinterpret_cast<const uint8_t *>(coords.data()),
203 coords.size() * sizeof(SerialCoordData)};
204 else if (idx == 2)
205 return {reinterpret_cast<const uint8_t *>(&footer), sizeof(footer)};
206 else
207 throw std::out_of_range("SerialCoordInput: invalid segment index");
208 }
209};
210
211#pragma pack(push, 1)
213 uint8_t _pad[6];
214 uint16_t coordsCount;
215};
217 uint32_t y;
218 uint32_t x;
219};
223};
224#pragma pack(pop)
225static_assert(sizeof(SerialCoordOutputFrame) == 8, "Size mismatch");
226
228 size_t numCoords = 100;
229 uint32_t xTrans = 10, yTrans = 20;
230
231 // Generate random coordinates.
232 std::mt19937 rng(0xDEADBEEF);
233 std::uniform_int_distribution<uint32_t> dist(0, 1000000);
234 std::vector<Coord> inputCoords;
235 inputCoords.reserve(numCoords);
236 for (uint32_t i = 0; i < numCoords; ++i)
237 inputCoords.push_back({dist(rng), dist(rng)});
238
239 auto child = accel->getChildren().find(AppID("coord_translator_serial"));
240 if (child == accel->getChildren().end())
241 throw std::runtime_error("Serial coord translate test: no "
242 "'coord_translator_serial' child found");
243
244 auto &ports = child->second->getPorts();
245 auto portIter = ports.find(AppID("translate_coords_serial"));
246 if (portIter == ports.end())
247 throw std::runtime_error(
248 "Serial coord translate test: no 'translate_coords_serial' port found");
249
250 auto *func = portIter->second.getAs<services::FuncService::Function>();
251 if (!func)
252 throw std::runtime_error(
253 "Serial coord translate test: port is not a FuncService::Function");
254
255 // Drive the raw arg port through TypedWritePort so the segmented batch is
256 // packed correctly; drain the raw result port directly. We bypass
257 // TypedFunction here because `SerialCoordOutputFrame` is a hand-written
258 // union (not a real ESI type) and we don't want the typed result decoder
259 // to backpressure on the N+2 reply frames.
260 TypedWritePort<SerialCoordInput, /*SkipTypeCheck=*/true> argPort(
261 func->getRawWrite("arg"));
262 ChannelPort::ConnectOptions argOpts(/*bufferSize=*/std::nullopt,
263 /*translateMessage=*/false);
264 argPort.connect(argOpts);
265 ReadChannelPort &rawResult = func->getRawRead("result");
266 // The reply contains numCoords + 2 raw frames (one header + N data + one
267 // footer). Use an unbounded buffer (bufferSize=0) so the polling queue
268 // doesn't backpressure the backend mid-stream.
269 ChannelPort::ConnectOptions resultOpts(/*bufferSize=*/0,
270 /*translateMessage=*/false);
271 rawResult.connect(resultOpts);
272
273 std::vector<SerialCoordData> coords;
274 for (auto &c : inputCoords)
275 coords.emplace_back(c.x, c.y);
276 SerialCoordInput batch(xTrans, yTrans, coords);
277 argPort.write(batch);
278
279 // The bulk-list reply is one header frame + numCoords data frames + one
280 // footer frame. TODO: list results aren't decoded here; we just drain the
281 // frames so the backend doesn't backpressure / hang.
282 MessageData drained;
283 for (size_t i = 0; i < numCoords + 2; ++i)
284 rawResult.read(drained);
285 std::cout << "serial_coord_translate ok\n";
286 return 0;
287}
288
290 std::cout << "depth: 0x" << std::hex << esi_system::LoopbackIP::depth
291 << std::dec << "\n";
292 std::cout << "depth_constant ok\n";
293 return 0;
294}
295
296ESI_PROBE_REGISTRY("loopback-cpp",
297 "Loopback cosim test using generated ESI headers.",
298 {"depth_constant", &runDepthConstant},
299 {"loopback_i8", &runLoopbackI8},
300 {"struct_func", &runStructFunc},
301 {"odd_struct_func", &runOddStructFunc},
302 {"array_func", &runArrayFunc},
303 {"serial_coord_translate", &serialCoordTranslateTest}, );
Top level accelerator class.
Definition Accelerator.h:70
Services provide connections to 'bundles' – collections of named, unidirectional communication channe...
Definition Ports.h:611
T * getAs() const
Cast this Bundle port to a subclass which is actually useful.
Definition Ports.h:639
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
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
A concrete flat message backed by a single vector of bytes.
Definition Common.h:155
const T * as() const
Cast to a type.
Definition Common.h:190
size_t getSize() const
Get the size of the data in bytes.
Definition Common.h:180
static MessageData from(T &t)
Cast from a type to its raw bytes.
Definition Common.h:200
A ChannelPort which reads data from the accelerator.
Definition Ports.h:453
virtual void connect(ReadCallback callback, const ConnectOptions &options={})
Definition Ports.cpp:140
virtual void read(MessageData &outData)
Specify a buffer to read into.
Definition Ports.h:517
Abstract multi-segment message.
Definition Common.h:133
void connect(const ChannelPort::ConnectOptions &opts={std::nullopt, false})
Definition TypedPorts.h:626
void write(const T &data)
Definition TypedPorts.h:636
A ChannelPort which sends data to the accelerator.
Definition Ports.h:308
void write(const MessageData &data)
A very basic blocking write API.
Definition Ports.h:327
virtual void connect(const ConnectOptions &options={}) override
Set up a connection to the accelerator.
Definition Ports.h:312
A function call which gets attached to a service port.
Definition Services.h:353
static int serialCoordTranslateTest(Accelerator *accel)
Definition loopback.cpp:227
static int runDepthConstant(Accelerator *)
Definition loopback.cpp:289
static int runArrayFunc(Accelerator *accel)
Definition loopback.cpp:115
static int runOddStructFunc(Accelerator *accel)
Definition loopback.cpp:75
static int runStructFunc(Accelerator *accel)
Definition loopback.cpp:48
static int runLoopbackI8(Accelerator *accel)
Definition loopback.cpp:17
Definition esi.py:1
#define ESI_PROBE_REGISTRY(name, description,...)
Convenience macro: defines main() with a probe registry.
Test the CoordTranslator module using message translation.
uint32_t x
uint32_t y
SerialCoordData(uint32_t x, uint32_t y)
Definition loopback.cpp:169
size_t numSegments() const override
Number of segments in the message.
Definition loopback.cpp:197
SerialCoordHeader header
SerialCoordHeader footer
Segment segment(size_t idx) const override
Get a segment by index.
Definition loopback.cpp:198
SerialCoordInput(uint32_t xTrans, uint32_t yTrans, std::vector< SerialCoordData > &coords)
Definition loopback.cpp:188
std::vector< SerialCoordData > coords
A contiguous, non-owning view of bytes within a SegmentedMessageData.
Definition Common.h:118
size_t size
Definition Common.h:120
SerialCoordOutputData data
SerialCoordOutputHeader header