CIRCT 24.0.0git
Loading...
Searching...
No Matches
test_esitester.py
Go to the documentation of this file.
1# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
2# See https://llvm.org/LICENSE.txt for license information.
3# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5from __future__ import annotations
6
7import json
8from pathlib import Path
9import subprocess
10
11import esiaccel
12from esiaccel.accelerator import AcceleratorConnection
13from esiaccel.cosim.pytest import cosim_test
14import esiaccel.types as types
15import pytest
16
17from .conftest import HW_DIR, check_lines, require_tool, run_cmd
18
19
20@cosim_test(HW_DIR / "esitester.py", args=("{tmp_dir}", "cosim"))
22
23 def setup_method(self) -> None:
24 require_tool("esitester")
25 require_tool("esiquery")
26
27 def test_callback(self, host: str, port: int) -> None:
28 conn = f"{host}:{port}"
29 stdout = run_cmd(["esitester", "-v", "cosim", conn, "callback", "-i", "5"])
30 check_lines(stdout, [
31 "[CONNECT] connecting to backend",
32 ])
33 # The callback loop should print values 0 through 4.
34 for i in range(5):
35 assert f"callback: {i}" in stdout, \
36 f"Expected 'callback: {i}' in stdout"
37
38 def test_streaming_add(self, host: str, port: int) -> None:
39 conn = f"{host}:{port}"
40 stdout = run_cmd(["esitester", "cosim", conn, "streaming_add"])
41 check_lines(stdout, [
42 "Streaming add test results:",
43 "input[0]=222709 + 5 = 222714 (expected 222714)",
44 "input[1]=894611 + 5 = 894616 (expected 894616)",
45 "input[2]=772894 + 5 = 772899 (expected 772899)",
46 "input[3]=429150 + 5 = 429155 (expected 429155)",
47 "input[4]=629806 + 5 = 629811 (expected 629811)",
48 "Streaming add test passed",
49 ])
50
51 def test_streaming_add_quiet(self, host: str, port: int) -> None:
52 conn = f"{host}:{port}"
53 stdout = run_cmd(["esitester", "cosim", conn, "streaming_add", "-t"])
54 check_lines(stdout, [
55 "Streaming add test results:",
56 "Streaming add test passed",
57 ])
58
59 def test_translate_coords(self, host: str, port: int) -> None:
60 conn = f"{host}:{port}"
61 stdout = run_cmd(["esitester", "cosim", conn, "translate_coords"])
62 check_lines(stdout, [
63 "Coord translate test results:",
64 "coord[0]=(222709,894611) + (10,20) = (222719,894631)",
65 "coord[1]=(772894,429150) + (10,20) = (772904,429170)",
66 "coord[2]=(629806,138727) + (10,20) = (629816,138747)",
67 "coord[3]=(218516,390276) + (10,20) = (218526,390296)",
68 "coord[4]=(750021,423525) + (10,20) = (750031,423545)",
69 "Coord translate test passed",
70 ])
71
72 def test_serial_coords(self, host: str, port: int) -> None:
73 conn = f"{host}:{port}"
74 stdout = run_cmd(
75 ["esitester", "cosim", conn, "serial_coords", "-n", "40", "-b", "33"])
76 check_lines(stdout, [
77 "Serial coord translate test results:",
78 "coord[0]=",
79 "Serial coord translate test passed",
80 ])
81
82 def test_auto_serial_coords(self, host: str, port: int) -> None:
83 conn = f"{host}:{port}"
84 stdout = run_cmd(
85 ["esitester", "cosim", conn, "auto_serial_coords", "-n", "5"])
86 check_lines(stdout, [
87 "Auto serial coord translate test results:",
88 "coord[0]=",
89 "Auto serial coord translate test passed",
90 ])
91
92 def test_channel(self, host: str, port: int) -> None:
93 conn = f"{host}:{port}"
94 stdout = run_cmd(["esitester", "cosim", conn, "channel", "-i", "3"])
95 check_lines(stdout, [
96 "[channel] producer i=0 got=0",
97 "[channel] producer i=1 got=1",
98 "[channel] producer i=2 got=2",
99 "[channel] loopback i=0",
100 "[channel] loopback i=1",
101 "[channel] loopback i=2",
102 "Channel test passed",
103 ])
104
105 def test_telemetry(self, host: str, port: int) -> None:
106 conn = f"{host}:{port}"
107 result = subprocess.run(
108 ["esiquery", "cosim", conn, "telemetry"],
109 check=False,
110 capture_output=True,
111 text=True,
112 )
113 assert result.returncode == 0, result.stdout + result.stderr
114 stdout = result.stdout
115 check_lines(stdout, [
116 "* Telemetry",
117 "fromhostdma[32].fromHostChecksum: 0",
118 "fromhostdma[32].fromHostCycles: 0",
119 "readmem[32].addrCmdIssued: 0",
120 "readmem[32].addrCmdResponses: 0",
121 "readmem[32].lastReadLSB: 0",
122 "readmem[32].readChecksum: 0",
123 "tohostdma[32].toHostCycles: 0",
124 "tohostdma[32].totalWrites: 0",
125 "writemem[32].addrCmdIssued: 0",
126 "writemem[32].addrCmdResponses: 0",
127 "readmem[32].addrCmdResp.cycles: 0",
128 "writemem[32].addrCmdResp.cycles: 0",
129 ])
130
131 def test_reset(self, host: str, port: int) -> None:
132 conn = f"{host}:{port}"
133 stdout = run_cmd(["esitester", "cosim", conn, "reset"])
134 check_lines(stdout, [
135 "[reset] reset requested",
136 "[reset] telemetry addrCmdResponses after reset = 0",
137 "Reset test passed",
138 ])
139
140 def test_channel_python(self, conn: AcceleratorConnection) -> None:
141 """Test ChannelService ToHost and FromHost ports from Python."""
142 acc = conn.build_accelerator()
143 channel_test = acc.children[esiaccel.AppID("channel_test")]
144 ports = channel_test.ports
145
146 # Get the MMIO port and trigger the producer to send 5 values.
147 mmio = ports[esiaccel.AppID("cmd")]
148 assert isinstance(mmio, types.MMIORegion), \
149 f"Expected MMIORegion, got {type(mmio)}"
150
151 producer = ports[esiaccel.AppID("producer")]
152 assert isinstance(producer, types.ToHostPort), \
153 f"Expected ToHostPort, got {type(producer)}"
154 producer.connect()
155
156 num_values = 5
157 mmio.write(0x0, num_values)
158 for i in range(num_values):
159 result = producer.read().result()
160 assert result == i, f"Producer: expected {i}, got {result}"
161
162 # Test from_host -> to_host loopback.
163 loopback_in = ports[esiaccel.AppID("loopback_in")]
164 assert isinstance(loopback_in, types.FromHostPort), \
165 f"Expected FromHostPort, got {type(loopback_in)}"
166 loopback_in.connect()
167
168 loopback_out = ports[esiaccel.AppID("loopback_out")]
169 assert isinstance(loopback_out, types.ToHostPort), \
170 f"Expected ToHostPort, got {type(loopback_out)}"
171 loopback_out.connect()
172
173 for i in range(5):
174 loopback_in.write(42 + i)
175 result = loopback_out.read().result()
176 assert result == 42 + i, \
177 f"Loopback: expected {42 + i}, got {result}"
178
179
180@cosim_test(HW_DIR / "esitester.py", args=("{tmp_dir}", "cosim_dma"))
182
183 def setup_method(self) -> None:
184 require_tool("esitester")
185 require_tool("esiquery")
186
187 def test_channel_engine_manifest(self, sources_dir: Path) -> None:
188 """CosimBSP_DMA's caller-supplied channel service must emit DMA engines."""
189 manifest = json.loads(
190 (sources_dir / "esi_system_manifest.json").read_text())
191 engine_names = {"OneItemBuffersToHost", "OneItemBuffersFromHost"}
192 engines = [
193 engine for engine in manifest["design"]["engines"]
194 if engine["serviceImplName"] in engine_names
195 ]
196 assert {engine["serviceImplName"] for engine in engines} == engine_names
197 assert all(engine["clientDetails"] for engine in engines)
198
199 def test_hostmem(self, host: str, port: int) -> None:
200 conn = f"{host}:{port}"
201 run_cmd(["esitester", "cosim", conn, "hostmem"])
202
203 def test_dma(self, host: str, port: int) -> None:
204 conn = f"{host}:{port}"
205 result = subprocess.run(
206 ["esitester", "cosim", conn, "dma", "-w", "-r"],
207 check=False,
208 capture_output=True,
209 text=True,
210 )
211 assert result.returncode == 0, result.stdout + result.stderr
212
213 def test_bandwidth_data_integrity(self, host: str, port: int) -> None:
214 """Verify complete engine payloads in both transfer directions."""
215 conn = f"{host}:{port}"
216 result = subprocess.run(
217 [
218 "esitester",
219 "cosim",
220 conn,
221 "bandwidth",
222 "-w",
223 "-r",
224 "--check-data",
225 "--widths",
226 "24",
227 "64",
228 "534",
229 "--count",
230 "24",
231 ],
232 check=False,
233 capture_output=True,
234 text=True,
235 )
236 assert result.returncode == 0, result.stdout + result.stderr
237
238 def test_hostmembw(self, host: str, port: int) -> None:
239 conn = f"{host}:{port}"
240 # Widths chosen to cover every element-vs-engine-word relationship the read
241 # gearbox must unpack from a contiguous, byte-packed layout: 24 (sub-word,
242 # does not divide the 64-bit engine word), 32 (sub-word divisor), 72
243 # (wider than the word, not a multiple -> straddles words), and 128 (a whole
244 # number of words). A 1000-element 24-bit read burst is 3000 bytes, which
245 # exceeds the PCIe maximum read request size and so exercises the
246 # read-request splitter. The -w/-r data-integrity checks verify each element
247 # landed at / was fetched from the right bytes.
248 result = subprocess.run(
249 [
250 "esitester",
251 "cosim",
252 conn,
253 "hostmembw",
254 "-w",
255 "-r",
256 "-c",
257 "1000",
258 ],
259 check=False,
260 capture_output=True,
261 text=True,
262 )
263 combined = result.stdout + result.stderr
264 assert result.returncode == 0, combined
265 assert "exceeds the PCIe maximum read request size" not in combined, \
266 combined
267
268 def test_aggbandwidth(self, host: str, port: int) -> None:
269 conn = f"{host}:{port}"
270 # Aggregate bandwidth across the width-512 readmem*/writemem* units
271 # (4 read + 4 write), exercising the multi-unit nested-AppID resolution
272 # (mmio[width]/cmd and addrCmdResp/cycles) of the burst modules.
273 result = subprocess.run(
274 [
275 "esitester", "cosim", conn, "aggbandwidth", "--width", "512", "-r",
276 "-w", "-c", "64"
277 ],
278 check=False,
279 capture_output=True,
280 text=True,
281 )
282 assert result.returncode == 0, result.stdout + result.stderr
283
284 def test_telemetry(self, host: str, port: int) -> None:
285 conn = f"{host}:{port}"
286 result = subprocess.run(
287 ["esiquery", "cosim", conn, "telemetry"],
288 check=False,
289 capture_output=True,
290 text=True,
291 )
292 assert result.returncode == 0, result.stdout + result.stderr
293 stdout = result.stdout
294 check_lines(stdout, [
295 "* Telemetry",
296 "fromhostdma[32].fromHostChecksum: 0",
297 "fromhostdma[32].fromHostCycles: 0",
298 "tohostdma[32].toHostCycles: 0",
299 ])
300
301 def test_channel(self, host: str, port: int) -> None:
302 conn = f"{host}:{port}"
303 stdout = run_cmd(["esitester", "cosim", conn, "channel", "-i", "3"])
304 check_lines(stdout, [
305 "[channel] producer i=0 got=0",
306 "[channel] producer i=1 got=1",
307 "[channel] producer i=2 got=2",
308 "[channel] loopback i=0",
309 "[channel] loopback i=1",
310 "[channel] loopback i=2",
311 "Channel test passed",
312 ])
313
314 def test_serial_coords(self, host: str, port: int) -> None:
315 conn = f"{host}:{port}"
316 stdout = run_cmd(
317 ["esitester", "cosim", conn, "serial_coords", "-n", "40", "-b", "33"])
318 check_lines(stdout, [
319 "Serial coord translate test results:",
320 "coord[0]=",
321 "Serial coord translate test passed",
322 ])
None test_bandwidth_data_integrity(self, str host, int port)
None test_channel_python(self, AcceleratorConnection conn)
None test_streaming_add_quiet(self, str host, int port)
None test_translate_coords(self, str host, int port)
None test_streaming_add(self, str host, int port)
None test_serial_coords(self, str host, int port)
None test_auto_serial_coords(self, str host, int port)