Add version information to Makefile, API
[tidl/tidl-api.git] / examples / test / main.cpp
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 tidl;
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 TIDL on the SoC, exit
81     uint32_t num_dla =
82                 Executor::GetNumDevicesSupportingTIDL(DeviceType::DLA);
83     uint32_t num_dsp =
84                 Executor::GetNumDevicesSupportingTIDL(DeviceType::DSP);
85     if (num_dla == 0 && num_dsp == 0)
86     {
87         std::cout << "TI DL not supported on this SoC." << std::endl;
88         return EXIT_SUCCESS;
89     }
90     std::cout << "API Version: " << Executor::GetAPIVersion() << std::endl;
92     // Process arguments
93     std::string config_file;
94     int         num_devices = 1;
95     DeviceType  device_type = DeviceType::DLA;
96     ProcessArgs(argc, argv, config_file, num_devices, device_type);
98     bool status = true;
99     if (!config_file.empty())
100         status = RunConfiguration(config_file, num_devices, device_type);
101     else
102     {
103         if (num_dla > 0)
104         {
105             //TODO: Use memory availability to determine # devices
106             // Run on 2 devices because there is not enough CMEM available by
107             // default
108             if (num_dla = 4) num_dla = 2;
109             status = RunAllConfigurations(num_dla, DeviceType::DLA);
110             status &= RunMultipleExecutors(
111                      "testvecs/config/infer/tidl_config_j11_v2.txt",
112                      "testvecs/config/infer/tidl_config_j11_cifar.txt",
113                      num_dla);
114         }
116         if (num_dsp > 0)
117         {
118             status &= RunAllConfigurations(num_dsp, DeviceType::DSP);
119         }
120     }
122     if (!status)
123     {
124         std::cout << "tidl FAILED" << std::endl;
125         return EXIT_FAILURE;
126     }
128     std::cout << "tidl PASSED" << std::endl;
129     return EXIT_SUCCESS;
132 bool RunConfiguration(const std::string& config_file, int num_devices,
133                       DeviceType device_type)
135     DeviceIds ids;
136     for (int i = 0; i < num_devices; i++)
137         ids.insert(static_cast<DeviceId>(i));
139     // Read the TI DL configuration file
140     Configuration configuration;
141     bool status = configuration.ReadFromFile(config_file);
142     if (!status)
143     {
144         std::cerr << "Error in configuration file: " << config_file
145                   << std::endl;
146         return false;
147     }
149     // Open input and output files
150     std::ifstream input_data_file(configuration.inData, std::ios::binary);
151     std::ofstream output_data_file(configuration.outData, std::ios::binary);
152     assert (input_data_file.good());
153     assert (output_data_file.good());
155     // Determine input frame size from configuration
156     size_t frame_sz = configuration.inWidth * configuration.inHeight *
157                       configuration.inNumChannels;
159     try
160     {
161         // Create a executor with the approriate core type, number of cores
162         // and configuration specified
163         Executor executor(device_type, ids, configuration);
165         // Query Executor for set of ExecutionObjects created
166         const ExecutionObjects& execution_objects =
167                                                 executor.GetExecutionObjects();
168         int num_eos = execution_objects.size();
170         // Allocate input and output buffers for each execution object
171         std::vector<void *> buffers;
172         for (auto &eo : execution_objects)
173         {
174             ArgInfo in  = { ArgInfo(malloc_ddr<char>(frame_sz), frame_sz)};
175             ArgInfo out = { ArgInfo(malloc_ddr<char>(frame_sz), frame_sz)};
176             eo->SetInputOutputBuffer(in, out);
178             buffers.push_back(in.ptr());
179             buffers.push_back(out.ptr());
180         }
182         #define MAX_NUM_EOS  4
183         struct timespec t0[MAX_NUM_EOS], t1;
185         // Process frames with available execution objects in a pipelined manner
186         // additional num_eos iterations to flush the pipeline (epilogue)
187         for (int frame_idx = 0;
188              frame_idx < configuration.numFrames + num_eos; frame_idx++)
189         {
190             ExecutionObject* eo = execution_objects[frame_idx % num_eos].get();
192             // Wait for previous frame on the same eo to finish processing
193             if (eo->ProcessFrameWait())
194             {
195                 clock_gettime(CLOCK_MONOTONIC, &t1);
196                 double elapsed_host =
197                                 ms_diff(t0[eo->GetFrameIndex() % num_eos], t1);
198                 double elapsed_device = eo->GetProcessTimeInMilliSeconds();
199                 double overhead = 100 - (elapsed_device/elapsed_host*100);
201                 std::cout << "frame[" << eo->GetFrameIndex() << "]: "
202                           << "Time on device: "
203                           << std::setw(6) << std::setprecision(4)
204                           << elapsed_device << "ms, "
205                           << "host: "
206                           << std::setw(6) << std::setprecision(4)
207                           << elapsed_host << "ms ";
208                 std::cout << "API overhead: "
209                           << std::setw(6) << std::setprecision(3)
210                           << overhead << " %" << std::endl;
212                 WriteFrame(*eo, output_data_file);
213             }
215             // Read a frame and start processing it with current eo
216             if (ReadFrame(*eo, frame_idx, configuration, input_data_file))
217             {
218                 clock_gettime(CLOCK_MONOTONIC, &t0[frame_idx % num_eos]);
219                 eo->ProcessFrameStartAsync();
220             }
221         }
223         for (auto b : buffers)
224             __free_ddr(b);
226     }
227     catch (tidl::Exception &e)
228     {
229         std::cerr << e.what() << std::endl;
230         status = false;
231     }
234     input_data_file.close();
235     output_data_file.close();
237     return status;
240 namespace tidl {
241 extern bool CompareFiles (const std::string &F1, const std::string &F2);
242 extern bool CompareFrames(const std::string &F1, const std::string &F2,
243                           int numFrames, int width, int height);
246 bool RunAllConfigurations(int32_t num_devices, DeviceType device_type)
248     std::vector<std::string> configurations;
250     if (device_type == DeviceType::DLA)
251         configurations = {"dense_1x1",  "j11_bn", "j11_cifar",
252                           "j11_controlLayers", "j11_prelu", "j11_v2",
253                           "jseg21", "jseg21_tiscapes", "smallRoi", "squeeze1_1"};
254     else
255         configurations = {"j11_bn",
256                           "j11_controlLayers", "j11_v2",
257                           "jseg21", "jseg21_tiscapes", "smallRoi", "squeeze1_1"};
259     int errors = 0;
260     for (auto config : configurations)
261     {
262         std::string config_file = "testvecs/config/infer/tidl_config_"
263                                   + config + ".txt";
264         std::cout << "Running " << config << " on " << num_devices
265                   << " devices, type "
266                   << ((device_type == DeviceType::DLA) ? "EVE" : "DSP")
267                   << std::endl;
269         Configuration configuration;
270         bool status = configuration.ReadFromFile(config_file);
271         if (!status) { errors++; continue; }
273         status = RunConfiguration(config_file, num_devices, device_type);
275         if (!status) { errors++; continue; }
277         // Check output against reference output
278         std::string reference_output = "testvecs/reference/"
279                                        + config + "_ref.bin";
281         // Reference for jseg21_tiscapes only has one frame
282         if (config.compare("jseg21_tiscapes") == 0)
283             status = CompareFrames(configuration.outData, reference_output,
284                                    1, 1024, 512);
285         else
286             status = CompareFiles(configuration.outData, reference_output);
288         if (status) std::cout << config << " : PASSED" << std::endl;
289         else        std::cout << config << " : FAILED" << std::endl;
291         if (!status) errors++;
292     }
294     if (errors > 0) return false;
296     return true;
301 bool ReadFrame(ExecutionObject &eo, int frame_idx,
302                const Configuration& configuration,
303                std::istream& input_file)
305     if (frame_idx >= configuration.numFrames)
306         return false;
308     char*  frame_buffer = eo.GetInputBufferPtr();
309     assert (frame_buffer != nullptr);
311     input_file.read(eo.GetInputBufferPtr(),
312                     eo.GetInputBufferSizeInBytes());
314     if (input_file.eof())
315         return false;
317     assert (input_file.good());
319     // Set the frame index  being processed by the EO. This is used to
320     // sort the frames before they are output
321     eo.SetFrameIndex(frame_idx);
323     if (input_file.good())
324         return true;
326     return false;
329 bool WriteFrame(const ExecutionObject &eo, std::ostream& output_file)
331     output_file.write(
332             eo.GetOutputBufferPtr(), eo.GetOutputBufferSizeInBytes());
333     assert(output_file.good() == true);
335     if (output_file.good())
336         return true;
338     return false;
342 void ProcessArgs(int argc, char *argv[], std::string& config_file,
343                  int& num_devices, DeviceType& device_type)
345     const struct option long_options[] =
346     {
347         {"config_file", required_argument, 0, 'c'},
348         {"num_devices", required_argument, 0, 'n'},
349         {"device_type", required_argument, 0, 't'},
350         {"help",        no_argument,       0, 'h'},
351         {"verbose",     no_argument,       0, 'v'},
352         {0, 0, 0, 0}
353     };
355     int option_index = 0;
357     while (true)
358     {
359         int c = getopt_long(argc, argv, "c:n:t:hv", long_options, &option_index);
361         if (c == -1)
362             break;
364         switch (c)
365         {
366             case 'c': config_file = optarg;
367                       break;
369             case 'n': num_devices = atoi(optarg);
370                       assert (num_devices > 0 && num_devices <= 4);
371                       break;
373             case 't': if (*optarg == 'e')
374                           device_type = DeviceType::DLA;
375                       else if (*optarg == 'd')
376                           device_type = DeviceType::DSP;
377                       else
378                       {
379                           std::cerr << "Invalid argument to -t, only e or d"
380                                        " allowed" << std::endl;
381                           exit(EXIT_FAILURE);
382                       }
383                       break;
385             case 'v': __TI_show_debug_ = true;
386                       break;
388             case 'h': DisplayHelp();
389                       exit(EXIT_SUCCESS);
390                       break;
392             case '?': // Error in getopt_long
393                       exit(EXIT_FAILURE);
394                       break;
396             default:
397                       std::cerr << "Unsupported option: " << c << std::endl;
398                       break;
399         }
400     }
403 void DisplayHelp()
405     std::cout << "Usage: test_tinn\n"
406                  "  Will run all available networks if invoked without"
407                  " any arguments.\n  Use -c to run a single network.\n"
408                  "Optional arguments:\n"
409                  " -c                   Path to the configuration file\n"
410                  " -n <number of cores> Number of cores to use (1 - 4)\n"
411                  " -t <d|e>             Type of core. d -> DSP, e -> DLA\n"
412                  " -v                   Verbose output during execution\n"
413                  " -h                   Help\n";