MLIR-AIE 1.4.5.dev
test_utils.cpp
Go to the documentation of this file.
1//===- test_utils.cpp ----------------------------000---*- C++ -*-===//
2//
3// Copyright (C) 2024 Advanced Micro Devices, Inc.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5//
6//===----------------------------------------------------------------------===//
7
8// This file contains common helper functions for the generic host code
9
10#include "test_utils.h"
11#include <cassert>
12#include <filesystem>
13
14#ifdef TEST_UTILS_USE_XRT
15#include "xrt/xrt_device.h"
16#include "xrt/xrt_kernel.h"
17#endif
18
19// --------------------------------------------------------------------------
20// Command Line Argument Handling
21// --------------------------------------------------------------------------
22
23void test_utils::check_arg_file_exists(const cxxopts::ParseResult &result,
24 std::string name) {
25 if (!result.count(name)) {
26 throw std::runtime_error("Missing required argument: " + name);
27 }
28 std::string path = result[name].as<std::string>();
29 if (!std::filesystem::exists(path)) {
30 throw std::runtime_error("File does not exist: " + path);
31 }
32}
33
34void test_utils::add_default_options(cxxopts::Options &options) {
35 options.add_options()("help,h", "produce help message")(
36 "xclbin,x", "the input xclbin path", cxxopts::value<std::string>())(
37 "kernel,k", "the kernel name in the XCLBIN (for instance PP_PRE_FD)",
38 cxxopts::value<std::string>())("verbosity,v",
39 "the verbosity of the output",
40 cxxopts::value<int>()->default_value("0"))(
41 "instr,i",
42 "path of file containing userspace instructions sent to the NPU",
43 cxxopts::value<std::string>())(
44 "verify", "whether to verify the AIE computed output",
45 cxxopts::value<bool>()->default_value("true"))(
46 "iters", "number of iterations",
47 cxxopts::value<int>()->default_value("1"))(
48 "warmup", "number of warmup iterations",
49 cxxopts::value<int>()->default_value("0"))(
50 "trace_sz,t", "trace size", cxxopts::value<int>()->default_value("0"))(
51 "trace_file", "where to store trace output",
52 cxxopts::value<std::string>()->default_value("trace.txt"));
53}
54
55void test_utils::parse_options(int argc, const char *argv[],
56 cxxopts::Options &options,
57 cxxopts::ParseResult &vm) {
58 try {
59 vm = options.parse(argc, argv);
60
61 if (vm.count("help")) {
62 std::cout << options.help() << "\n";
63 std::exit(1);
64 }
65 } catch (const cxxopts::exceptions::parsing &e) {
66 std::cerr << e.what() << "\n\n";
67 std::cerr << "Usage:\n" << options.help() << "\n";
68 std::exit(1);
69 }
70
71 try {
72 check_arg_file_exists(vm, "xclbin");
73 check_arg_file_exists(vm, "instr");
74 } catch (const std::exception &ex) {
75 std::cerr << ex.what() << "\n\n";
76 }
77}
78
79// --------------------------------------------------------------------------
80// AIE Specifics
81// --------------------------------------------------------------------------
82
83std::vector<uint32_t> test_utils::load_instr_sequence(std::string instr_path) {
84 std::ifstream instr_file(instr_path);
85 std::string line;
86 std::vector<uint32_t> instr_v;
87 while (std::getline(instr_file, line)) {
88 std::istringstream iss(line);
89 uint32_t a;
90 if (!(iss >> std::hex >> a)) {
91 throw std::runtime_error("Unable to parse instruction file\n");
92 }
93 instr_v.push_back(a);
94 }
95 return instr_v;
96}
97
98std::vector<uint32_t> test_utils::load_instr_binary(std::string instr_path) {
99 // Open file in binary mode
100 std::ifstream instr_file(instr_path, std::ios::binary);
101 if (!instr_file.is_open()) {
102 throw std::runtime_error("Unable to open instruction file\n");
103 }
104
105 // Get the size of the file
106 instr_file.seekg(0, std::ios::end);
107 std::streamsize size = instr_file.tellg();
108 instr_file.seekg(0, std::ios::beg);
109
110 // Check that the file size is a multiple of 4 bytes (size of uint32_t)
111 if (size % 4 != 0) {
112 throw std::runtime_error("File size is not a multiple of 4 bytes\n");
113 }
114
115 // Allocate vector and read the binary data
116 std::vector<uint32_t> instr_v(size / 4);
117 if (!instr_file.read(reinterpret_cast<char *>(instr_v.data()), size)) {
118 throw std::runtime_error("Failed to read instruction file\n");
119 }
120 return instr_v;
121}
122
123#ifdef TEST_UTILS_USE_XRT
124
125// --------------------------------------------------------------------------
126// XRT
127// --------------------------------------------------------------------------
128std::string test_utils::get_kernel_name(const xrt::xclbin &xclbin,
129 const std::string &name_prefix,
130 int verbosity) {
131 std::string available;
132 for (auto &k : xclbin.get_kernels()) {
133 auto name = k.get_name();
134 if (verbosity >= 1)
135 std::cout << "Name: " << name << std::endl;
136 if (name.rfind(name_prefix, 0) == 0)
137 return name;
138 if (!available.empty())
139 available += ", ";
140 available += name;
141 }
142 throw std::runtime_error(
143 "No kernel in the xclbin starts with '" + name_prefix +
144 "'. Available kernels: " + (available.empty() ? "(none)" : available));
145}
146
147void test_utils::init_xrt_load_kernel(xrt::device &device, xrt::kernel &kernel,
148 int verbosity, std::string xclbinFileName,
149 std::string kernelNameInXclbin) {
150 // Get a device handle
151 unsigned int device_index = 0;
152 device = xrt::device(device_index);
153
154 // Load the xclbin
155 if (verbosity >= 1)
156 std::cout << "Loading xclbin: " << xclbinFileName << "\n";
157 auto xclbin = xrt::xclbin(xclbinFileName);
158
159 if (verbosity >= 1)
160 std::cout << "Kernel opcode: " << kernelNameInXclbin << "\n";
161
162 // Get the kernel from the xclbin
163 auto kernelName = get_kernel_name(xclbin, kernelNameInXclbin, verbosity);
164
165 // Register xclbin
166 if (verbosity >= 1)
167 std::cout << "Registering xclbin: " << xclbinFileName << "\n";
168
169 device.register_xclbin(xclbin);
170
171 // Get a hardware context
172 if (verbosity >= 1)
173 std::cout << "Getting hardware context.\n";
174 xrt::hw_context context(device, xclbin.get_uuid());
175
176 // Get a kernel handle
177 if (verbosity >= 1)
178 std::cout << "Getting handle to kernel:" << kernelName << "\n";
179 kernel = xrt::kernel(context, kernelName);
180
181 return;
182}
183
184#endif // TEST_UTILS_USE_XRT
185
186// --------------------------------------------------------------------------
187// Matrix / Float / Math
188// --------------------------------------------------------------------------
189
190// nearly_equal function adapted from Stack Overflow, License CC BY-SA 4.0
191// Original author: P-Gn
192// Source: https://stackoverflow.com/a/32334103
193bool test_utils::nearly_equal(float a, float b, float epsilon, float abs_th)
194// those defaults are arbitrary and could be removed
195{
196 assert(std::numeric_limits<float>::epsilon() <= epsilon);
197 assert(epsilon < 1.f);
198
199 if (a == b)
200 return true;
201
202 auto diff = std::abs(a - b);
203 auto norm =
204 std::min((std::abs(a) + std::abs(b)), std::numeric_limits<float>::max());
205 // or even faster: std::min(std::abs(a + b),
206 // std::numeric_limits<float>::max()); keeping this commented out until I
207 // update figures below
208 return diff < std::max(abs_th, epsilon * norm);
209}
210
211// --------------------------------------------------------------------------
212// Tracing
213// --------------------------------------------------------------------------
214void test_utils::write_out_trace(char *traceOutPtr, size_t trace_size,
215 std::string path) {
216 std::ofstream fout(path);
217 uint32_t *traceOut = (uint32_t *)traceOutPtr;
218 for (int i = 0; i < trace_size / sizeof(traceOut[0]); i++) {
219 fout << std::setfill('0') << std::setw(8) << std::hex << (int)traceOut[i];
220 fout << std::endl;
221 }
222}
bool nearly_equal(float a, float b, float epsilon=128 *FLT_EPSILON, float abs_th=FLT_MIN)
std::vector< uint32_t > load_instr_binary(std::string instr_path)
void init_xrt_load_kernel(xrt::device &device, xrt::kernel &kernel, int verbosity, std::string xclbinFileName, std::string kernelNameInXclbin)
void parse_options(int argc, const char *argv[], cxxopts::Options &options, cxxopts::ParseResult &result)
std::string get_kernel_name(const xrt::xclbin &xclbin, const std::string &name_prefix, int verbosity=0)
void write_out_trace(char *traceOutPtr, size_t trace_size, std::string path)
void check_arg_file_exists(const cxxopts::ParseResult &result, std::string name)
std::vector< uint32_t > load_instr_sequence(std::string instr_path)
void add_default_options(cxxopts::Options &options)