1 /******************************************************************************
2 * Copyright (c) 2017, 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 <iomanip>
32 #include <fstream>
33 #include <cassert>
34 #include <string>
35 #include <functional>
36 #include <algorithm>
37 #include <time.h>
39 #include "executor.h"
40 #include "execution_object.h"
41 #include "configuration.h"
43 bool __TI_show_debug_ = false;
45 using namespace tinn;
47 bool RunMultipleExecutors(const std::string& config_file_1,
48 const std::string& config_file_2,
49 uint32_t num_devices_available);
51 bool RunConfiguration(const std::string& config_file, int num_devices,
52 DeviceType device_type);
53 bool RunAllConfigurations(int32_t num_devices, DeviceType device_type);
55 bool ReadFrame(ExecutionObject& eo,
56 int frame_idx,
57 const Configuration& configuration,
58 std::istream& input_file);
60 bool WriteFrame(const ExecutionObject &eo,
61 std::ostream& output_file);
63 static void ProcessArgs(int argc, char *argv[],
64 std::string& config_file,
65 int& num_devices,
66 DeviceType& device_type);
68 static void DisplayHelp();
70 static double ms_diff(struct timespec &t0, struct timespec &t1)
71 { return (t1.tv_sec - t0.tv_sec) * 1e3 + (t1.tv_nsec - t0.tv_nsec) / 1e6; }
74 int main(int argc, char *argv[])
75 {
76 // Catch ctrl-c to ensure a clean exit
77 signal(SIGABRT, exit);
78 signal(SIGTERM, exit);
80 // If there are no devices capable of offloading TINN on the SoC, exit
81 uint32_t num_dla = Executor::GetNumDevices(DeviceType::DLA);
82 uint32_t num_dsp = Executor::GetNumDevices(DeviceType::DSP);
83 if (num_dla == 0 && num_dsp == 0)
84 {
85 std::cout << "TI DL not supported on this SoC." << std::endl;
86 return EXIT_SUCCESS;
87 }
88 std::cout << "API Version: " << Executor::GetAPIVersion() << std::endl;
90 // Process arguments
91 std::string config_file;
92 int num_devices = 1;
93 DeviceType device_type = DeviceType::DLA;
94 ProcessArgs(argc, argv, config_file, num_devices, device_type);
96 bool status = true;
97 if (!config_file.empty())
98 status = RunConfiguration(config_file, num_devices, device_type);
99 else
100 {
101 if (num_dla > 0)
102 {
103 //TODO: Use memory availability to determine # devices
104 // Run on 2 devices because there is not enough CMEM available by
105 // default
106 if (num_dla = 4) num_dla = 2;
107 status = RunAllConfigurations(num_dla, DeviceType::DLA);
108 status &= RunMultipleExecutors(
109 "testvecs/config/infer/tidl_config_j11_v2.txt",
110 "testvecs/config/infer/tidl_config_j11_cifar.txt",
111 num_dla);
112 }
114 if (num_dsp > 0)
115 {
116 status &= RunAllConfigurations(num_dsp, DeviceType::DSP);
117 }
118 }
120 if (!status)
121 {
122 std::cout << "tidl FAILED" << std::endl;
123 return EXIT_FAILURE;
124 }
126 std::cout << "tidl PASSED" << std::endl;
127 return EXIT_SUCCESS;
128 }
130 bool RunConfiguration(const std::string& config_file, int num_devices,
131 DeviceType device_type)
132 {
133 DeviceIds ids;
134 for (int i = 0; i < num_devices; i++)
135 ids.insert(static_cast<DeviceId>(i));
137 // Read the TI DL configuration file
138 Configuration configuration;
139 bool status = configuration.ReadFromFile(config_file);
140 if (!status)
141 {
142 std::cerr << "Error in configuration file: " << config_file
143 << std::endl;
144 return false;
145 }
147 // Open input and output files
148 std::ifstream input_data_file(configuration.inData, std::ios::binary);
149 std::ofstream output_data_file(configuration.outData, std::ios::binary);
150 assert (input_data_file.good());
151 assert (output_data_file.good());
153 // Determine input frame size from configuration
154 size_t frame_sz = configuration.inWidth * configuration.inHeight *
155 configuration.inNumChannels;
157 try
158 {
159 // Create a executor with the approriate core type, number of cores
160 // and configuration specified
161 Executor executor(device_type, ids, configuration);
163 // Query Executor for set of ExecutionObjects created
164 const ExecutionObjects& execution_objects =
165 executor.GetExecutionObjects();
166 int num_eos = execution_objects.size();
168 // Allocate input and output buffers for each execution object
169 std::vector<void *> buffers;
170 for (auto &eo : execution_objects)
171 {
172 ArgInfo in = { ArgInfo(malloc(frame_sz), frame_sz)};
173 ArgInfo out = { ArgInfo(malloc(frame_sz), frame_sz)};
174 eo->SetInputOutputBuffer(in, out);
176 buffers.push_back(in.ptr());
177 buffers.push_back(out.ptr());
178 }
180 #define MAX_NUM_EOS 4
181 struct timespec t0[MAX_NUM_EOS], t1;
183 // Process frames with available execution objects in a pipelined manner
184 // additional num_eos iterations to flush the pipeline (epilogue)
185 for (int frame_idx = 0;
186 frame_idx < configuration.numFrames + num_eos; frame_idx++)
187 {
188 ExecutionObject* eo = execution_objects[frame_idx % num_eos].get();
190 // Wait for previous frame on the same eo to finish processing
191 if (eo->ProcessFrameWait())
192 {
193 clock_gettime(CLOCK_MONOTONIC, &t1);
194 double elapsed_host =
195 ms_diff(t0[eo->GetFrameIndex() % num_eos], t1);
196 double elapsed_device = eo->GetProcessTimeInMilliSeconds();
197 double overhead = 100 - (elapsed_device/elapsed_host*100);
199 std::cout << "frame[" << eo->GetFrameIndex() << "]: "
200 << "Time on device: "
201 << std::setw(6) << std::setprecision(4)
202 << elapsed_device << "ms, "
203 << "host: "
204 << std::setw(6) << std::setprecision(4)
205 << elapsed_host << "ms ";
206 std::cout << "API overhead: "
207 << std::setw(6) << std::setprecision(3)
208 << overhead << " %" << std::endl;
210 WriteFrame(*eo, output_data_file);
211 }
213 // Read a frame and start processing it with current eo
214 if (ReadFrame(*eo, frame_idx, configuration, input_data_file))
215 {
216 clock_gettime(CLOCK_MONOTONIC, &t0[frame_idx % num_eos]);
217 eo->ProcessFrameStartAsync();
218 }
219 }
221 for (auto b : buffers)
222 free(b);
224 }
225 catch (tinn::Exception &e)
226 {
227 std::cerr << e.what() << std::endl;
228 status = false;
229 }
232 input_data_file.close();
233 output_data_file.close();
235 return status;
236 }
238 namespace tinn {
239 extern bool CompareFiles (const std::string &F1, const std::string &F2);
240 extern bool CompareFrames(const std::string &F1, const std::string &F2,
241 int numFrames, int width, int height);
242 }
244 bool RunAllConfigurations(int32_t num_devices, DeviceType device_type)
245 {
246 std::vector<std::string> configurations;
248 if (device_type == DeviceType::DLA)
249 configurations = {"dense_1x1", "j11_bn", "j11_cifar",
250 "j11_controlLayers", "j11_prelu", "j11_v2",
251 "jseg21", "jseg21_tiscapes", "smallRoi", "squeeze1_1"};
252 else
253 configurations = {"j11_bn",
254 "j11_controlLayers", "j11_v2",
255 "jseg21", "jseg21_tiscapes", "smallRoi", "squeeze1_1"};
257 int errors = 0;
258 for (auto config : configurations)
259 {
260 std::string config_file = "testvecs/config/infer/tidl_config_"
261 + config + ".txt";
262 std::cout << "Running " << config << " on " << num_devices
263 << " devices, type "
264 << ((device_type == DeviceType::DLA) ? "EVE" : "DSP")
265 << std::endl;
267 Configuration configuration;
268 bool status = configuration.ReadFromFile(config_file);
269 if (!status) { errors++; continue; }
271 status = RunConfiguration(config_file, num_devices, device_type);
273 if (!status) { errors++; continue; }
275 // Check output against reference output
276 std::string reference_output = "testvecs/reference/"
277 + config + "_ref.bin";
279 // Reference for jseg21_tiscapes only has one frame
280 if (config.compare("jseg21_tiscapes") == 0)
281 status = CompareFrames(configuration.outData, reference_output,
282 1, 1024, 512);
283 else
284 status = CompareFiles(configuration.outData, reference_output);
286 if (status) std::cout << config << " : PASSED" << std::endl;
287 else std::cout << config << " : FAILED" << std::endl;
289 if (!status) errors++;
290 }
292 if (errors > 0) return false;
294 return true;
295 }
299 bool ReadFrame(ExecutionObject &eo, int frame_idx,
300 const Configuration& configuration,
301 std::istream& input_file)
302 {
303 if (frame_idx >= configuration.numFrames)
304 return false;
306 char* frame_buffer = eo.GetInputBufferPtr();
307 assert (frame_buffer != nullptr);
309 input_file.read(eo.GetInputBufferPtr(),
310 eo.GetInputBufferSizeInBytes());
312 if (input_file.eof())
313 return false;
315 assert (input_file.good());
317 // Set the frame index being processed by the EO. This is used to
318 // sort the frames before they are output
319 eo.SetFrameIndex(frame_idx);
321 if (input_file.good())
322 return true;
324 return false;
325 }
327 bool WriteFrame(const ExecutionObject &eo, std::ostream& output_file)
328 {
329 output_file.write(
330 eo.GetOutputBufferPtr(), eo.GetOutputBufferSizeInBytes());
331 assert(output_file.good() == true);
333 if (output_file.good())
334 return true;
336 return false;
337 }
340 void ProcessArgs(int argc, char *argv[], std::string& config_file,
341 int& num_devices, DeviceType& device_type)
342 {
343 const struct option long_options[] =
344 {
345 {"config_file", required_argument, 0, 'c'},
346 {"num_devices", required_argument, 0, 'n'},
347 {"device_type", required_argument, 0, 't'},
348 {"help", no_argument, 0, 'h'},
349 {"verbose", no_argument, 0, 'v'},
350 {0, 0, 0, 0}
351 };
353 int option_index = 0;
355 while (true)
356 {
357 int c = getopt_long(argc, argv, "c:n:t:hv", long_options, &option_index);
359 if (c == -1)
360 break;
362 switch (c)
363 {
364 case 'c': config_file = optarg;
365 break;
367 case 'n': num_devices = atoi(optarg);
368 assert (num_devices > 0 && num_devices <= 4);
369 break;
371 case 't': if (*optarg == 'e')
372 device_type = DeviceType::DLA;
373 else if (*optarg == 'd')
374 device_type = DeviceType::DSP;
375 else
376 {
377 std::cerr << "Invalid argument to -t, only e or d"
378 " allowed" << std::endl;
379 exit(EXIT_FAILURE);
380 }
381 break;
383 case 'v': __TI_show_debug_ = true;
384 break;
386 case 'h': DisplayHelp();
387 exit(EXIT_SUCCESS);
388 break;
390 case '?': // Error in getopt_long
391 exit(EXIT_FAILURE);
392 break;
394 default:
395 std::cerr << "Unsupported option: " << c << std::endl;
396 break;
397 }
398 }
399 }
401 void DisplayHelp()
402 {
403 std::cout << "Usage: test_tinn\n"
404 " Will run all available networks if invoked without"
405 " any arguments.\n Use -c to run a single network.\n"
406 "Optional arguments:\n"
407 " -c Path to the configuration file\n"
408 " -n <number of cores> Number of cores to use (1 - 4)\n"
409 " -t <d|e> Type of core. d -> DSP, e -> DLA\n"
410 " -v Verbose output during execution\n"
411 " -h Help\n";
412 }