CIRCT 23.0.0git
Loading...
Searching...
No Matches
test_loopback_cpp.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
7from pathlib import Path
8import shutil
9import subprocess
10import sys
11
12import pytest
13
14from esiaccel.cosim.pytest import cosim_test
15
16from .conftest import (HW_DIR, SW_DIR, check_lines, get_runtime_root,
17 require_tool)
18
19
20@cosim_test(HW_DIR / "loopback.py")
21@pytest.mark.parametrize("mode", ["from_manifest", "from_accel"])
22def test_loopback_cpp_codegen(mode: str, tmp_path: Path, host: str, port: int,
23 sources_dir: Path) -> None:
24 require_tool("cmake")
25
26 runtime_root = get_runtime_root()
27
28 include_dir = tmp_path / "include"
29 generated_dir = include_dir / "loopback"
30 generated_dir.mkdir(parents=True, exist_ok=True)
31 if mode == "from_manifest":
32 # Codegen was already run automatically; copy the generated code.
33 codegen_src = sources_dir / "generated"
34 if codegen_src.exists():
35 for item in codegen_src.iterdir():
36 if item.is_file():
37 shutil.copy(item, generated_dir)
38 else:
39 # Generate from live cosim connection instead.
40 subprocess.run(
41 [
42 sys.executable,
43 "-m",
44 "esiaccel.codegen",
45 "--platform",
46 "cosim",
47 "--connection",
48 f"{host}:{port}",
49 "--output-dir",
50 str(generated_dir),
51 ],
52 check=True,
53 )
54
55 # Verify generated header content (LOOPBACK-H checks).
56 header_path = generated_dir / "LoopbackIP.h"
57 assert header_path.exists(), "Generated header LoopbackIP.h not found"
58 header_content = header_path.read_text()
59 check_lines(header_content, [
60 "/// Generated header for esi_system module LoopbackIP.",
61 "#pragma once",
62 '#include "types.h"',
63 "namespace esi_system {",
64 "class LoopbackIP {",
65 "static constexpr uint32_t depth = 0x5;",
66 "} // namespace esi_system",
67 ])
68
69 build_dir = tmp_path / f"loopback-build-{mode}"
70 result = subprocess.run(
71 [
72 "cmake",
73 "-S",
74 str(SW_DIR),
75 "-B",
76 str(build_dir),
77 f"-DLOOPBACK_GENERATED_DIR={include_dir}",
78 f"-DESI_RUNTIME_ROOT={runtime_root}",
79 ],
80 capture_output=True,
81 text=True,
82 )
83 assert result.returncode == 0, (
84 f"cmake configure failed (rc={result.returncode}):\n"
85 f"--- stdout ---\n{result.stdout}\n"
86 f"--- stderr ---\n{result.stderr}")
87
88 result = subprocess.run(
89 ["cmake", "--build",
90 str(build_dir), "--target", "loopback_test"],
91 capture_output=True,
92 text=True,
93 )
94 assert result.returncode == 0, (
95 f"cmake build failed (rc={result.returncode}):\n"
96 f"--- stdout ---\n{result.stdout}\n"
97 f"--- stderr ---\n{result.stderr}")
98
99 LOOPBACK_EXPECTED = [
100 "depth: 0x5",
101 "loopback i8 ok: 0x5a",
102 "struct func ok: b=-7 x=-6 y=-7",
103 "odd struct func ok: a=2749 b=-20 p=10 q=-5 r0=4 r1=6",
104 "array func ok: -3 -2",
105 ]
106
107 LOOPBACK_TYPED_EXPECTED = LOOPBACK_EXPECTED[:1] + [
108 "loopback i8 ok: 0x5a",
109 "sint4 loopback ok: pos=5 neg=-3",
110 "struct func ok: b=-7 x=-6 y=-7",
111 "odd struct func ok: a=2749 b=-20 p=10 q=-5 r0=4 r1=6",
112 "array func ok: -3 -2",
113 ]
114
115 # Run the C++ test binary and verify output (CPP-TEST checks).
116 result = subprocess.run(
117 [str(build_dir / "loopback_test"), "cosim", f"{host}:{port}"],
118 check=True,
119 capture_output=True,
120 text=True,
121 )
122 check_lines(result.stdout, LOOPBACK_EXPECTED)
123
124 # Also build and run the typed-port variant (loopback_typed_test) which
125 # exercises TypedWritePort, TypedReadPort, and TypedFunction wrappers.
126 # Only done for a single codegen mode to avoid redundant work.
127 if mode == "from_manifest":
128 result = subprocess.run(
129 ["cmake", "--build",
130 str(build_dir), "--target", "loopback_typed_test"],
131 capture_output=True,
132 text=True,
133 )
134 assert result.returncode == 0, (
135 f"cmake build (typed) failed (rc={result.returncode}):\n"
136 f"--- stdout ---\n{result.stdout}\n"
137 f"--- stderr ---\n{result.stderr}")
138
139 result = subprocess.run(
140 [str(build_dir / "loopback_typed_test"), "cosim", f"{host}:{port}"],
141 check=True,
142 capture_output=True,
143 text=True,
144 )
145 check_lines(result.stdout, LOOPBACK_TYPED_EXPECTED)
146
147
148@cosim_test(HW_DIR / "loopback.py")
150 """Tests for esiquery against the loopback design."""
151
152 def test_loopback_query_info(self, sources_dir: Path) -> None:
153 """Verify esiquery info output against the generated manifest
154 (QUERY-INFO checks)."""
155 require_tool("esiquery")
156 manifest = sources_dir / "esi_system_manifest.json"
157 assert manifest.exists(), "Manifest not found"
158 result = subprocess.run(
159 ["esiquery", "trace", f"w:{manifest}", "info"],
160 check=True,
161 capture_output=True,
162 text=True,
163 )
164 check_lines(result.stdout, [
165 "API version: 0",
166 "* Module information",
167 "- LoopbackIP v0.0",
168 "IP which simply echos bytes",
169 "Constants:",
170 "depth: 5",
171 "Extra metadata:",
172 "foo: 1",
173 ])
174
175 def test_loopback_query_hier(self, sources_dir: Path) -> None:
176 """Verify esiquery hier output against the generated manifest
177 (QUERY-HIER checks)."""
178 require_tool("esiquery")
179 manifest = sources_dir / "esi_system_manifest.json"
180 assert manifest.exists(), "Manifest not found"
181 result = subprocess.run(
182 ["esiquery", "trace", f"w:{manifest}", "hier"],
183 check=True,
184 capture_output=True,
185 text=True,
186 )
187 check_lines(result.stdout, [
188 "* Design hierarchy",
189 "func1: function uint16(uint16)",
190 "structFunc: function ResultStruct(ArgStruct)",
191 "arrayFunc: function ResultArray(sint8[1])",
192 "* Instance: loopback_inst[0]",
193 "loopback_tohw:",
194 "recv: bits8",
195 "loopback_fromhw:",
196 "send: bits8",
197 "mysvc_recv:",
198 "recv: void",
199 "mysvc_send:",
200 "send: void",
201 "* Instance: loopback_inst[1]",
202 ])
None test_loopback_cpp_codegen(str mode, Path tmp_path, str host, int port, Path sources_dir)