1 /******************************************************************************
2 * Copyright (c) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of Texas Instruments Incorporated nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26 * THE POSSIBILITY OF SUCH DAMAGE.
27 *****************************************************************************/
28 #include <signal.h>
29 #include <getopt.h>
30 #include <iostream>
31 #include <fstream>
32 #include <cassert>
33 #include <string>
34 #include <vector>
36 #include "executor.h"
37 #include "execution_object.h"
38 #include "configuration.h"
39 #include "utils.h"
41 using namespace tidl;
42 using std::string;
44 bool RunMultipleExecutors(const string& config_file_1,
45 const string& config_file_2,
46 uint32_t num_devices_available);
48 bool RunConfiguration(const string& config_file,
49 int num_devices,
50 DeviceType device_type);
52 bool RunAllConfigurations(int32_t num_devices,
53 DeviceType device_type);
55 bool RunNetwork(DeviceType device_type,
56 const DeviceIds& ids,
57 const Configuration& c,
58 std::istream& input,
59 std::ostream& output);
61 static void ProcessArgs(int argc, char *argv[],
62 string& config_file,
63 int& num_devices,
64 DeviceType& device_type);
66 static void DisplayHelp();
68 bool verbose = false;
70 int main(int argc, char *argv[])
71 {
72 // Catch ctrl-c to ensure a clean exit
73 signal(SIGABRT, exit);
74 signal(SIGTERM, exit);
76 // If there are no devices capable of offloading TIDL on the SoC, exit
77 uint32_t num_eve = Executor::GetNumDevices(DeviceType::EVE);
78 uint32_t num_dsp = Executor::GetNumDevices(DeviceType::DSP);
79 if (num_eve == 0 && num_dsp == 0)
80 {
81 std::cout << "TI DL not supported on this processor." << std::endl;
82 return EXIT_SUCCESS;
83 }
85 // Process arguments
86 string config_file;
87 int num_devices = 1;
88 DeviceType device_type = DeviceType::EVE;
89 ProcessArgs(argc, argv, config_file, num_devices, device_type);
91 bool status = true;
92 if (!config_file.empty())
93 status = RunConfiguration(config_file, num_devices, device_type);
94 else
95 {
96 if (num_eve > 0)
97 {
98 // Run on 2 devices because there is not enough CMEM available by
99 // default
100 if (num_eve == 4)
101 {
102 std::cout
103 << "Running on 2 EVE devices instead of the available 4 "
104 << "due to insufficient OpenCL global memory. Refer the "
105 << "TIDL API User's Guide, Frequently Asked Questions, "
106 << "Section \"Insufficient OpenCL global memory\" for details "
107 << "on increasing the amount of CMEM available for OpenCL."
108 << std::endl;
109 num_eve = 2;
110 }
111 status = RunAllConfigurations(num_eve, DeviceType::EVE);
112 status &= RunMultipleExecutors(
113 "testvecs/config/infer/tidl_config_j11_v2.txt",
114 "testvecs/config/infer/tidl_config_j11_cifar.txt",
115 num_eve);
116 }
118 if (num_dsp > 0)
119 {
120 status &= RunAllConfigurations(num_dsp, DeviceType::DSP);
121 }
122 }
124 if (!status)
125 {
126 std::cout << "tidl FAILED" << std::endl;
127 return EXIT_FAILURE;
128 }
130 std::cout << "tidl PASSED" << std::endl;
131 return EXIT_SUCCESS;
132 }
134 bool RunConfiguration(const std::string& config_file, int num_devices,
135 DeviceType device_type)
136 {
137 DeviceIds ids;
138 for (int i = 0; i < num_devices; i++)
139 ids.insert(static_cast<DeviceId>(i));
141 // Read the TI DL configuration file
142 Configuration c;
143 if (!c.ReadFromFile(config_file)) return false;
144 if (verbose) c.enableApiTrace = true;
146 // Open input and output files
147 std::ifstream input_data_file(c.inData, std::ios::binary);
148 std::ofstream output_data_file(c.outData, std::ios::binary);
149 assert (input_data_file.good());
150 assert (output_data_file.good());
152 bool status = RunNetwork(device_type, ids, c,
153 input_data_file, output_data_file);
155 input_data_file.close();
156 output_data_file.close();
158 return status;
159 }
161 bool RunNetwork(DeviceType device_type,
162 const DeviceIds& ids,
163 const Configuration& c,
164 std::istream& input,
165 std::ostream& output)
166 {
167 bool status = true;
169 try
170 {
171 // Create a executor with the specified core type, number of cores
172 // and configuration
173 Executor E(device_type, ids, c);
175 std::vector<ExecutionObject *> EOs;
176 for (unsigned int i = 0; i < E.GetNumExecutionObjects(); i++)
177 EOs.push_back(E[i]);
179 int num_eos = EOs.size();
181 // Allocate input and output buffers for each execution object
182 for (auto eo : EOs)
183 {
184 size_t in_size = eo->GetInputBufferSizeInBytes();
185 size_t out_size = eo->GetOutputBufferSizeInBytes();
186 ArgInfo in = { ArgInfo(malloc(in_size), in_size)};
187 ArgInfo out = { ArgInfo(malloc(out_size), out_size)};
188 eo->SetInputOutputBuffer(in, out);
189 }
191 // Process frames with available execution objects in a pipelined manner
192 // additional num_eos iterations to flush the pipeline (epilogue)
193 for (int frame_idx = 0; frame_idx < c.numFrames + num_eos; frame_idx++)
194 {
195 ExecutionObject* eo = EOs[frame_idx % num_eos];
197 // Wait for previous frame on the same eo to finish processing
198 if (eo->ProcessFrameWait())
199 {
200 ReportTime(eo);
201 WriteFrame(eo, output);
202 }
204 // Read a frame and start processing it with current eo
205 if (ReadFrame(eo, frame_idx, c, input))
206 eo->ProcessFrameStartAsync();
207 }
209 for (auto eo : EOs)
210 {
211 free(eo->GetInputBufferPtr());
212 free(eo->GetOutputBufferPtr());
213 }
214 }
215 catch (tidl::Exception &e)
216 {
217 std::cerr << e.what() << std::endl;
218 status = false;
219 }
221 return status;
222 }
225 namespace tidl {
226 extern bool CompareFiles (const std::string &F1, const std::string &F2);
227 extern bool CompareFrames(const std::string &F1, const std::string &F2,
228 int numFrames, int width, int height);
229 }
231 bool RunAllConfigurations(int32_t num_devices, DeviceType device_type)
232 {
233 std::vector<std::string> configurations;
235 if (device_type == DeviceType::EVE)
236 configurations = {"dense_1x1", "j11_bn", "j11_cifar",
237 "j11_controlLayers", "j11_prelu", "j11_v2",
238 "jseg21", "jseg21_tiscapes", "smallRoi", "squeeze1_1"};
239 else
240 configurations = {"dense_1x1", "j11_bn", "j11_cifar",
241 "j11_controlLayers", "j11_v2",
242 "jseg21", "jseg21_tiscapes", "smallRoi", "squeeze1_1"};
244 int errors = 0;
245 for (auto config : configurations)
246 {
247 std::string config_file = "testvecs/config/infer/tidl_config_"
248 + config + ".txt";
249 std::cout << "Running " << config << " on " << num_devices
250 << " devices, type "
251 << ((device_type == DeviceType::EVE) ? "EVE" : "DSP")
252 << std::endl;
254 Configuration configuration;
255 bool status = configuration.ReadFromFile(config_file);
256 if (!status) { errors++; continue; }
258 status = RunConfiguration(config_file, num_devices, device_type);
260 if (!status) { errors++; continue; }
262 // Skip smallRoi reference checking, will investigate
263 if (config.compare("smallRoi") == 0) continue;
265 // Check output against reference output
266 std::string reference_output = "testvecs/reference/"
267 + config + "_ref.bin";
269 // Reference for jseg21_tiscapes only has one frame
270 if (config.compare("jseg21_tiscapes") == 0)
271 status = CompareFrames(configuration.outData, reference_output,
272 1, 1024, 512);
273 else
274 status = CompareFiles(configuration.outData, reference_output);
276 if (status) std::cout << config << " : PASSED" << std::endl;
277 else std::cout << config << " : FAILED" << std::endl;
279 if (!status) errors++;
280 }
282 if (errors > 0) return false;
284 return true;
285 }
287 void ProcessArgs(int argc, char *argv[], std::string& config_file,
288 int& num_devices, DeviceType& device_type)
289 {
290 const struct option long_options[] =
291 {
292 {"config_file", required_argument, 0, 'c'},
293 {"num_devices", required_argument, 0, 'n'},
294 {"device_type", required_argument, 0, 't'},
295 {"help", no_argument, 0, 'h'},
296 {"verbose", no_argument, 0, 'v'},
297 {0, 0, 0, 0}
298 };
300 int option_index = 0;
302 while (true)
303 {
304 int c = getopt_long(argc, argv, "c:n:t:hv", long_options, &option_index);
306 if (c == -1)
307 break;
309 switch (c)
310 {
311 case 'c': config_file = optarg;
312 break;
314 case 'n': num_devices = atoi(optarg);
315 assert (num_devices > 0 && num_devices <= 4);
316 break;
318 case 't': if (*optarg == 'e')
319 device_type = DeviceType::EVE;
320 else if (*optarg == 'd')
321 device_type = DeviceType::DSP;
322 else
323 {
324 std::cerr << "Invalid argument to -t, only e or d"
325 " allowed" << std::endl;
326 exit(EXIT_FAILURE);
327 }
328 break;
330 case 'v': verbose = true;
331 break;
333 case 'h': DisplayHelp();
334 exit(EXIT_SUCCESS);
335 break;
337 case '?': // Error in getopt_long
338 exit(EXIT_FAILURE);
339 break;
341 default:
342 std::cerr << "Unsupported option: " << c << std::endl;
343 break;
344 }
345 }
346 }
348 void DisplayHelp()
349 {
350 std::cout << "Usage: test_tidl\n"
351 " Will run all available networks if invoked without"
352 " any arguments.\n Use -c to run a single network.\n"
353 "Optional arguments:\n"
354 " -c Path to the configuration file\n"
355 " -n <number of cores> Number of cores to use (1 - 4)\n"
356 " -t <d|e> Type of core. d -> DSP, e -> EVE\n"
357 " -v Verbose output during execution\n"
358 " -h Help\n";
359 }