CIRCT 23.0.0git
Loading...
Searching...
No Matches
esitester.py
Go to the documentation of this file.
1# ===- esitester.py - accelerator for testing ESI functionality -----------===//
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# This accelerator is intended to eventually grow into a full ESI accelerator
10# test image. It will be used both to test system functionality and system
11# performance. The corresponding software appliciation in the ESI runtime and
12# the ESI cosim. Where this should live longer-term is a unclear.
13#
14# ===----------------------------------------------------------------------===//
15
16# REQUIRES: esi-runtime, esi-cosim, rtl-sim, esitester
17# RUN: rm -rf %t
18# RUN: mkdir %t && cd %t
19
20# Test cosim without DMA.
21# RUN: %PYTHON% %s %t cosim 2>&1
22# RUN: esi-cosim.py --source %t -- esitester -v cosim env callback -i 5 | FileCheck %s
23# RUN: esi-cosim.py --source %t -- esitester cosim env streaming_add | FileCheck %s --check-prefix=STREAMING_ADD
24# RUN: esi-cosim.py --source %t -- esitester cosim env streaming_add -t | FileCheck %s --check-prefix=STREAMING_ADD
25# RUN: esi-cosim.py --source %t -- esitester cosim env translate_coords | FileCheck %s --check-prefix=TRANSLATE_COORDS
26# RUN: esi-cosim.py --source %t -- esitester cosim env serial_coords -n 40 -b 33 | FileCheck %s --check-prefix=SERIAL_COORDS
27# RUN: ESI_COSIM_MANIFEST_MMIO=1 esi-cosim.py --source %t -- esiquery cosim env info
28# RUN: esi-cosim.py --source %t -- esiquery cosim env telemetry | FileCheck %s --check-prefix=TELEMETRY
29
30# Now test the DMA engines.
31# RUN: %PYTHON% %s %t cosim_dma 2>&1
32# RUN: esi-cosim.py --source %t -- esitester cosim env hostmem
33# RUN: esi-cosim.py --source %t -- esitester cosim env dma -w -r
34# RUN: esi-cosim.py --source %t -- esiquery cosim env telemetry | FileCheck %s --check-prefix=TELEMETRY
35
36import sys
37from typing import Type
38
39import pycde.esi as esi
40from pycde import Clock, Module, Reset, System, generator, modparams
41from pycde.bsp import get_bsp
42from pycde.common import AppID, Constant, InputChannel, Output, OutputChannel
43from pycde.constructs import ControlReg, Counter, Mux, NamedWire, Reg, Wire
44from pycde.module import Metadata
45from pycde.testing import print_info
46from pycde.types import Bits, Channel, ChannelSignaling, UInt
47
48# CHECK: [CONNECT] connecting to backend
49
50# STREAMING_ADD: Streaming add test results:
51# STREAMING_ADD: input[0]=222709 + 5 = 222714 (expected 222714)
52# STREAMING_ADD: input[1]=894611 + 5 = 894616 (expected 894616)
53# STREAMING_ADD: input[2]=772894 + 5 = 772899 (expected 772899)
54# STREAMING_ADD: input[3]=429150 + 5 = 429155 (expected 429155)
55# STREAMING_ADD: input[4]=629806 + 5 = 629811 (expected 629811)
56# STREAMING_ADD: Streaming add test passed
57
58# TRANSLATE_COORDS: Coord translate test results:
59# TRANSLATE_COORDS: coord[0]=(222709,894611) + (10,20) = (222719,894631)
60# TRANSLATE_COORDS: coord[1]=(772894,429150) + (10,20) = (772904,429170)
61# TRANSLATE_COORDS: coord[2]=(629806,138727) + (10,20) = (629816,138747)
62# TRANSLATE_COORDS: coord[3]=(218516,390276) + (10,20) = (218526,390296)
63# TRANSLATE_COORDS: coord[4]=(750021,423525) + (10,20) = (750031,423545)
64# TRANSLATE_COORDS: Coord translate test passed
65
66# SERIAL_COORDS: Serial coord translate test results:
67# SERIAL_COORDS: coord[0]=
68# SERIAL_COORDS: Serial coord translate test passed
69
70# TELEMETRY: ********************************
71# TELEMETRY: * Telemetry
72# TELEMETRY: ********************************
73
74# TELEMETRY: fromhostdma[32].fromHostCycles: 0
75# TELEMETRY: fromhostdma[64].fromHostCycles: 0
76# TELEMETRY: fromhostdma[128].fromHostCycles: 0
77# TELEMETRY: fromhostdma[256].fromHostCycles: 0
78# TELEMETRY: fromhostdma[512].fromHostCycles: 0
79# TELEMETRY: fromhostdma[534].fromHostCycles: 0
80# TELEMETRY: fromhostdma_0[512].fromHostCycles: 0
81# TELEMETRY: fromhostdma_1[512].fromHostCycles: 0
82# TELEMETRY: fromhostdma_2[512].fromHostCycles: 0
83# TELEMETRY: readmem[32].addrCmdCycles: 0
84# TELEMETRY: readmem[32].addrCmdIssued: 0
85# TELEMETRY: readmem[32].addrCmdResponses: 0
86# TELEMETRY: readmem[32].lastReadLSB: 0
87# TELEMETRY: readmem[64].addrCmdCycles: 0
88# TELEMETRY: readmem[64].addrCmdIssued: 0
89# TELEMETRY: readmem[64].addrCmdResponses: 0
90# TELEMETRY: readmem[64].lastReadLSB: 0
91# TELEMETRY: readmem[128].addrCmdCycles: 0
92# TELEMETRY: readmem[128].addrCmdIssued: 0
93# TELEMETRY: readmem[128].addrCmdResponses: 0
94# TELEMETRY: readmem[128].lastReadLSB: 0
95# TELEMETRY: readmem[256].addrCmdCycles: 0
96# TELEMETRY: readmem[256].addrCmdIssued: 0
97# TELEMETRY: readmem[256].addrCmdResponses: 0
98# TELEMETRY: readmem[256].lastReadLSB: 0
99# TELEMETRY: readmem[512].addrCmdCycles: 0
100# TELEMETRY: readmem[512].addrCmdIssued: 0
101# TELEMETRY: readmem[512].addrCmdResponses: 0
102# TELEMETRY: readmem[512].lastReadLSB: 0
103# TELEMETRY: readmem[534].addrCmdCycles: 0
104# TELEMETRY: readmem[534].addrCmdIssued: 0
105# TELEMETRY: readmem[534].addrCmdResponses: 0
106# TELEMETRY: readmem[534].lastReadLSB: 0
107# TELEMETRY: readmem_0[512].addrCmdCycles: 0
108# TELEMETRY: readmem_0[512].addrCmdIssued: 0
109# TELEMETRY: readmem_0[512].addrCmdResponses: 0
110# TELEMETRY: readmem_0[512].lastReadLSB: 0
111# TELEMETRY: readmem_1[512].addrCmdCycles: 0
112# TELEMETRY: readmem_1[512].addrCmdIssued: 0
113# TELEMETRY: readmem_1[512].addrCmdResponses: 0
114# TELEMETRY: readmem_1[512].lastReadLSB: 0
115# TELEMETRY: readmem_2[512].addrCmdCycles: 0
116# TELEMETRY: readmem_2[512].addrCmdIssued: 0
117# TELEMETRY: readmem_2[512].addrCmdResponses: 0
118# TELEMETRY: readmem_2[512].lastReadLSB: 0
119# TELEMETRY: tohostdma[32].toHostCycles: 0
120# TELEMETRY: tohostdma[32].totalWrites: 0
121# TELEMETRY: tohostdma[64].toHostCycles: 0
122# TELEMETRY: tohostdma[64].totalWrites: 0
123# TELEMETRY: tohostdma[128].toHostCycles: 0
124# TELEMETRY: tohostdma[128].totalWrites: 0
125# TELEMETRY: tohostdma[256].toHostCycles: 0
126# TELEMETRY: tohostdma[256].totalWrites: 0
127# TELEMETRY: tohostdma[512].toHostCycles: 0
128# TELEMETRY: tohostdma[512].totalWrites: 0
129# TELEMETRY: tohostdma[534].toHostCycles: 0
130# TELEMETRY: tohostdma[534].totalWrites: 0
131# TELEMETRY: tohostdma_0[512].toHostCycles: 0
132# TELEMETRY: tohostdma_0[512].totalWrites: 0
133# TELEMETRY: tohostdma_1[512].toHostCycles: 0
134# TELEMETRY: tohostdma_1[512].totalWrites: 0
135# TELEMETRY: tohostdma_2[512].toHostCycles: 0
136# TELEMETRY: tohostdma_2[512].totalWrites: 0
137# TELEMETRY: writemem[32].addrCmdCycles: 0
138# TELEMETRY: writemem[32].addrCmdIssued: 0
139# TELEMETRY: writemem[32].addrCmdResponses: 0
140# TELEMETRY: writemem[64].addrCmdCycles: 0
141# TELEMETRY: writemem[64].addrCmdIssued: 0
142# TELEMETRY: writemem[64].addrCmdResponses: 0
143# TELEMETRY: writemem[128].addrCmdCycles: 0
144# TELEMETRY: writemem[128].addrCmdIssued: 0
145# TELEMETRY: writemem[128].addrCmdResponses: 0
146# TELEMETRY: writemem[256].addrCmdCycles: 0
147# TELEMETRY: writemem[256].addrCmdIssued: 0
148# TELEMETRY: writemem[256].addrCmdResponses: 0
149# TELEMETRY: writemem[512].addrCmdCycles: 0
150# TELEMETRY: writemem[512].addrCmdIssued: 0
151# TELEMETRY: writemem[512].addrCmdResponses: 0
152# TELEMETRY: writemem[534].addrCmdCycles: 0
153# TELEMETRY: writemem[534].addrCmdIssued: 0
154# TELEMETRY: writemem[534].addrCmdResponses: 0
155# TELEMETRY: writemem_0[512].addrCmdCycles: 0
156# TELEMETRY: writemem_0[512].addrCmdIssued: 0
157# TELEMETRY: writemem_0[512].addrCmdResponses: 0
158# TELEMETRY: writemem_1[512].addrCmdCycles: 0
159# TELEMETRY: writemem_1[512].addrCmdIssued: 0
160# TELEMETRY: writemem_1[512].addrCmdResponses: 0
161# TELEMETRY: writemem_2[512].addrCmdCycles: 0
162# TELEMETRY: writemem_2[512].addrCmdIssued: 0
163# TELEMETRY: writemem_2[512].addrCmdResponses: 0
164
165
166class CallbackTest(Module):
167 """Call a function on the host when an MMIO write is received at offset
168 0x10."""
169
170 # CHECK: 0
171 # CHECK: 1
172 # CHECK: 2
173 # CHECK: 3
174 # CHECK: 4
175
176 clk = Clock()
177 rst = Reset()
178
179 @generator
180 def construct(ports):
181 clk = ports.clk
182 rst = ports.rst
183
184 mmio_bundle = esi.MMIO.read_write(appid=AppID("cmd"))
185 data_resp_chan = Wire(Channel(Bits(64)))
186 mmio_cmd_chan = mmio_bundle.unpack(data=data_resp_chan)["cmd"]
187 cb_trigger, mmio_cmd_chan_fork = mmio_cmd_chan.fork(clk=clk, rst=rst)
188
189 data_resp_chan.assign(
190 mmio_cmd_chan_fork.transform(lambda cmd: Bits(64)(cmd.data)))
191
192 cb_trigger_ready = Wire(Bits(1))
193 cb_trigger_cmd, cb_trigger_valid = cb_trigger.unwrap(cb_trigger_ready)
194 trigger = cb_trigger_valid & (cb_trigger_cmd.offset == UInt(32)(0x10))
195 data_reg = cb_trigger_cmd.data.reg(clk, rst, ce=trigger)
196 cb_chan, cb_trigger_ready_sig = Channel(Bits(64)).wrap(
197 data_reg, trigger.reg(clk, rst))
198 cb_trigger_ready.assign(cb_trigger_ready_sig)
199 esi.CallService.call(AppID("cb"), cb_chan, Bits(0))
200
201
202class LoopbackInOutAdd(Module):
203 """Exposes a function which adds the 'add_amt' constant to the argument."""
204
205 clk = Clock()
206 rst = Reset()
207
208 add_amt = Constant(UInt(16), 11)
209
210 @generator
211 def construct(ports):
212 loopback = Wire(Channel(UInt(16), signaling=ChannelSignaling.FIFO))
213 args = esi.FuncService.get_call_chans(AppID("add"),
214 arg_type=UInt(24),
215 result=loopback)
216
217 ready = Wire(Bits(1))
218 data, valid = args.unwrap(ready)
219 plus7 = data + LoopbackInOutAdd.add_amt.value
220 data_chan, data_ready = Channel(UInt(16), ChannelSignaling.ValidReady).wrap(
221 plus7.as_uint(16), valid)
222 data_chan_buffered = data_chan.buffer(ports.clk, ports.rst, 1,
223 ChannelSignaling.FIFO)
224 ready.assign(data_ready)
225 loopback.assign(data_chan_buffered)
226
227
228@modparams
229def StreamingAdder(numItems: int):
230 """Creates a StreamingAdder module parameterized by the number of items per
231 window frame. The module exposes a function which has an argument of struct
232 {add_amt, list<uint32>}. It then adds add_amt to each element of the list in
233 parallel (numItems at a time) and returns the resulting list.
234 """
235
236 class StreamingAdder(Module):
237 clk = Clock()
238 rst = Reset()
239
240 @generator
241 def construct(ports):
242 from pycde.types import StructType, List, Window
243
244 # Define the argument type: struct { add_amt: UInt(32), list: List<UInt(32)> }
245 arg_struct_type = StructType([("add_amt", UInt(32)),
246 ("input", List(UInt(32)))])
247
248 # Create a windowed version with numItems parallel elements
249 arg_window_type = Window(
250 "arg_window", arg_struct_type,
251 [Window.Frame(None, ["add_amt", ("input", numItems)])])
252
253 # Result is also a List with numItems parallel elements
254 result_struct_type = StructType([("data", List(UInt(32)))])
255 result_window_type = Window("result_window", result_struct_type,
256 [Window.Frame(None, [("data", numItems)])])
257
258 result_chan = Wire(Channel(result_window_type))
259 args = esi.FuncService.get_call_chans(AppID("streaming_add"),
260 arg_type=arg_window_type,
261 result=result_chan)
262
263 # Unwrap the argument channel
264 ready = Wire(Bits(1))
265 arg_data, arg_valid = args.unwrap(ready)
266
267 # Unwrap the window to get the lowered struct
268 # Lowered type: struct { add_amt, input: array[numItems], input_size, last }
269 arg_unwrapped = arg_data.unwrap()
270
271 # Extract add_amt and input array from the struct
272 add_amt = arg_unwrapped["add_amt"]
273 input_arr = arg_unwrapped["input"]
274
275 # Perform all additions in parallel
276 result_arr = [
277 (add_amt + input_arr[i]).as_uint(32) for i in range(numItems)
278 ]
279
280 # Build the result lowered type
281 # Lowered type: struct { data: array[numItems], data_size, last }
282 lowered_val = result_window_type.lowered_type({
283 "data": result_arr,
284 "data_size": arg_unwrapped["input_size"],
285 "last": arg_unwrapped["last"]
286 })
287
288 result_window = result_window_type.wrap(lowered_val)
289
290 # Wrap the result into a channel
291 result_chan_internal, result_ready = Channel(result_window_type).wrap(
292 result_window, arg_valid)
293 ready.assign(result_ready)
294 result_chan.assign(result_chan_internal)
295
296 return StreamingAdder
297
298
299class CoordTranslator(Module):
300 """Exposes a function which takes a struct of {x_translation, y_translation,
301 coords: list<struct{x, y}>} and adds the translation to each coordinate,
302 returning the translated list of coordinates.
303 """
304
305 clk = Clock()
306 rst = Reset()
307
308 @generator
309 def construct(ports):
310 from pycde.types import StructType, List, Window
311
312 # Define the coordinate type: struct { x: UInt(32), y: UInt(32) }
313 coord_type = StructType([("x", UInt(32)), ("y", UInt(32))])
314
315 # Define the argument type: struct { x_translation, y_translation, coords }
316 arg_struct_type = StructType([("x_translation", UInt(32)),
317 ("y_translation", UInt(32)),
318 ("coords", List(coord_type))])
319
320 # Create a windowed version of the argument struct for streaming
321 arg_window_type = Window.default_of(arg_struct_type)
322
323 # Result is also a List of coordinates
324 result_type = List(coord_type)
325 result_window_type = Window.default_of(result_type)
326
327 result_chan = Wire(Channel(result_window_type))
328 args = esi.FuncService.get_call_chans(AppID("translate_coords"),
329 arg_type=arg_window_type,
330 result=result_chan)
331
332 # Unwrap the argument channel
333 ready = Wire(Bits(1))
334 arg_data, arg_valid = args.unwrap(ready)
335
336 # Unwrap the window to get the struct/union
337 arg_unwrapped = arg_data.unwrap()
338
339 # Extract translations and coordinates from the struct
340 x_translation = arg_unwrapped["x_translation"]
341 y_translation = arg_unwrapped["y_translation"]
342 input_coord = arg_unwrapped["coords"]
343
344 # Add translations to each coordinate
345 result_x = (x_translation + input_coord["x"]).as_uint(32)
346 result_y = (y_translation + input_coord["y"]).as_uint(32)
347
348 # Create the result coordinate struct
349 result_coord = coord_type({"x": result_x, "y": result_y})
350
351 result_window = result_window_type.wrap(
352 result_window_type.lowered_type({
353 "data": result_coord,
354 "last": arg_unwrapped.last
355 }))
356
357 # Wrap the result into a channel
358 result_chan_internal, result_ready = Channel(result_window_type).wrap(
359 result_window, arg_valid)
360 ready.assign(result_ready)
361 result_chan.assign(result_chan_internal)
362
363
365 """Like CoordTranslator, but uses the serial (bulk-transfer) list encoding.
366
367 Input wire format is a window with two frames:
368 - "header": {x_translation, y_translation, coords_count}
369 - "data": {coords[1]} (one coordinate per frame)
370
371 Output wire format is also a window with two frames:
372 - "header": {coords_count}
373 - "data": {coords[1]} (one coordinate per frame)
374
375 In bulk-transfer encoding, the sender may transmit multiple header/data
376 sequences to extend a list. A common pattern is to set coords_count=64 and
377 re-send a new header every 64 items; the final header has coords_count=0.
378 This module passes the header count through and translates each coordinate.
379 """
380
381 clk = Clock()
382 rst = Reset()
383
384 @generator
385 def construct(ports):
386 from pycde.types import List, StructType, Window
387
388 clk = ports.clk
389 rst = ports.rst
390
391 bulk_count_width = 16
392 items_per_frame = 1
393
394 coord_type = StructType([("x", Bits(32)), ("y", Bits(32))])
395
396 # ----- Input window type (serial/bulk transfer) -----
397 arg_struct_type = StructType([
398 ("x_translation", Bits(32)),
399 ("y_translation", Bits(32)),
400 ("coords", List(coord_type)),
401 ])
402 arg_window_type = Window(
403 "serial_coord_args",
404 arg_struct_type,
405 [
406 Window.Frame(
407 "header",
408 [
409 "x_translation",
410 "y_translation",
411 ("coords", 0, bulk_count_width),
412 ],
413 ),
414 Window.Frame(
415 "data",
416 [("coords", items_per_frame, 0)],
417 ),
418 ],
419 )
420
421 # ----- Output window type (serial/bulk transfer) -----
422 result_struct_type = StructType([("coords", List(coord_type))])
423 result_window_type = Window(
424 "serial_coord_result",
425 result_struct_type,
426 [
427 Window.Frame(
428 "header",
429 [("coords", 0, bulk_count_width)],
430 ),
431 Window.Frame(
432 "data",
433 [("coords", items_per_frame, 0)],
434 ),
435 ],
436 )
437
438 result_chan = Wire(Channel(result_window_type))
439 args = esi.FuncService.get_call_chans(
440 AppID("translate_coords_serial"),
441 arg_type=arg_window_type,
442 result=result_chan,
443 )
444
445 # Unwrap the argument channel.
446 in_ready = Wire(Bits(1))
447 in_window, in_valid = args.unwrap(in_ready)
448 in_union = in_window.unwrap()
449
450 hdr_frame = in_union["header"]
451 data_frame = in_union["data"]
452
453 hdr_x = hdr_frame["x_translation"].as_uint(32)
454 hdr_y = hdr_frame["y_translation"].as_uint(32)
455 hdr_count_bits = hdr_frame["coords_count"]
456 hdr_count = hdr_count_bits.as_uint(bulk_count_width)
457
458 out_hdr_struct_ty = result_window_type.lowered_type.header
459 out_data_struct_ty = result_window_type.lowered_type.data
460
461 # Output channel (built below) drives readiness/backpressure.
462 out_ready_wire = Wire(Bits(1))
463 handshake = in_valid & out_ready_wire
464
465 # Track which frame we're currently expecting.
466 in_is_header = Reg(
467 Bits(1),
468 clk=clk,
469 rst=rst,
470 rst_value=1,
471 ce=handshake,
472 name="in_is_header",
473 )
474 # Only log the frame count when the handshake is for a header frame.
475 hdr_handshake = handshake & in_is_header
476 hdr_handshake.when_true(
477 lambda: print_info("Received frame count=%d", hdr_count_bits))
478
479 # Latch the most recent header count for re-use when emitting the output
480 # header (do not rely on union extracts during data frames).
481 hdr_is_zero = hdr_count == UInt(bulk_count_width)(0)
482 footer_handshake = hdr_handshake & hdr_is_zero
483 start_handshake = hdr_handshake & ~hdr_is_zero
484 message_active = ControlReg(
485 clk,
486 rst,
487 asserts=[start_handshake],
488 resets=[footer_handshake],
489 name="message_active",
490 )
491 count_reg = Reg(
492 UInt(bulk_count_width),
493 clk=clk,
494 rst=rst,
495 rst_value=0,
496 ce=hdr_handshake,
497 name="coords_count",
498 )
499 count_reg.assign(hdr_count)
500
501 data_handshake = handshake & ~in_is_header
502 data_count = Counter(bulk_count_width)(
503 clk=clk,
504 rst=rst,
505 clear=hdr_handshake,
506 increment=data_handshake,
507 instance_name="data_count",
508 ).out
509
510 # Latch translations only on the first header of a message.
511 x_translation_reg = Reg(
512 UInt(32),
513 clk=clk,
514 rst=rst,
515 rst_value=0,
516 ce=start_handshake & ~message_active,
517 name="x_translation",
518 )
519 y_translation_reg = Reg(
520 UInt(32),
521 clk=clk,
522 rst=rst,
523 rst_value=0,
524 ce=start_handshake & ~message_active,
525 name="y_translation",
526 )
527 x_translation_reg.assign(hdr_x)
528 y_translation_reg.assign(hdr_y)
529
530 # Next-state logic for header/data tracking.
531 count_minus_one = (count_reg -
532 UInt(bulk_count_width)(1)).as_uint(bulk_count_width)
533 data_last = data_count == count_minus_one
534 next_is_header = Mux(in_is_header, data_last, hdr_is_zero)
535 in_is_header.assign(next_is_header)
536
537 # Build output frames.
538 out_hdr_struct = out_hdr_struct_ty(
539 {"coords_count": hdr_count.as_bits(bulk_count_width)})
540
541 in_coord = data_frame["coords"][0]
542 in_x = in_coord["x"].as_uint(32)
543 in_y = in_coord["y"].as_uint(32)
544 translated_x = (x_translation_reg + in_x).as_uint(32)
545 translated_y = (y_translation_reg + in_y).as_uint(32)
546 out_coord = coord_type({
547 "x": translated_x.as_bits(32),
548 "y": translated_y.as_bits(32),
549 })
550 out_data_struct = out_data_struct_ty({"coords": [out_coord]})
551
552 out_union_hdr = result_window_type.lowered_type(("header", out_hdr_struct))
553 out_union_data = result_window_type.lowered_type(("data", out_data_struct))
554 out_union = Mux(in_is_header, out_union_data, out_union_hdr)
555 out_window = result_window_type.wrap(out_union)
556
557 out_chan, out_ready = Channel(result_window_type).wrap(out_window, in_valid)
558 out_ready_wire.assign(out_ready)
559
560 in_ready.assign(out_ready)
561 result_chan.assign(out_chan)
562
563
564@modparams
565def MMIOAdd(add_amt: int) -> Type[Module]:
566
567 class MMIOAdd(Module):
568 """Exposes an MMIO address space wherein MMIO reads return the <address
569 offset into its space> + add_amt."""
570
571 metadata = Metadata(
572 name="MMIOAdd",
573 misc={"add_amt": add_amt},
574 )
575
576 add_amt_const = Constant(UInt(32), add_amt)
577
578 @generator
579 def build(ports):
580 mmio_read_bundle = esi.MMIO.read(appid=AppID("mmio_client", add_amt))
581
582 address_chan_wire = Wire(Channel(UInt(32)))
583 address, address_valid = address_chan_wire.unwrap(1)
584 response_data = (address.as_uint() + add_amt).as_bits(64)
585 response_chan, response_ready = Channel(Bits(64)).wrap(
586 response_data, address_valid)
587
588 address_chan = mmio_read_bundle.unpack(data=response_chan)["offset"]
589 address_chan_wire.assign(address_chan)
590
591 return MMIOAdd
592
593
594@modparams
595def AddressCommand(width: int):
596
597 class AddressCommand(Module):
598 """Constructs an module which takes MMIO commands and issues host memory
599 commands based on those commands. Tracks the number of cycles to issue
600 addresses and get all of the expected responses.
601
602 MMIO offsets:
603 0x10: Starting address for host memory operations.
604 0x18: Number of flits to read/write.
605 0x20: Start read/write operation.
606 """
607
608 clk = Clock()
609 rst = Reset()
610
611 # Number of flits left to issue.
612 flits_left = Output(UInt(64))
613 # Signal to start the operation.
614 command_go = Output(Bits(1))
615
616 # Channel which issues hostmem addresses. Must be transformed into
617 # read/write requests by the instantiator.
618 hostmem_cmd_address = OutputChannel(UInt(64))
619
620 # Channel which indicates when the read/write operation is done.
621 hostmem_cmd_done = InputChannel(Bits(0))
622
623 @generator
624 def construct(ports):
625 # MMIO command channel setup.
626 cmd_chan_wire = Wire(Channel(esi.MMIOReadWriteCmdType))
627 resp_ready_wire = Wire(Bits(1))
628 cmd, cmd_valid = cmd_chan_wire.unwrap(resp_ready_wire)
629 mmio_xact = cmd_valid & resp_ready_wire
630
631 # Write enables.
632 start_addr_we = (mmio_xact & cmd.write & (cmd.offset == UInt(32)(0x10)))
633 flits_we = mmio_xact & cmd.write & (cmd.offset == UInt(32)(0x18))
634 start_op_we = mmio_xact & cmd.write & (cmd.offset == UInt(32)(0x20))
635 ports.command_go = start_op_we
636
637 # Registers for start address and number of flits.
638 start_addr = cmd.data.as_uint().reg(
639 clk=ports.clk,
640 rst=ports.rst,
641 rst_value=0,
642 ce=start_addr_we,
643 name="start_addr",
644 )
645 flits_total = cmd.data.as_uint().reg(
646 clk=ports.clk,
647 rst=ports.rst,
648 rst_value=0,
649 ce=flits_we,
650 name="flits_total",
651 )
652
653 # Response counter.
654 responses_incr = Wire(Bits(1))
655 responses_cnt = Counter(64)(
656 clk=ports.clk,
657 rst=ports.rst,
658 clear=start_op_we,
659 increment=responses_incr,
660 instance_name="addr_cmd_responses_cnt",
661 )
662
663 operation_done = responses_cnt.out.as_uint() == flits_total
664 operation_active = ControlReg(
665 clk=ports.clk,
666 rst=ports.rst,
667 asserts=[start_op_we],
668 resets=[operation_done],
669 name="operation_active",
670 )
671 # Cycle counter while active.
672 cycles_cnt = Counter(64)(
673 clk=ports.clk,
674 rst=ports.rst,
675 clear=start_op_we,
676 increment=operation_active,
677 instance_name="addr_cmd_cycle_counter",
678 )
679 # Latch final cycle count at completion.
680 final_cycles = Reg(
681 UInt(64),
682 clk=ports.clk,
683 rst=ports.rst,
684 rst_value=0,
685 ce=operation_done,
686 name="addr_cmd_cycles",
687 )
688 final_cycles.assign(cycles_cnt.out.as_uint())
689
690 # Issue counter.
691 issue_incr = Wire(Bits(1))
692 issue_cnt = Counter(64)(
693 clk=ports.clk,
694 rst=ports.rst,
695 clear=start_op_we,
696 increment=issue_incr,
697 )
698
699 # Increment by number of bytes per flit, rounded up to nearest 32
700 # bits (double word).
701 incr_bytes = UInt(64)(((width + 31) // 32) * 4)
702
703 # Generate current address.
704 current_addr = (start_addr +
705 (issue_cnt.out.as_uint() * incr_bytes)).as_uint(64)
706
707 # Valid when active and still have flits to issue.
708 addr_valid = operation_active & (issue_cnt.out.as_uint() < flits_total)
709 addr_chan, addr_ready = Channel(UInt(64),
710 ChannelSignaling.ValidReady).wrap(
711 current_addr, addr_valid)
712 issue_xact = addr_valid & addr_ready
713 issue_incr.assign(issue_xact)
714
715 # Consume hostmem_cmd_done (Bits(0) channel) for completed responses.
716 _, done_valid = ports.hostmem_cmd_done.unwrap(Bits(1)(1))
717 responses_incr.assign(done_valid)
718
719 # flits_left = total - responses received.
720 flits_left_val = (flits_total - responses_cnt.out.as_uint()).as_uint(64)
721 ports.flits_left = flits_left_val # direct assignment
722
723 # Drive output channel.
724 ports.hostmem_cmd_address = addr_chan # direct assignment
725
726 # MMIO read response: return flits_left.
727 response_data = flits_left_val.as_bits(64)
728 response_chan, response_ready = Channel(Bits(64)).wrap(
729 response_data, cmd_valid)
730 resp_ready_wire.assign(response_ready)
731
732 mmio_rw = esi.MMIO.read_write(appid=AppID("cmd", width))
733 mmio_rw_cmd_chan = mmio_rw.unpack(data=response_chan)["cmd"]
734 cmd_chan_wire.assign(mmio_rw_cmd_chan)
735
736 # Report telemetry.
737 esi.Telemetry.report_signal(
738 ports.clk,
739 ports.rst,
740 esi.AppID("addrCmdCycles"),
741 final_cycles,
742 )
743 esi.Telemetry.report_signal(
744 ports.clk,
745 ports.rst,
746 esi.AppID("addrCmdIssued"),
747 issue_cnt.out,
748 )
749 esi.Telemetry.report_signal(
750 ports.clk,
751 ports.rst,
752 esi.AppID("addrCmdResponses"),
753 responses_cnt.out,
754 )
755
756 return AddressCommand
757
758
759@modparams
760def ReadMem(width: int):
761
762 class ReadMem(Module):
763 """Host memory read test module.
764
765 Function:
766 Issues a sequence of host memory read requests using an internal
767 address/control submodule which is configured via MMIO writes. Each
768 read returns 'width' bits; the low 64 bits of the most recent
769 response are latched and exported as telemetry (lastReadLSB).
770
771 Flit width:
772 'width' is the number of payload data bits per read flit. The address
773 stride between successive requests is ceil(width/32) 32-bit words
774 (= ceil(width/32) * 4 bytes). Non–power‑of‑two widths are supported
775 and packed into the minimum whole 32‑bit word count.
776
777 MMIO command interface:
778 0x10 Write: Starting base address for the read operation.
779 0x18 Write: Number of flits (read transactions) to perform.
780 0x20 Write: Start the operation (assert once to launch).
781 Reads return the current flits_left (remaining responses).
782
783 Operation:
784 After 0x20 is written, sequential addresses are generated:
785 addr = start_addr + i * ceil(width/32) (i = 0 .. flits-1)
786 Each address produces one host memory read request.
787
788 Telemetry (AppID -> signal):
789 addrCmdCycles Total cycles elapsed during the active window.
790 addrCmdIssued Count of host memory commands issued.
791 addrCmdResponses Count of host memory responses received.
792 lastReadLSB Low 64 bits of the most recently received read data.
793
794 Notes:
795 Backpressure on the read response channel naturally throttles issue.
796 Completion occurs when responses == requested flits.
797 """
798
799 clk = Clock()
800 rst = Reset()
801
802 width_bits = Constant(UInt(32), width)
803
804 @generator
805 def construct(ports):
806 clk = ports.clk
807 rst = ports.rst
808
809 address_cmd_resp = Wire(Channel(Bits(0)))
810 addresses = AddressCommand(width)(
811 clk=clk,
812 rst=rst,
813 hostmem_cmd_done=address_cmd_resp,
814 instance_name="address_command",
815 )
816
817 read_cmd_chan = addresses.hostmem_cmd_address.transform(
818 lambda addr: esi.HostMem.ReadReqType({
819 "tag": UInt(8)(0),
820 "address": addr
821 }))
822 read_responses = esi.HostMem.read(
823 appid=AppID("host"),
824 data_type=Bits(width),
825 req=read_cmd_chan,
826 )
827 # Signal completion to AddressCommand (each response -> Bits(0)).
828 address_cmd_resp.assign(read_responses.transform(lambda resp: Bits(0)(0)))
829 # Snoop the response channel to capture the low 64 bits without consuming it.
830 read_resp_valid, _, read_resp_data = read_responses.snoop()
831 last_read_lsb = Reg(
832 UInt(64),
833 clk=ports.clk,
834 rst=ports.rst,
835 rst_value=0,
836 ce=read_resp_valid,
837 name="last_read_lsb",
838 )
839 last_read_lsb.assign(read_resp_data.data.as_uint(64))
840 esi.Telemetry.report_signal(
841 ports.clk,
842 ports.rst,
843 esi.AppID("lastReadLSB"),
844 last_read_lsb,
845 )
846
847 return ReadMem
848
849
850@modparams
851def WriteMem(width: int) -> Type[Module]:
852
853 class WriteMem(Module):
854 """Host memory write test module.
855
856 Function:
857 Issues sequential host memory write requests produced by an internal
858 address/control submodule configured via MMIO writes. Data for each
859 flit is the current free‑running 32‑bit cycle counter value
860 (zero‑extended or truncated to 'width').
861
862 Flit width:
863 'width' is the number of payload data bits per write flit. The address
864 stride between successive writes is ceil(width/32) 32‑bit words
865 (= ceil(width/32) * 4 bytes). Wider payloads span multiple words;
866 narrower payloads still consume one word of address space per flit.
867
868 MMIO command interface:
869 0x10 Write: Starting base address for the write operation.
870 0x18 Write: Number of flits (write transactions) to perform.
871 0x20 Write: Start the operation (assert once to launch).
872 Reads return the current flits_left (remaining responses).
873
874 Data pattern:
875 data[i] = cycle_counter sampled when the write command for flit i is formed.
876
877 Telemetry (AppID -> signal):
878 addrCmdCycles Total cycles elapsed during the active window.
879 addrCmdIssued Count of host memory commands issued.
880 addrCmdResponses Count of host memory responses received.
881
882 Notes:
883 No additional telemetry beyond the above signals is generated here.
884 Completion occurs when write responses == requested flits.
885 """
886
887 clk = Clock()
888 rst = Reset()
889
890 width_bits = Constant(UInt(32), width)
891
892 @generator
893 def construct(ports):
894 clk = ports.clk
895 rst = ports.rst
896
897 cycle_counter_reset = Wire(Bits(1))
898 cycle_counter = Counter(32)(
899 clk=clk,
900 rst=rst,
901 clear=Bits(1)(0),
902 increment=Bits(1)(1),
903 )
904
905 address_cmd_resp = Wire(Channel(Bits(0)))
906 addresses = AddressCommand(width)(
907 clk=clk,
908 rst=rst,
909 hostmem_cmd_done=address_cmd_resp,
910 instance_name="address_command",
911 )
912 cycle_counter_reset.assign(addresses.command_go)
913
914 write_cmd_chan = addresses.hostmem_cmd_address.transform(
915 lambda addr: esi.HostMem.write_req_channel_type(UInt(width))({
916 "tag": UInt(8)(0),
917 "address": addr,
918 "data": cycle_counter.out.as_uint(width),
919 }))
920
921 write_responses = esi.HostMem.write(
922 appid=AppID("host"),
923 req=write_cmd_chan,
924 )
925 # Signal completion to AddressCommand (each response -> Bits(0)).
926 address_cmd_resp.assign(
927 write_responses.transform(lambda resp: Bits(0)(0)))
928
929 return WriteMem
930
931
932@modparams
933def ToHostDMATest(width: int):
934 """Construct a module that sends the write count over a channel to the host
935 the specified number of times. Exercises any DMA engine."""
936
937 class ToHostDMATest(Module):
938 """Transmit a 32-bit cycle counter value to the host a programmed number of times.
939
940 Functionality:
941 A free-running 32-bit counter advances only on successful channel
942 handshakes. A write to MMIO offset 0x0 programs 'write_count' (number
943 of messages to send). Each message’s payload is the counter value
944 constrained to 'width':
945 width < 32 -> lower 'width' bits (truncated)
946 width >= 32 -> zero-extended to 'width' bits
947 One message is emitted per handshake until 'write_count' messages have
948 been transferred. A new write to 0x0 re-arms after completion.
949
950 Width:
951 Selects how the 32-bit counter is represented on the output channel
952 (truncate or zero-extend as above).
953
954 MMIO command interface:
955 0x0 Write: Set write_count (messages to transmit). Starts a new
956 sequence if idle/completed.
957 0x0 Read: Returns constant 0.
958
959 Telemetry:
960 totalWrites (AppID "totalWrites"): Count of messages successfully sent.
961 toHostCycles (AppID "toHostCycles"): Cycle count from write_count programming
962 (start) through completion (inclusive of active cycles).
963
964 Notes:
965 Backpressure governs pacing. Completion when totalWrites == write_count.
966 """
967
968 clk = Clock()
969 rst = Reset()
970
971 width_bits = Constant(UInt(32), width)
972
973 @generator
974 def construct(ports):
975 count_reached = Wire(Bits(1))
976 count_valid = Wire(Bits(1))
977 out_xact = Wire(Bits(1))
978 cycle_counter = Counter(32)(
979 clk=ports.clk,
980 rst=ports.rst,
981 clear=Bits(1)(0),
982 increment=out_xact,
983 )
984
985 write_cntr_incr = ~count_reached & count_valid & out_xact
986 write_counter = Counter(32)(
987 clk=ports.clk,
988 rst=ports.rst,
989 clear=count_reached,
990 increment=write_cntr_incr,
991 )
992 num_writes = write_counter.out
993
994 # Get the MMIO space for commands.
995 cmd_chan_wire = Wire(Channel(esi.MMIOReadWriteCmdType))
996 resp_ready_wire = Wire(Bits(1))
997 cmd, cmd_valid = cmd_chan_wire.unwrap(resp_ready_wire)
998 mmio_xact = cmd_valid & resp_ready_wire
999 response_data = Bits(64)(0)
1000 response_chan, response_ready = Channel(response_data.type).wrap(
1001 response_data, cmd_valid)
1002 resp_ready_wire.assign(response_ready)
1003
1004 # write_count is the specified number of times to send the cycle count.
1005 write_count_ce = mmio_xact & cmd.write & (cmd.offset == UInt(32)(0))
1006 write_count = cmd.data.as_uint().reg(clk=ports.clk,
1007 rst=ports.rst,
1008 rst_value=0,
1009 ce=write_count_ce)
1010 count_reached.assign(num_writes == write_count)
1011 count_valid.assign(
1012 ControlReg(
1013 clk=ports.clk,
1014 rst=ports.rst,
1015 asserts=[write_count_ce],
1016 resets=[count_reached],
1017 ))
1018
1019 mmio_rw = esi.MMIO.read_write(appid=AppID("cmd"))
1020 mmio_rw_cmd_chan = mmio_rw.unpack(data=response_chan)["cmd"]
1021 cmd_chan_wire.assign(mmio_rw_cmd_chan)
1022
1023 # Output channel.
1024 out_channel, out_channel_ready = Channel(UInt(width)).wrap(
1025 cycle_counter.out.as_uint(width), count_valid)
1026 out_xact.assign(out_channel_ready & count_valid)
1027 esi.ChannelService.to_host(name=AppID("out"), chan=out_channel)
1028
1029 total_write_counter = Counter(64)(
1030 clk=ports.clk,
1031 rst=ports.rst,
1032 clear=Bits(1)(0),
1033 increment=write_cntr_incr,
1034 )
1035 esi.Telemetry.report_signal(
1036 ports.clk,
1037 ports.rst,
1038 esi.AppID("totalWrites"),
1039 total_write_counter.out,
1040 )
1041
1042 # Cycle telemetry: count cycles while sequence active.
1043 tohost_cycle_cnt = Counter(64)(
1044 clk=ports.clk,
1045 rst=ports.rst,
1046 clear=write_count_ce,
1047 increment=count_valid,
1048 instance_name="tohost_cycle_counter",
1049 )
1050 tohost_final_cycles = Reg(
1051 UInt(64),
1052 clk=ports.clk,
1053 rst=ports.rst,
1054 rst_value=0,
1055 ce=count_reached,
1056 name="tohost_cycles",
1057 )
1058 tohost_final_cycles.assign(tohost_cycle_cnt.out.as_uint())
1059 esi.Telemetry.report_signal(
1060 ports.clk,
1061 ports.rst,
1062 esi.AppID("toHostCycles"),
1063 tohost_final_cycles,
1064 )
1065
1066 return ToHostDMATest
1067
1068
1069@modparams
1070def FromHostDMATest(width: int):
1071 """Construct a module that receives the write count over a channel from the
1072 host the specified number of times. Exercises any DMA engine."""
1073
1074 class FromHostDMATest(Module):
1075 """Receive test data from the host a programmed number of times.
1076
1077 Functionality:
1078 A write to MMIO offset 0x0 programs 'read_count', the number of messages
1079 to accept from the host. The input channel (AppID "in") is marked ready
1080 while the number of received messages is less than 'read_count'. Each
1081 received width-bit payload is latched; the most recent value is exposed
1082 on MMIO reads.
1083
1084 Width:
1085 'width' is the payload bit width of each received message. The latched
1086 value is widened/truncated to 64 bits for MMIO read-back (lower 64 bits
1087 if width > 64).
1088
1089 MMIO command interface:
1090 0x0 Write: Set read_count (number of messages to receive). Clears the
1091 internal receive counter.
1092 0x0 Read: Returns the last received value (Bits(64), derived from the
1093 width-bit payload).
1094
1095 Telemetry:
1096 fromHostCycles (AppID "fromHostCycles"): Cycle count from read_count programming
1097 (start) through completion of the programmed receive sequence.
1098
1099 Notes:
1100 Completion is when received messages == programmed read_count; another
1101 write to 0x0 re-arms for a new sequence.
1102 """
1103
1104 clk = Clock()
1105 rst = Reset()
1106
1107 width_bits = Constant(UInt(32), width)
1108
1109 @generator
1110 def build(ports):
1111 last_read = Wire(UInt(width))
1112
1113 # Get the MMIO space for commands.
1114 cmd_chan_wire = Wire(Channel(esi.MMIOReadWriteCmdType))
1115 resp_ready_wire = Wire(Bits(1))
1116 cmd, cmd_valid = cmd_chan_wire.unwrap(resp_ready_wire)
1117 mmio_xact = cmd_valid & resp_ready_wire
1118 response_data = last_read.as_bits(64)
1119 response_chan, response_ready = Channel(response_data.type).wrap(
1120 response_data, cmd_valid)
1121 resp_ready_wire.assign(response_ready)
1122
1123 # read_count is the specified number of times to recieve data.
1124 read_count_ce = mmio_xact & cmd.write & (cmd.offset == UInt(32)(0))
1125 read_count = cmd.data.as_uint().reg(clk=ports.clk,
1126 rst=ports.rst,
1127 rst_value=0,
1128 ce=read_count_ce)
1129 in_data_xact = NamedWire(Bits(1), "in_data_xact")
1130 read_counter = Counter(32)(
1131 clk=ports.clk,
1132 rst=ports.rst,
1133 clear=read_count_ce,
1134 increment=in_data_xact,
1135 )
1136
1137 mmio_rw = esi.MMIO.read_write(appid=AppID("cmd"))
1138 mmio_rw_cmd_chan = mmio_rw.unpack(data=response_chan)["cmd"]
1139 cmd_chan_wire.assign(mmio_rw_cmd_chan)
1140
1141 in_chan = esi.ChannelService.from_host(name=AppID("in"), type=UInt(width))
1142 in_ready = NamedWire(read_counter.out < read_count, "in_ready")
1143 in_data, in_valid = in_chan.unwrap(in_ready)
1144 NamedWire(in_data, "in_data")
1145 in_data_xact.assign(in_valid & in_ready)
1146
1147 last_read.assign(
1148 in_data.reg(
1149 clk=ports.clk,
1150 rst=ports.rst,
1151 ce=in_data_xact,
1152 name="last_read",
1153 ))
1154
1155 # Cycle telemetry: detect completion and count active cycles.
1156 fromhost_count_reached = Wire(Bits(1))
1157 fromhost_count_reached.assign(read_counter.out == read_count)
1158 fromhost_cycle_valid = ControlReg(
1159 clk=ports.clk,
1160 rst=ports.rst,
1161 asserts=[read_count_ce],
1162 resets=[fromhost_count_reached],
1163 name="fromhost_cycle_active",
1164 )
1165 fromhost_cycle_cnt = Counter(64)(
1166 clk=ports.clk,
1167 rst=ports.rst,
1168 clear=read_count_ce,
1169 increment=fromhost_cycle_valid,
1170 instance_name="fromhost_cycle_counter",
1171 )
1172 fromhost_final_cycles = Reg(
1173 UInt(64),
1174 clk=ports.clk,
1175 rst=ports.rst,
1176 rst_value=0,
1177 ce=fromhost_count_reached,
1178 name="fromhost_cycles",
1179 )
1180 fromhost_final_cycles.assign(fromhost_cycle_cnt.out.as_uint())
1181 esi.Telemetry.report_signal(
1182 ports.clk,
1183 ports.rst,
1184 esi.AppID("fromHostCycles"),
1185 fromhost_final_cycles,
1186 )
1187
1188 return FromHostDMATest
1189
1190
1191class EsiTester(Module):
1192 """Top-level ESI test harness module.
1193
1194 Contains submodules:
1195 CallbackTest (single instance) – host callback via MMIO write (offset 0x10).
1196 LoopbackInOutAdd (single instance) – function service adding constant 11.
1197 MMIOAdd(add_amt) instances for add_amt in {4, 9, 14} – MMIO read returns offset + add_amt.
1198 ReadMem(width) for widths: 32, 64, 128, 256, 512, 534 – host memory read tests.
1199 WriteMem(width) for widths: 32, 64, 128, 256, 512, 534 – host memory write tests.
1200 ToHostDMATest(width) for widths: 32, 64, 128, 256, 512, 534 – DMA to host, cycle & count telemetry.
1201 FromHostDMATest(width) for widths: 32, 64, 128, 256, 512, 534 – DMA from host, cycle telemetry.
1202
1203 Width set used across Read/Write/DMA tests:
1204 widths = [32, 64, 128, 256, 512, 534]
1205
1206 Purpose:
1207 Aggregates all functional, MMIO, host memory, and DMA tests into one image
1208 for comprehensive accelerator validation and telemetry collection.
1209 """
1210
1211 clk = Clock()
1212 rst = Reset()
1213
1214 @generator
1215 def construct(ports):
1217 clk=ports.clk,
1218 rst=ports.rst,
1219 instance_name="cb_test",
1220 appid=AppID("cb_test"),
1221 )
1223 clk=ports.clk,
1224 rst=ports.rst,
1225 instance_name="loopback",
1226 appid=AppID("loopback"),
1227 )
1228 StreamingAdder(1)(
1229 clk=ports.clk,
1230 rst=ports.rst,
1231 instance_name="streaming_adder",
1232 appid=AppID("streaming_adder"),
1233 )
1235 clk=ports.clk,
1236 rst=ports.rst,
1237 instance_name="coord_translator",
1238 appid=AppID("coord_translator"),
1239 )
1241 clk=ports.clk,
1242 rst=ports.rst,
1243 instance_name="coord_translator_serial",
1244 appid=AppID("coord_translator_serial"),
1245 )
1246
1247 for i in range(4, 18, 5):
1248 MMIOAdd(i)(instance_name=f"mmio_add_{i}", appid=AppID("mmio_add", i))
1249
1250 for width in [32, 64, 128, 256, 512, 534]:
1251 ReadMem(width)(
1252 instance_name=f"readmem_{width}",
1253 appid=esi.AppID("readmem", width),
1254 clk=ports.clk,
1255 rst=ports.rst,
1256 )
1257 WriteMem(width)(
1258 instance_name=f"writemem_{width}",
1259 appid=AppID("writemem", width),
1260 clk=ports.clk,
1261 rst=ports.rst,
1262 )
1263 ToHostDMATest(width)(
1264 instance_name=f"tohostdma_{width}",
1265 appid=AppID("tohostdma", width),
1266 clk=ports.clk,
1267 rst=ports.rst,
1268 )
1269 FromHostDMATest(width)(
1270 instance_name=f"fromhostdma_{width}",
1271 appid=AppID("fromhostdma", width),
1272 clk=ports.clk,
1273 rst=ports.rst,
1274 )
1275
1276 for i in range(3):
1277 ReadMem(512)(
1278 instance_name=f"readmem_{i}",
1279 appid=esi.AppID(f"readmem_{i}", 512),
1280 clk=ports.clk,
1281 rst=ports.rst,
1282 )
1283 WriteMem(512)(
1284 instance_name=f"writemem_{i}",
1285 appid=AppID(f"writemem_{i}", 512),
1286 clk=ports.clk,
1287 rst=ports.rst,
1288 )
1289 ToHostDMATest(512)(
1290 instance_name=f"tohostdma_{i}",
1291 appid=AppID(f"tohostdma_{i}", 512),
1292 clk=ports.clk,
1293 rst=ports.rst,
1294 )
1295 FromHostDMATest(512)(
1296 instance_name=f"fromhostdma_{i}",
1297 appid=AppID(f"fromhostdma_{i}", 512),
1298 clk=ports.clk,
1299 rst=ports.rst,
1300 )
1301
1302
1303if __name__ == "__main__":
1304 bsp = get_bsp(sys.argv[2])
1305 s = System(bsp(EsiTester), name="EsiTester", output_directory=sys.argv[1])
1306 s.compile()
1307 s.package()
return wrap(CMemoryType::get(unwrap(ctx), baseType, numElements))
ToHostDMATest(int width)
Definition esitester.py:933
AddressCommand(int width)
Definition esitester.py:595
StreamingAdder(int numItems)
Definition esitester.py:229
Type[Module] WriteMem(int width)
Definition esitester.py:851
ReadMem(int width)
Definition esitester.py:760
Type[Module] MMIOAdd(int add_amt)
Definition esitester.py:565
FromHostDMATest(int width)