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