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 ")");
352 std::cout << "typed_func_struct ok (b=" << (int)arg.b()
353 << " -> x=" << (int)res.x() << " y=" << (int)res.y() << ")\n";
354 return 0;
355}
356
357//===----------------------------------------------------------------------===//
358// Typed function: nested odd-bit-width struct round-trip.
359//===----------------------------------------------------------------------===//
361 esi_system::TypedFuncNestedStruct mod(
362 findInst(accel, "typed_func_nested_struct_inst"));
363 auto c = mod.connect();
364
365 esi_system::OddStruct arg;
366 arg.a(0xabc);
367 arg.b(static_cast<int8_t>(-17));
368 auto inner = arg.inner();
369 inner.p(5);
370 inner.q(static_cast<int8_t>(-7));
371 inner.r({3, 4});
372 arg.inner(inner);
373
374 esi_system::OddStruct res = c->call(arg).get();
375 uint16_t expA = static_cast<uint16_t>(arg.a() + 1);
376 int8_t expB = static_cast<int8_t>(arg.b() - 3);
377 uint8_t expP = static_cast<uint8_t>(arg.inner().p() + 5);
378 int8_t expQ = static_cast<int8_t>(arg.inner().q() + 2);
379 uint8_t expR0 = static_cast<uint8_t>(arg.inner().r()[0] + 1);
380 uint8_t expR1 = static_cast<uint8_t>(arg.inner().r()[1] + 2);
381 if (res.a() != expA || res.b() != expB || res.inner().p() != expP ||
382 res.inner().q() != expQ || res.inner().r()[0] != expR0 ||
383 res.inner().r()[1] != expR1)
384 throw std::runtime_error("typed_func_nested_struct: result mismatch");
385 std::cout << "typed_func_nested_struct ok (a=" << res.a()
386 << " b=" << (int)res.b() << " p=" << (int)res.inner().p()
387 << " q=" << (int)res.inner().q() << " r=["
388 << (int)res.inner().r()[0] << "," << (int)res.inner().r()[1]
389 << "])\n";
390 return 0;
391}
392
393//===----------------------------------------------------------------------===//
394// Typed function: ``si4 -> si4`` identity. Probes sign extension at a
395// sub-byte width through the typed facade.
396//===----------------------------------------------------------------------===//
398 esi_system::TypedFuncSubByteSigned mod(
399 findInst(accel, "typed_func_subbyte_signed_inst"));
400 auto c = mod.connect();
401
402 for (int8_t arg : {static_cast<int8_t>(5), static_cast<int8_t>(-3),
403 static_cast<int8_t>(-8), static_cast<int8_t>(7)}) {
404 int8_t got = c->call(arg).get();
405 if (got != arg)
406 throw std::runtime_error(
407 "typed_func_subbyte_signed: arg=" + std::to_string(arg) +
408 " got=" + std::to_string(got));
409 }
410 std::cout << "typed_func_subbyte_signed ok (4 values)\n";
411 return 0;
412}
413
414//===----------------------------------------------------------------------===//
415// Typed function with an array result.
416//===----------------------------------------------------------------------===//
418 esi_system::TypedFuncArrayResult mod(
419 findInst(accel, "typed_func_array_result_inst"));
420 auto c = mod.connect();
421
422 esi_system::TypedFuncArrayResult::callArgs arg{static_cast<int8_t>(-3)};
423 esi_system::ArrayResult res = c->call(arg).get();
424 int8_t a = res[0];
425 int8_t b = res[1];
426 int8_t expect0 = arg[0];
427 int8_t expect1 = static_cast<int8_t>(arg[0] + 1);
428 bool ok = (a == expect0 && b == expect1) || (a == expect1 && b == expect0);
429 if (!ok)
430 throw std::runtime_error("typed_func_array_result: result mismatch");
431 std::cout << "typed_func_array_result ok ([" << (int)a << "," << (int)b
432 << "])\n";
433 return 0;
434}
435
436//===----------------------------------------------------------------------===//
437// Typed function over a windowed list payload. Doubles each element of the
438// input list and reads the result back as another serial-burst window.
439// Exercises the auto serial<->parallel windowed-list converters and the
440// `SerialListTypeDeserializer` end-to-end.
441//===----------------------------------------------------------------------===//
443 esi_system::TypedFuncWindowedList mod(
444 findInst(accel, "typed_func_windowed_list_inst"));
445 auto c = mod.connect();
446
447 using ArgT = esi_system::TypedFuncWindowedList::callArgs;
448 using ResT = esi_system::TypedFuncWindowedList::callResult;
449 std::vector<esi_system::TransformListItem> input;
450 for (uint32_t v : {3u, 5u, 7u, 9u, 11u})
451 input.emplace_back(v);
452
453 ArgT arg(input);
454 ResT result = c->call(arg).get();
455
456 if (result.data_count() != input.size())
457 throw std::runtime_error(
458 "typed_func_windowed_list: wrong result size (got " +
459 std::to_string(result.data_count()) + ")");
460 size_t i = 0;
461 for (const esi_system::TransformListItem &item : result.data()) {
462 uint32_t expected = input[i].v() + input[i].v();
463 if (item.v() != expected)
464 throw std::runtime_error("typed_func_windowed_list: element " +
465 std::to_string(i) + " expected " +
466 std::to_string(expected) + ", got " +
467 std::to_string(item.v()));
468 ++i;
469 }
470 std::cout << "typed_func_windowed_list ok (" << input.size()
471 << " items doubled)\n";
472 return 0;
473}
474
475//===----------------------------------------------------------------------===//
476// To-host channel of windowed list-with-header. Exercises the typed read path
477// for serial-burst bulk transfers.
478//===----------------------------------------------------------------------===//
480 esi_system::ChannelWindowedListRead mod(
481 findInst(accel, "channel_windowed_list_read_inst"));
482 auto c = mod.connect();
483
484 using WinT = esi_system::ChannelWindowedListRead::dataData;
485
486 // Arm one burst via the MMIO trigger. The HW only emits when triggered, so
487 // free-running emission can't fill the host's polling buffer.
488 c->trigger.write(0x10, 0u);
489
490 std::unique_ptr<WinT> got = c->data.read();
491 if (!got)
492 throw std::runtime_error("channel_windowed_list_read: null read result");
493 // The HW emits one burst with ``[10, 20, 30, 40]`` and ``tag = 0xCAFE``.
494 static constexpr uint16_t kTag = 0xCAFE;
495 static const uint32_t kExpected[] = {10u, 20u, 30u, 40u};
496 if (got->tag() != kTag)
497 throw std::runtime_error(
498 "channel_windowed_list_read: wrong tag, expected 0x" +
499 toHex(static_cast<uint64_t>(kTag)) + " got 0x" +
500 toHex(static_cast<uint64_t>(got->tag())));
501 if (got->items_count() != 4)
502 throw std::runtime_error(
503 "channel_windowed_list_read: wrong item count, expected 4 got " +
504 std::to_string(got->items_count()));
505 size_t i = 0;
506 for (uint32_t v : got->items()) {
507 if (v != kExpected[i])
508 throw std::runtime_error("channel_windowed_list_read: element " +
509 std::to_string(i) + " expected " +
510 std::to_string(kExpected[i]) + " got " +
511 std::to_string(v));
512 ++i;
513 }
514 std::cout << "channel_windowed_list_read ok (tag=0x"
515 << toHex(static_cast<uint64_t>(kTag)) << ", items=[10,20,30,40])\n";
516 return 0;
517}
518
519//===----------------------------------------------------------------------===//
520// From-host channel of windowed list-with-header. Exercises the typed write
521// path: the host constructs a complete burst from a header tag plus a list of
522// items, and the HW AND-reduces each beat against the expected pattern. The
523// driver verifies success via the ``match`` MMIO read region.
524//===----------------------------------------------------------------------===//
526 esi_system::ChannelWindowedListWrite mod(
527 findInst(accel, "channel_windowed_list_write_inst"));
528 auto c = mod.connect();
529
530 using WinT = esi_system::ChannelWindowedListWrite::dataData;
531
532 static constexpr uint16_t kTag = 0xCAFE;
533 std::vector<uint32_t> items{10u, 20u, 30u, 40u};
534 c->data.write(WinT(kTag, items));
535
536 // Poll the match flag MMIO until the burst has been processed (or time
537 // out). The HW updates the latch on the burst-end beat.
538 using clock = std::chrono::steady_clock;
539 auto deadline = clock::now() + std::chrono::seconds(5);
540 uint64_t match = 0;
541 while (clock::now() < deadline) {
542 match = c->match.read(0);
543 if (match & 1)
544 break;
545 std::this_thread::sleep_for(std::chrono::milliseconds(5));
546 }
547 if (!(match & 1))
548 throw std::runtime_error(
549 "channel_windowed_list_write: HW did not report a match within "
550 "timeout (got 0x" +
551 toHex(match) + ")");
552 std::cout << "channel_windowed_list_write ok (tag=0x"
553 << toHex(static_cast<uint64_t>(kTag)) << ", items=[10,20,30,40])\n";
554 return 0;
555}
556
557//===----------------------------------------------------------------------===//
558// Callback with windowed list argument: HW sends a serial-burst windowed
559// list (tag + items) into a host callback. Verifies that the
560// `SerialListTypeDeserializer` works end-to-end through the
561// `TypedCallback<WindowT, void>` path.
562//===----------------------------------------------------------------------===//
564 esi_system::CallbackWindowedList mod(
565 findInst(accel, "callback_windowed_list_inst"));
566 auto c = mod.connect();
567
568 using WinT = esi_system::CallbackWindowedList::callbackArgs;
569
570 std::atomic<bool> got_call(false);
571 std::string error_msg;
572 std::mutex error_mtx;
573 c->callback.connect([&](const WinT &arg) {
574 try {
575 static constexpr uint16_t kTag = 0xCAFE;
576 static const uint32_t kExpected[] = {10u, 20u, 30u, 40u};
577 if (arg.tag() != kTag)
578 throw std::runtime_error(
579 "callback_windowed_list: wrong tag, expected 0x" +
580 toHex(static_cast<uint64_t>(kTag)) + " got 0x" +
581 toHex(static_cast<uint64_t>(arg.tag())));
582 if (arg.items_count() != 4)
583 throw std::runtime_error(
584 "callback_windowed_list: wrong item count, expected 4 got " +
585 std::to_string(arg.items_count()));
586 size_t i = 0;
587 for (uint32_t v : arg.items()) {
588 if (v != kExpected[i])
589 throw std::runtime_error("callback_windowed_list: element " +
590 std::to_string(i) + " expected " +
591 std::to_string(kExpected[i]) + " got " +
592 std::to_string(v));
593 ++i;
594 }
595
596 std::cout << "callback_windowed_list ok (tag=0x"
597 << toHex(static_cast<uint64_t>(kTag))
598 << ", items=[10,20,30,40])\n";
599 } catch (const std::exception &e) {
600 std::lock_guard<std::mutex> lk(error_mtx);
601 error_msg = e.what();
602 }
603 got_call.store(true, std::memory_order_release);
604 });
605
606 // Arm the burst via MMIO trigger.
607 c->trigger.write(0x10, 0u);
608
609 using clock = std::chrono::steady_clock;
610 auto deadline = clock::now() + std::chrono::seconds(5);
611 while (!got_call.load(std::memory_order_acquire) && clock::now() < deadline)
612 std::this_thread::sleep_for(std::chrono::milliseconds(10));
613 if (!got_call.load(std::memory_order_acquire))
614 throw std::runtime_error(
615 "callback_windowed_list: callback did not fire within timeout");
616 std::lock_guard<std::mutex> lk(error_mtx);
617 if (!error_msg.empty())
618 throw std::runtime_error(error_msg);
619 return 0;
620}
621
623 "test-codegen",
624 "Per-port-kind coverage tests for ESI runtime + facade codegen. "
625 "Run a single probe with --probe NAME or run all probes (in registry "
626 "order) by omitting the flag.",
627 {"typed_func_multi_arg", &runTypedFuncMultiArg},
628 {"typed_func_void_arg", &runTypedFuncVoidArg},
629 {"typed_func_void_result", &runTypedFuncVoidResult},
630 {"call_service_callback", &runCallServiceCallback},
631 {"typed_read_channel_struct", &runTypedReadChannelStruct},
632 {"typed_write_channel_byte", &runTypedWriteChannelByte},
633 {"mmio_read_write", &runMmioReadWrite},
634 {"telemetry_metric", &runTelemetryMetric},
635 {"indexed_func_group", &runIndexedFuncGroup},
636 {"custom_service_decl_channel_0", &runCustomServiceDeclChannel0},
637 {"custom_service_decl_channel_1", &runCustomServiceDeclChannel1},
638 {"typed_func_struct", &runTypedFuncStruct},
639 {"typed_func_nested_struct", &runTypedFuncNestedStruct},
640 {"typed_func_subbyte_signed", &runTypedFuncSubByteSigned},
641 {"typed_func_array_result", &runTypedFuncArrayResult},
642 {"typed_func_windowed_list", &runTypedFuncWindowedList},
643 {"channel_windowed_list_read", &runChannelWindowedListRead},
644 {"channel_windowed_list_write", &runChannelWindowedListWrite},
645 {"callback_windowed_list", &runCallbackWindowedList}, );
Top level accelerator class.
Definition Accelerator.h:77
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)