CIRCT 24.0.0git
Loading...
Searching...
No Matches
test_hostmem_write.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
5import ctypes
6import json
7
8import esiaccel
9import pytest
10from esiaccel import esiCppAccel as cpp
11from esiaccel.accelerator import AcceleratorConnection
12from esiaccel.cosim.pytest import cosim_test
13
14from .conftest import HW_DIR
15
16
17def _int(value):
18 return int.from_bytes(value, "little") if isinstance(
19 value, (bytes, bytearray)) else int(value)
20
21
22def _run(conn: AcceleratorConnection, bus_width: int):
23 # Exercise discovery without a caller-populated manifest type table.
24 hostmem = conn.get_service_hostmem()
25 hostmem.start()
26 acc = conn.build_accelerator()
27 allocations = []
28 writers = []
29 for width in [13, bus_width, bus_width + 24, bus_width * 2 + 24, 16384]:
30 dut = acc.children[esiaccel.AppID("wr", width)]
31 req, ack, gate = [
32 dut.ports[esiaccel.AppID(name)] for name in ("req", "ack", "gate")
33 ]
34 for port in (req, ack, gate):
35 port.connect()
36 size = (width + 7) // 8
37 expected = []
38 for i in range(3):
39 mem = hostmem.allocate(size + 16)
40 allocations.append(mem)
41 ctypes.memset(mem.ptr, 0xA5, size + 16)
42 data = bytes((j * 37 + 11 + i) & 0xFF for j in range(size))
43 value = int.from_bytes(data, "little") & ((1 << width) - 1)
44 data = value.to_bytes(size, "little")
45 req.write({"address": mem.ptr + 3, "tag": 0, "data": data})
46 expected.append((mem, data))
47 writers.append((ack, gate, expected))
48
49 # Release completions after issuing all clients' writes. Open every gate
50 # before waiting on any ack: the clients share one in-order response stream,
51 # so a response for a still-gated client would block those behind it.
52 for _, gate, _ in writers:
53 gate.write(1)
54 for ack, _, expected in writers:
55 for mem, data in expected:
56 ack.read().result(timeout=30)
57 assert ctypes.string_at(mem.ptr,
58 len(data) + 16) == (b"\xA5" * 3 + data +
59 b"\xA5" * 13)
60
61 req = acc.ports[esiaccel.AppID("list_req")]
62 ack = acc.ports[esiaccel.AppID("list_ack")]
63 req.connect()
64 ack.connect()
65 for count in [1, 7, 100]:
66 mem = hostmem.allocate(count * 3 + 16)
67 allocations.append(mem)
68 ctypes.memset(mem.ptr, 0xA5, count * 3 + 16)
69 data = bytes((i * 29 + 7) & 0xFF for i in range(count * 3))
70 for i in range(count):
71 req.write({
72 "address": mem.ptr + 3,
73 "data": data[i * 3:i * 3 + 3],
74 "last": int(i == count - 1),
75 })
76 for _ in range(count):
77 ack.read().result(timeout=30)
78 assert ctypes.string_at(mem.ptr, count * 3 + 16) == (b"\xA5" * 3 + data +
79 b"\xA5" * 13)
80
81 req = acc.ports[esiaccel.AppID("probe_req")]
82 resp = acc.ports[esiaccel.AppID("probe_resp")]
83 req.connect()
84 resp.connect()
85 data = bytes((i * 13 + 3) & 0xFF for i in range(2048))
86 req.write({"address": 19, "tag": 17, "data": data})
87 beat_bytes = bus_width // 8
88 cap = max(256, beat_bytes)
89 for offset in range(0, len(data), beat_bytes):
90 beat = resp.read().result(timeout=30)
91 assert _int(beat["address"]) == 19 + offset
92 assert _int(beat["tag"]) == 17
93 assert _int(beat["valid_bytes"]) == beat_bytes
94 assert _int(beat["data"]) == int.from_bytes(
95 data[offset:offset + beat_bytes], "little")
96 assert _int(beat["last"]) == int((offset + beat_bytes) % cap == 0)
97
98
99@cosim_test(HW_DIR / "hostmem_write.py", args=("{tmp_dir}",))
101
102 def test_writes(self, conn: AcceleratorConnection):
103 _run(conn, 64)
104
105 @pytest.mark.parametrize("invalid_layout", [
106 "width",
107 "field_name",
108 "missing_data",
109 "missing_field",
110 "extra_field",
111 "field_order",
112 "data_type",
113 ])
114 @pytest.mark.parametrize("direction", ["read", "write"])
115 def test_rejects_invalid_layout(self, host, port, invalid_layout, direction):
116 # Do not reuse cached valid types from earlier tests in a Windows worker.
117 conn = esiaccel.Context().connect("cosim", f"{host}:{port}")
118 manifest = json.loads(conn.sysinfo().json_manifest())
119 names = (["tag", "data", "last"] if direction == "read" else
120 ["address", "tag", "data", "data_size", "last"])
121 layout = next(
122 channel["type"]["inner"]
123 for t in manifest["types"]
124 for channel in t.get("channels", [])
125 if
126 [f["name"]
127 for f in channel["type"].get("inner", {}).get("fields", [])] == names)
128 fields = layout["fields"]
129 data = next(field for field in fields if field["name"] == "data")
130 message = f"Invalid HostMem {direction} field"
131 if invalid_layout == "width":
132 data["type"].update(id="i24", hwBitwidth=24)
133 message = f"{direction} width must be a power of two"
134 elif invalid_layout == "field_name":
135 fields[0]["name"] = "invalid_address"
136 elif invalid_layout == "missing_data":
137 fields.remove(data)
138 message += ": data"
139 elif invalid_layout == "missing_field":
140 fields.pop()
141 message = f"Invalid HostMem {direction} channel layout"
142 elif invalid_layout == "extra_field":
143 fields.append({"name": "extra", "type": fields[0]["type"]})
144 message = f"Invalid HostMem {direction} channel layout"
145 elif invalid_layout == "field_order":
146 fields.reverse()
147 elif invalid_layout == "data_type":
148 data["type"] = {
149 "id": "invalid_data_struct",
150 "mnemonic": "struct",
151 "fields": [{
152 "name": "bits",
153 "type": data["type"]
154 }],
155 }
156 cpp.Manifest(conn.ctxt.cpp_ctxt, json.dumps(manifest))
157 with pytest.raises(RuntimeError, match=message):
158 conn.get_service_hostmem().start()
159
160
161@cosim_test(HW_DIR / "hostmem_write.py", args=("{tmp_dir}", "8"))
163
164 def test_writes(self, conn: AcceleratorConnection):
165 _run(conn, 8)
166
167
168@cosim_test(HW_DIR / "hostmem_write.py", args=("{tmp_dir}", "512"))
170
171 def test_writes(self, conn: AcceleratorConnection):
172 _run(conn, 512)
173
174
175@cosim_test(HW_DIR / "hostmem_write.py", args=("{tmp_dir}", "2048"))
177
178 def test_writes(self, conn: AcceleratorConnection):
179 _run(conn, 2048)
180
181
182@cosim_test(HW_DIR / "hostmem_write.py", args=("{tmp_dir}", "4096"))
184
185 def test_writes(self, conn: AcceleratorConnection):
186 _run(conn, 4096)
test_rejects_invalid_layout(self, host, port, invalid_layout, direction)
_run(AcceleratorConnection conn, int bus_width)