CIRCT 23.0.0git
Loading...
Searching...
No Matches
test_codegen.cpp
Go to the documentation of this file.
1// Driver for the codegen / port-kind coverage integration test. Each probe
2// below targets exactly one combination of port kind (function / callback /
3// channel / MMIO / metric) and codegen path (typed scalar / typed struct /
4// void specialization / indexed group). Each probe is small and self-checking
5// so that a runtime regression in any one path lights up exactly one probe.
6//
7// The binary supports a ``--probe NAME`` flag that runs only one probe; the
8// pytest harness uses this to surface each probe as a separate pytest test.
9// With no ``--probe`` flag, every probe runs in sequence.
10
11#include "test_codegen/CallServiceCallback.h"
12#include "test_codegen/CallbackWindowedList.h"
13#include "test_codegen/ChannelWindowedListRead.h"
14#include "test_codegen/ChannelWindowedListWrite.h"
15#include "test_codegen/CustomServiceDeclChannel.h"
16#include "test_codegen/IndexedFuncGroup.h"
17#include "test_codegen/MmioReadWrite.h"
18#include "test_codegen/TelemetryMetric.h"
19#include "test_codegen/TypedFuncArrayResult.h"
20#include "test_codegen/TypedFuncMultiArg.h"
21#include "test_codegen/TypedFuncNestedStruct.h"
22#include "test_codegen/TypedFuncStruct.h"
23#include "test_codegen/TypedFuncSubByteSigned.h"
24#include "test_codegen/TypedFuncVoidArg.h"
25#include "test_codegen/TypedFuncVoidResult.h"
26#include "test_codegen/TypedFuncWindowedList.h"
27#include "test_codegen/TypedReadChannelStruct.h"
28#include "test_codegen/TypedWriteChannelByte.h"
29
30#include "probe_runner.h"
31
32#include "esi/Accelerator.h"
33#include "esi/Manifest.h"
34#include "esi/Services.h"
35#include "esi/TypedPorts.h"
36
37#include <atomic>
38#include <chrono>
39#include <cstdint>
40#include <iostream>
41#include <map>
42#include <mutex>
43#include <stdexcept>
44#include <string>
45#include <thread>
46#include <vector>
47
48using namespace esi;
49
50// Resolve a child instance by AppID name from the top-level accelerator.
51static esi::HWModule *findInst(Accelerator *accel, const char *appidName) {
52 auto it = accel->getChildren().find(AppID(appidName));
53 if (it == accel->getChildren().end())
54 throw std::runtime_error(std::string("test_codegen instance '") +
55 appidName + "' not found");
56 return it->second;
57}
58
59//===----------------------------------------------------------------------===//
60// Function: typed multi-arg call via emplace ctor.
61//===----------------------------------------------------------------------===//
63 esi_system::TypedFuncMultiArg mod(
64 findInst(accel, "typed_func_multi_arg_inst"));
65 auto c = mod.connect();
66
67 // The emplace-style call() forwards its arguments into the generated
68 // arg struct's constructor, so we never have to spell that struct out.
69 uint32_t got = c->call(7u, 6u).get();
70 if (got != 42u)
71 throw std::runtime_error("typed_func_multi_arg: expected 42, got " +
72 std::to_string(got));
73 std::cout << "typed_func_multi_arg ok\n";
74 return 0;
75}
76
77//===----------------------------------------------------------------------===//
78// Function: void argument (typed-result specialization).
79//===----------------------------------------------------------------------===//
80static int runTypedFuncVoidArg(Accelerator *accel) {
81 esi_system::TypedFuncVoidArg mod(findInst(accel, "typed_func_void_arg_inst"));
82 auto c = mod.connect();
83
84 uint32_t got = c->call().get();
85 if (got != 0xCAFEF00Du)
86 throw std::runtime_error(
87 "typed_func_void_arg: expected 0xCAFEF00D, got 0x" + toHex(got));
88 std::cout << "typed_func_void_arg ok\n";
89 return 0;
90}
91
92//===----------------------------------------------------------------------===//
93// Function: void return (typed-arg specialization).
94//===----------------------------------------------------------------------===//
96 esi_system::TypedFuncVoidResult mod(
97 findInst(accel, "typed_func_void_result_inst"));
98 auto c = mod.connect();
99
100 // Just asserts that the future resolves without throwing. A void result is
101 // the wire-level zero byte; any failure to consume that byte would surface
102 // here as a hung future or a deserializer exception.
103 c->call(esi_system::AckArgs(0x5A, 0x1234)).get();
104 std::cout << "typed_func_void_result ok\n";
105 return 0;
106}
107
108//===----------------------------------------------------------------------===//
109// Callback: HW-initiated call into the host (triggered via an MMIO write).
110//===----------------------------------------------------------------------===//
112 esi_system::CallServiceCallback mod(
113 findInst(accel, "call_service_callback_inst"));
114 auto c = mod.connect();
115
116 // Install the user callback. The handler stores what it saw and signals a
117 // flag; the driver thread polls the flag (with a timeout) so this works
118 // both for inline-from-callback-thread dispatch and for service-thread
119 // dispatch.
120 std::atomic<bool> got_call(false);
121 esi_system::NotifyArgs seen{};
122 c->callback.connect([&](const esi_system::NotifyArgs &a) {
123 seen = a;
124 got_call.store(true, std::memory_order_release);
125 });
126
127 // Trigger the callback by writing the payload to the MMIO command region
128 // at offset 0x10. The HW module forwards the bottom 32 bits of the write
129 // data into the callback as ``payload`` and uses a fixed ``tag = 0xA5``.
130 constexpr uint32_t kPayload = 0xDEADBEEFu;
131 c->trigger.write(0x10, static_cast<uint64_t>(kPayload));
132
133 // Wait up to ~5s for the callback to fire.
134 using clock = std::chrono::steady_clock;
135 auto deadline = clock::now() + std::chrono::seconds(5);
136 while (!got_call.load(std::memory_order_acquire) && clock::now() < deadline)
137 std::this_thread::sleep_for(std::chrono::milliseconds(10));
138 if (!got_call.load(std::memory_order_acquire))
139 throw std::runtime_error(
140 "call_service_callback: callback did not fire within timeout");
141
142 if (seen.tag != 0xA5)
143 throw std::runtime_error(
144 "call_service_callback: wrong tag, expected 0xA5 got 0x" +
145 toHex(static_cast<uint64_t>(seen.tag)));
146 if (seen.payload != kPayload)
147 throw std::runtime_error(
148 "call_service_callback: wrong payload, expected 0x" + toHex(kPayload) +
149 " got 0x" + toHex(seen.payload));
150 std::cout << "call_service_callback ok\n";
151 return 0;
152}
153
154//===----------------------------------------------------------------------===//
155// To-host channel: TypedReadPort<EventStruct> polling.
156//===----------------------------------------------------------------------===//
158 esi_system::TypedReadChannelStruct mod(
159 findInst(accel, "typed_read_channel_struct_inst"));
160 auto c = mod.connect();
161
162 // The constant on the HW side bounds how many events get pushed.
163 constexpr size_t kNum = esi_system::TypedReadChannelStruct::num_events;
164 for (size_t i = 1; i <= kNum; ++i) {
165 auto ev = c->data.read();
166 if (!ev)
167 throw std::runtime_error(
168 "typed_read_channel_struct: null read result at i=" +
169 std::to_string(i));
170 if (ev->ts != i)
171 throw std::runtime_error(
172 "typed_read_channel_struct: wrong ts at i=" + std::to_string(i) +
173 ", got " + std::to_string(ev->ts));
174 int32_t expected = -static_cast<int32_t>(i);
175 if (ev->val != expected)
176 throw std::runtime_error(
177 "typed_read_channel_struct: wrong val at i=" + std::to_string(i) +
178 ", got " + std::to_string(ev->val));
179 }
180 std::cout << "typed_read_channel_struct ok (" << kNum << " events)\n";
181 return 0;
182}
183
184//===----------------------------------------------------------------------===//
185// From-host channel: TypedWritePort<uint8_t> + MMIO read-back accumulator.
186//===----------------------------------------------------------------------===//
188 esi_system::TypedWriteChannelByte mod(
189 findInst(accel, "typed_write_channel_byte_inst"));
190 auto c = mod.connect();
191
192 // Send a sequence and accumulate the expected XOR. The HW receiver is
193 // always-ready and XORs every byte into a register whose value is exposed
194 // via the ``accumulator`` MMIO read port.
195 static constexpr uint8_t kBytes[] = {0x11, 0x22, 0x44, 0x88, 0x10, 0x55};
196 uint8_t expected = 0;
197 for (uint8_t b : kBytes) {
198 c->data.write(b);
199 expected ^= b;
200 }
201
202 // Poll the accumulator MMIO until it matches (or we time out). A small
203 // poll loop covers the case where the last byte hasn't drained yet.
204 using clock = std::chrono::steady_clock;
205 auto deadline = clock::now() + std::chrono::seconds(2);
206 uint8_t got = 0;
207 while (clock::now() < deadline) {
208 uint64_t resp = c->accumulator.read(0);
209 got = static_cast<uint8_t>(resp & 0xff);
210 if (got == expected)
211 break;
212 std::this_thread::sleep_for(std::chrono::milliseconds(5));
213 }
214 if (got != expected)
215 throw std::runtime_error(
216 "typed_write_channel_byte: accumulator mismatch (expected 0x" +
217 toHex(static_cast<uint64_t>(expected)) + ", got 0x" +
218 toHex(static_cast<uint64_t>(got)) + ")");
219 std::cout << "typed_write_channel_byte ok (acc=0x"
220 << toHex(static_cast<uint64_t>(expected)) << ")\n";
221 return 0;
222}
223
224//===----------------------------------------------------------------------===//
225// MMIO region: read-write loopback at offset 0x10.
226//===----------------------------------------------------------------------===//
227static int runMmioReadWrite(Accelerator *accel) {
228 esi_system::MmioReadWrite mod(findInst(accel, "mmio_read_write_inst"));
229 auto c = mod.connect();
230
231 // Write a 64-bit token, then read it back. The HW's storage register is
232 // shared across all offsets, so ``offset`` here is just for completeness.
233 constexpr uint64_t kToken = 0xA5A51234'56789ABCULL;
234 c->region.write(0x10, kToken);
235 uint64_t got = c->region.read(0x10);
236 if (got != kToken)
237 throw std::runtime_error("mmio_read_write: round-trip mismatch (wrote 0x" +
238 toHex(kToken) + ", read 0x" + toHex(got) + ")");
239 std::cout << "mmio_read_write ok (round-trip 0x" << toHex(kToken) << ")\n";
240 return 0;
241}
242
243//===----------------------------------------------------------------------===//
244// Telemetry: free-running cycle counter is monotonic between reads.
245//===----------------------------------------------------------------------===//
246static int runTelemetryMetric(Accelerator *accel) {
247 esi_system::TelemetryMetric mod(findInst(accel, "telemetry_metric_inst"));
248 auto c = mod.connect();
249
250 uint64_t first = c->cycleCount.readInt();
251 // Sleep enough wall-time for the simulator to advance many cycles even
252 // under heavy load.
253 std::this_thread::sleep_for(std::chrono::milliseconds(50));
254 uint64_t second = c->cycleCount.readInt();
255
256 if (second <= first)
257 throw std::runtime_error(
258 "telemetry_metric: counter did not advance (first=" +
259 std::to_string(first) + ", second=" + std::to_string(second) + ")");
260 std::cout << "telemetry_metric ok (advanced by " << (second - first) << ")\n";
261 return 0;
262}
263
264//===----------------------------------------------------------------------===//
265// Indexed function group: exercise every entry of IndexedPorts<TypedFunction>.
266//===----------------------------------------------------------------------===//
268 // Single IndexedFuncGroup module exposes N typed-function ports under the
269 // same appid name ``call`` with indices 0..N-1; codegen groups them into a
270 // single ``IndexedPorts<TypedFunction<...>>`` member that the driver
271 // iterates with ``c->call[idx]``.
272 esi_system::IndexedFuncGroup mod(findInst(accel, "indexed_func_group_inst"));
273 auto c = mod.connect();
274 constexpr size_t kN = esi_system::IndexedFuncGroup::num_entries;
275 for (uint32_t idx = 0; idx < kN; ++idx) {
276 constexpr uint16_t kArg = 100;
277 uint16_t got = c->call[idx](kArg).get();
278 uint16_t expected = static_cast<uint16_t>(kArg + (idx + 1));
279 if (got != expected)
280 throw std::runtime_error("indexed_func_group[" + std::to_string(idx) +
281 "]: expected " + std::to_string(expected) +
282 ", got " + std::to_string(got));
283 }
284 std::cout << "indexed_func_group ok (" << kN << " entries)\n";
285 return 0;
286}
287
288//===----------------------------------------------------------------------===//
289// Custom-`@esi.ServiceDecl` raw-channel byte loopback. Exercises bundle ports
290// backed by a custom service decl rather than the standard `ChannelService`,
291// across two indexed instances. The HW also exposes void (Bits(0)) bundles
292// for elaboration coverage; the C++ driver does not exercise them because
293// the runtime's blocking ``ReadChannelPort::read`` does not surface a
294// completion for zero-byte messages.
295//===----------------------------------------------------------------------===//
296static int runCustomServiceDeclChannel(Accelerator *accel, uint32_t idx) {
297 auto it =
298 accel->getChildren().find(AppID("custom_service_decl_channel", idx));
299 if (it == accel->getChildren().end())
300 throw std::runtime_error("custom_service_decl_channel[" +
301 std::to_string(idx) + "]: instance not found");
302 esi_system::CustomServiceDeclChannel mod(it->second);
303 auto c = mod.connect();
304
305 // Byte channel: send a unique byte per instance and verify the echo so a
306 // crossed-wires bug between the two CustomServiceDeclChannel instances
307 // would be caught (same-AppID-name multi-instance regression).
308 TypedWritePort<uint8_t> toHw(c->byte_in.getRawWrite("recv"));
309 TypedReadPort<uint8_t> fromHw(c->byte_out.getRawRead("send"));
310 toHw.connect();
311 fromHw.connect();
312
313 uint8_t sendVal = static_cast<uint8_t>(0x40 + idx);
314 toHw.write(sendVal);
315 std::unique_ptr<uint8_t> got = fromHw.read();
316 if (!got || *got != sendVal)
317 throw std::runtime_error(
318 "custom_service_decl_channel[" + std::to_string(idx) +
319 "]: byte loopback mismatch (sent 0x" +
320 toHex(static_cast<uint64_t>(sendVal)) + ", got 0x" +
321 toHex(static_cast<uint64_t>(got ? *got : 0u)) + ")");
322
323 std::cout << "custom_service_decl_channel_" << idx << " ok (byte 0x"
324 << toHex(static_cast<uint64_t>(sendVal)) << ")\n";
325 return 0;
326}
327
330 return 0;
331}
334 return 0;
335}
336
337//===----------------------------------------------------------------------===//
338// Typed function: small struct -> small struct.
339//===----------------------------------------------------------------------===//
340static int runTypedFuncStruct(Accelerator *accel) {
341 esi_system::TypedFuncStruct mod(findInst(accel, "typed_func_struct_inst"));
342 auto c = mod.connect();
343
344 esi_system::StructArgs arg(0x1234, static_cast<int8_t>(-7));
345 esi_system::StructResult res = c->call(arg).get();
346 int8_t expectedX = static_cast<int8_t>(arg.b + 1);
347 if (res.x != expectedX || res.y != arg.b)
348 throw std::runtime_error(
349 "typed_func_struct: wrong result (b=" + std::to_string(arg.b) +
350 " x=" + std::to_string(res.x) + " y=" + std::to_string(res.y) + ")");
351 std::cout << "typed_func_struct ok (b=" << (int)arg.b
352 << " -> x=" << (int)res.x << " y=" << (int)res.y << ")\n";
353 return 0;
354}
355
356//===----------------------------------------------------------------------===//
357// Typed function: nested odd-bit-width struct round-trip.
358//===----------------------------------------------------------------------===//
360 esi_system::TypedFuncNestedStruct mod(
361 findInst(accel, "typed_func_nested_struct_inst"));
362 auto c = mod.connect();
363
364 esi_system::OddStruct arg;
365 arg.a = 0xabc;
366 arg.b = static_cast<int8_t>(-17);
367 arg.inner.p = 5;
368 arg.inner.q = static_cast<int8_t>(-7);
369 arg.inner.r[0] = 3;
370 arg.inner.r[1] = 4;
371
372 esi_system::OddStruct res = c->call(arg).get();
373 uint16_t expA = static_cast<uint16_t>(arg.a + 1);
374 int8_t expB = static_cast<int8_t>(arg.b - 3);
375 uint8_t expP = static_cast<uint8_t>(arg.inner.p + 5);
376 int8_t expQ = static_cast<int8_t>(arg.inner.q + 2);
377 uint8_t expR0 = static_cast<uint8_t>(arg.inner.r[0] + 1);
378 uint8_t expR1 = static_cast<uint8_t>(arg.inner.r[1] + 2);
379 if (res.a != expA || res.b != expB || res.inner.p != expP ||
380 res.inner.q != expQ || res.inner.r[0] != expR0 || res.inner.r[1] != expR1)
381 throw std::runtime_error("typed_func_nested_struct: result mismatch");
382 std::cout << "typed_func_nested_struct ok (a=" << res.a << " b=" << (int)res.b
383 << " p=" << (int)res.inner.p << " q=" << (int)res.inner.q << " r=["
384 << (int)res.inner.r[0] << "," << (int)res.inner.r[1] << "])\n";
385 return 0;
386}
387
388//===----------------------------------------------------------------------===//
389// Typed function: ``si4 -> si4`` identity. Probes sign extension at a
390// sub-byte width through the typed facade.
391//===----------------------------------------------------------------------===//
393 esi_system::TypedFuncSubByteSigned mod(
394 findInst(accel, "typed_func_subbyte_signed_inst"));
395 auto c = mod.connect();
396
397 for (int8_t arg : {static_cast<int8_t>(5), static_cast<int8_t>(-3),
398 static_cast<int8_t>(-8), static_cast<int8_t>(7)}) {
399 int8_t got = c->call(arg).get();
400 if (got != arg)
401 throw std::runtime_error(
402 "typed_func_subbyte_signed: arg=" + std::to_string(arg) +
403 " got=" + std::to_string(got));
404 }
405 std::cout << "typed_func_subbyte_signed ok (4 values)\n";
406 return 0;
407}
408
409//===----------------------------------------------------------------------===//
410// Typed function with an array result.
411//===----------------------------------------------------------------------===//
413 esi_system::TypedFuncArrayResult mod(
414 findInst(accel, "typed_func_array_result_inst"));
415 auto c = mod.connect();
416
417 esi_system::TypedFuncArrayResult::callArgs arg{static_cast<int8_t>(-3)};
418 esi_system::ArrayResult res = c->call(arg).get();
419 int8_t a = res[0];
420 int8_t b = res[1];
421 int8_t expect0 = arg[0];
422 int8_t expect1 = static_cast<int8_t>(arg[0] + 1);
423 bool ok = (a == expect0 && b == expect1) || (a == expect1 && b == expect0);
424 if (!ok)
425 throw std::runtime_error("typed_func_array_result: result mismatch");
426 std::cout << "typed_func_array_result ok ([" << (int)a << "," << (int)b
427 << "])\n";
428 return 0;
429}
430
431//===----------------------------------------------------------------------===//
432// Typed function over a windowed list payload. Doubles each element of the
433// input list and reads the result back as another serial-burst window.
434// Exercises the auto serial<->parallel windowed-list converters and the
435// `SerialListTypeDeserializer` end-to-end.
436//===----------------------------------------------------------------------===//
438 esi_system::TypedFuncWindowedList mod(
439 findInst(accel, "typed_func_windowed_list_inst"));
440 auto c = mod.connect();
441
442 using ArgT = esi_system::TypedFuncWindowedList::callArgs;
443 using ResT = esi_system::TypedFuncWindowedList::callResult;
444 std::vector<esi_system::TransformListItem> input;
445 for (uint32_t v : {3u, 5u, 7u, 9u, 11u})
446 input.emplace_back(v);
447
448 ArgT arg(input);
449 ResT result = c->call(arg).get();
450
451 if (result.data_count() != input.size())
452 throw std::runtime_error(
453 "typed_func_windowed_list: wrong result size (got " +
454 std::to_string(result.data_count()) + ")");
455 size_t i = 0;
456 for (const esi_system::TransformListItem &item : result.data()) {
457 uint32_t expected = input[i].v + input[i].v;
458 if (item.v != expected)
459 throw std::runtime_error("typed_func_windowed_list: element " +
460 std::to_string(i) + " expected " +
461 std::to_string(expected) + ", got " +
462 std::to_string(item.v));
463 ++i;
464 }
465 std::cout << "typed_func_windowed_list ok (" << input.size()
466 << " items doubled)\n";
467 return 0;
468}
469
470//===----------------------------------------------------------------------===//
471// To-host channel of windowed list-with-header. Exercises the typed read path
472// for serial-burst bulk transfers.
473//===----------------------------------------------------------------------===//
475 esi_system::ChannelWindowedListRead mod(
476 findInst(accel, "channel_windowed_list_read_inst"));
477 auto c = mod.connect();
478
479 using WinT = esi_system::ChannelWindowedListRead::dataData;
480
481 // Arm one burst via the MMIO trigger. The HW only emits when triggered, so
482 // free-running emission can't fill the host's polling buffer.
483 c->trigger.write(0x10, 0u);
484
485 std::unique_ptr<WinT> got = c->data.read();
486 if (!got)
487 throw std::runtime_error("channel_windowed_list_read: null read result");
488 // The HW emits one burst with ``[10, 20, 30, 40]`` and ``tag = 0xCAFE``.
489 static constexpr uint16_t kTag = 0xCAFE;
490 static const uint32_t kExpected[] = {10u, 20u, 30u, 40u};
491 if (got->tag() != kTag)
492 throw std::runtime_error(
493 "channel_windowed_list_read: wrong tag, expected 0x" +
494 toHex(static_cast<uint64_t>(kTag)) + " got 0x" +
495 toHex(static_cast<uint64_t>(got->tag())));
496 if (got->items_count() != 4)
497 throw std::runtime_error(
498 "channel_windowed_list_read: wrong item count, expected 4 got " +
499 std::to_string(got->items_count()));
500 size_t i = 0;
501 for (uint32_t v : got->items()) {
502 if (v != kExpected[i])
503 throw std::runtime_error("channel_windowed_list_read: element " +
504 std::to_string(i) + " expected " +
505 std::to_string(kExpected[i]) + " got " +
506 std::to_string(v));
507 ++i;
508 }
509 std::cout << "channel_windowed_list_read ok (tag=0x"
510 << toHex(static_cast<uint64_t>(kTag)) << ", items=[10,20,30,40])\n";
511 return 0;
512}
513
514//===----------------------------------------------------------------------===//
515// From-host channel of windowed list-with-header. Exercises the typed write
516// path: the host constructs a complete burst from a header tag plus a list of
517// items, and the HW AND-reduces each beat against the expected pattern. The
518// driver verifies success via the ``match`` MMIO read region.
519//===----------------------------------------------------------------------===//
521 esi_system::ChannelWindowedListWrite mod(
522 findInst(accel, "channel_windowed_list_write_inst"));
523 auto c = mod.connect();
524
525 using WinT = esi_system::ChannelWindowedListWrite::dataData;
526
527 static constexpr uint16_t kTag = 0xCAFE;
528 std::vector<uint32_t> items{10u, 20u, 30u, 40u};
529 c->data.write(WinT(kTag, items));
530
531 // Poll the match flag MMIO until the burst has been processed (or time
532 // out). The HW updates the latch on the burst-end beat.
533 using clock = std::chrono::steady_clock;
534 auto deadline = clock::now() + std::chrono::seconds(5);
535 uint64_t match = 0;
536 while (clock::now() < deadline) {
537 match = c->match.read(0);
538 if (match & 1)
539 break;
540 std::this_thread::sleep_for(std::chrono::milliseconds(5));
541 }
542 if (!(match & 1))
543 throw std::runtime_error(
544 "channel_windowed_list_write: HW did not report a match within "
545 "timeout (got 0x" +
546 toHex(match) + ")");
547 std::cout << "channel_windowed_list_write ok (tag=0x"
548 << toHex(static_cast<uint64_t>(kTag)) << ", items=[10,20,30,40])\n";
549 return 0;
550}
551
552//===----------------------------------------------------------------------===//
553// Callback with windowed list argument: HW sends a serial-burst windowed
554// list (tag + items) into a host callback. Verifies that the
555// `SerialListTypeDeserializer` works end-to-end through the
556// `TypedCallback<WindowT, void>` path.
557//===----------------------------------------------------------------------===//
559 esi_system::CallbackWindowedList mod(
560 findInst(accel, "callback_windowed_list_inst"));
561 auto c = mod.connect();
562
563 using WinT = esi_system::CallbackWindowedList::callbackArgs;
564
565 std::atomic<bool> got_call(false);
566 std::string error_msg;
567 std::mutex error_mtx;
568 c->callback.connect([&](const WinT &arg) {
569 try {
570 static constexpr uint16_t kTag = 0xCAFE;
571 static const uint32_t kExpected[] = {10u, 20u, 30u, 40u};
572 if (arg.tag() != kTag)
573 throw std::runtime_error(
574 "callback_windowed_list: wrong tag, expected 0x" +
575 toHex(static_cast<uint64_t>(kTag)) + " got 0x" +
576 toHex(static_cast<uint64_t>(arg.tag())));
577 if (arg.items_count() != 4)
578 throw std::runtime_error(
579 "callback_windowed_list: wrong item count, expected 4 got " +
580 std::to_string(arg.items_count()));
581 size_t i = 0;
582 for (uint32_t v : arg.items()) {
583 if (v != kExpected[i])
584 throw std::runtime_error("callback_windowed_list: element " +
585 std::to_string(i) + " expected " +
586 std::to_string(kExpected[i]) + " got " +
587 std::to_string(v));
588 ++i;
589 }
590
591 std::cout << "callback_windowed_list ok (tag=0x"
592 << toHex(static_cast<uint64_t>(kTag))
593 << ", items=[10,20,30,40])\n";
594 } catch (const std::exception &e) {
595 std::lock_guard<std::mutex> lk(error_mtx);
596 error_msg = e.what();
597 }
598 got_call.store(true, std::memory_order_release);
599 });
600
601 // Arm the burst via MMIO trigger.
602 c->trigger.write(0x10, 0u);
603
604 using clock = std::chrono::steady_clock;
605 auto deadline = clock::now() + std::chrono::seconds(5);
606 while (!got_call.load(std::memory_order_acquire) && clock::now() < deadline)
607 std::this_thread::sleep_for(std::chrono::milliseconds(10));
608 if (!got_call.load(std::memory_order_acquire))
609 throw std::runtime_error(
610 "callback_windowed_list: callback did not fire within timeout");
611 std::lock_guard<std::mutex> lk(error_mtx);
612 if (!error_msg.empty())
613 throw std::runtime_error(error_msg);
614 return 0;
615}
616
618 "test-codegen",
619 "Per-port-kind coverage tests for ESI runtime + facade codegen. "
620 "Run a single probe with --probe NAME or run all probes (in registry "
621 "order) by omitting the flag.",
622 {"typed_func_multi_arg", &runTypedFuncMultiArg},
623 {"typed_func_void_arg", &runTypedFuncVoidArg},
624 {"typed_func_void_result", &runTypedFuncVoidResult},
625 {"call_service_callback", &runCallServiceCallback},
626 {"typed_read_channel_struct", &runTypedReadChannelStruct},
627 {"typed_write_channel_byte", &runTypedWriteChannelByte},
628 {"mmio_read_write", &runMmioReadWrite},
629 {"telemetry_metric", &runTelemetryMetric},
630 {"indexed_func_group", &runIndexedFuncGroup},
631 {"custom_service_decl_channel_0", &runCustomServiceDeclChannel0},
632 {"custom_service_decl_channel_1", &runCustomServiceDeclChannel1},
633 {"typed_func_struct", &runTypedFuncStruct},
634 {"typed_func_nested_struct", &runTypedFuncNestedStruct},
635 {"typed_func_subbyte_signed", &runTypedFuncSubByteSigned},
636 {"typed_func_array_result", &runTypedFuncArrayResult},
637 {"typed_func_windowed_list", &runTypedFuncWindowedList},
638 {"channel_windowed_list_read", &runChannelWindowedListRead},
639 {"channel_windowed_list_write", &runChannelWindowedListWrite},
640 {"callback_windowed_list", &runCallbackWindowedList}, );
Top level accelerator class.
Definition Accelerator.h:70
Represents either the top level or an instance of a hardware module.
Definition Design.h:47
const std::map< AppID, Instance * > & getChildren() const
Access the module's children by ID.
Definition Design.h:71
Strongly typed wrapper around a raw read channel.
Definition TypedPorts.h:718
std::unique_ptr< T > read()
Blocking typed read in polling mode.
Definition TypedPorts.h:801
void connect(const ChannelPort::ConnectOptions &opts={std::nullopt, false})
Connect in polling mode.
Definition TypedPorts.h:737
void connect(const ChannelPort::ConnectOptions &opts={std::nullopt, false})
Definition TypedPorts.h:626
void write(const T &data)
Definition TypedPorts.h:636
Definition esi.py:1
std::string toHex(void *val)
Definition Common.cpp:37
#define ESI_PROBE_REGISTRY(name, description,...)
Convenience macro: defines main() with a probe registry.
static int runCustomServiceDeclChannel0(Accelerator *accel)
static int runTypedFuncWindowedList(Accelerator *accel)
static int runChannelWindowedListRead(Accelerator *accel)
static int runMmioReadWrite(Accelerator *accel)
static int runTypedReadChannelStruct(Accelerator *accel)
static int runTypedFuncVoidResult(Accelerator *accel)
static int runCustomServiceDeclChannel(Accelerator *accel, uint32_t idx)
static int runTypedFuncSubByteSigned(Accelerator *accel)
static esi::HWModule * findInst(Accelerator *accel, const char *appidName)
static int runCallServiceCallback(Accelerator *accel)
static int runTypedFuncArrayResult(Accelerator *accel)
static int runCallbackWindowedList(Accelerator *accel)
static int runTypedFuncMultiArg(Accelerator *accel)
static int runTelemetryMetric(Accelerator *accel)
static int runIndexedFuncGroup(Accelerator *accel)
static int runTypedFuncVoidArg(Accelerator *accel)
static int runTypedWriteChannelByte(Accelerator *accel)
static int runCustomServiceDeclChannel1(Accelerator *accel)
static int runTypedFuncNestedStruct(Accelerator *accel)
static int runTypedFuncStruct(Accelerator *accel)
static int runChannelWindowedListWrite(Accelerator *accel)