Initial commit
[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     }
91     // Process arguments
92     std::string config_file;
93     int         num_devices = 1;
94     DeviceType  device_type = DeviceType::DLA;
95     ProcessArgs(argc, argv, config_file, num_devices, device_type);
97     bool status = true;
98     if (!config_file.empty())
99         status = RunConfiguration(config_file, num_devices, device_type);
100     else
101     {
102         if (num_dla > 0)
103         {
104             //TODO: Use memory availability to determine # devices
105             // Run on 2 devices because there is not enough CMEM available by
106             // default
107             if (num_dla = 4) num_dla = 2;
108             status = RunAllConfigurations(num_dla, DeviceType::DLA);
109             status &= RunMultipleExecutors(
110                      "testvecs/config/infer/tidl_config_j11_v2.txt",
111                      "testvecs/config/infer/tidl_config_j11_cifar.txt",
112                      num_dla);
113         }
115         if (num_dsp > 0)
116         {
117             status &= RunAllConfigurations(num_dsp, DeviceType::DSP);
118         }
119     }
121     if (!status)
122     {
123         std::cout << "tidl FAILED" << std::endl;
124         return EXIT_FAILURE;
125     }
127     std::cout << "tidl PASSED" << std::endl;
128     return EXIT_SUCCESS;
131 bool RunConfiguration(const std::string& config_file, int num_devices,
132                       DeviceType device_type)
134     DeviceIds ids;
135     for (int i = 0; i < num_devices; i++)
136         ids.insert(static_cast<DeviceId>(i));
138     // Read the TI DL configuration file
139     Configuration configuration;
140     bool status = configuration.ReadFromFile(config_file);
141     if (!status)
142     {
143         std::cerr << "Error in configuration file: " << config_file
144                   << std::endl;
145         return false;
146     }
148     // Open input and output files
149     std::ifstream input_data_file(configuration.inData, std::ios::binary);
150     std::ofstream output_data_file(configuration.outData, std::ios::binary);
151     assert (input_data_file.good());
152     assert (output_data_file.good());
154     // Determine input frame size from configuration
155     size_t frame_sz = configuration.inWidth * configuration.inHeight *
156                       configuration.inNumChannels;
158     try
159     {
160         // Create a executor with the approriate core type, number of cores
161         // and configuration specified
162         Executor executor(device_type, ids, configuration);
164         // Query Executor for set of ExecutionObjects created
165         const ExecutionObjects& execution_objects =
166                                                 executor.GetExecutionObjects();
167         int num_eos = execution_objects.size();
169         // Allocate input and output buffers for each execution object
170         std::vector<void *> buffers;
171         for (auto &eo : execution_objects)
172         {
173             ArgInfo in  = { ArgInfo(malloc_ddr<char>(frame_sz), frame_sz)};
174             ArgInfo out = { ArgInfo(malloc_ddr<char>(frame_sz), frame_sz)};
175             eo->SetInputOutputBuffer(in, out);
177             buffers.push_back(in.ptr());
178             buffers.push_back(out.ptr());
179         }
181         #define MAX_NUM_EOS  4
182         struct timespec t0[MAX_NUM_EOS], t1;
184         // Process frames with available execution objects in a pipelined manner
185         // additional num_eos iterations to flush the pipeline (epilogue)
186         for (int frame_idx = 0;
187              frame_idx < configuration.numFrames + num_eos; frame_idx++)
188         {
189             ExecutionObject* eo = execution_objects[frame_idx % num_eos].get();
191             // Wait for previous frame on the same eo to finish processing
192             if (eo->ProcessFrameWait())
193             {
194                 clock_gettime(CLOCK_MONOTONIC, &t1);
195                 double elapsed_host =
196                                 ms_diff(t0[eo->GetFrameIndex() % num_eos], t1);
197                 double elapsed_device = eo->GetProcessTimeInMilliSeconds();
198                 double overhead = 100 - (elapsed_device/elapsed_host*100);
200                 std::cout << "frame[" << eo->GetFrameIndex() << "]: "
201                           << "Time on device: "
202                           << std::setw(6) << std::setprecision(4)
203                           << elapsed_device << "ms, "
204                           << "host: "
205                           << std::setw(6) << std::setprecision(4)
206                           << elapsed_host << "ms ";
207                 std::cout << "API overhead: "
208                           << std::setw(6) << std::setprecision(3)
209                           << overhead << " %" << std::endl;
211                 WriteFrame(*eo, output_data_file);
212             }
214             // Read a frame and start processing it with current eo
215             if (ReadFrame(*eo, frame_idx, configuration, input_data_file))
216             {
217                 clock_gettime(CLOCK_MONOTONIC, &t0[frame_idx % num_eos]);
218                 eo->ProcessFrameStartAsync();
219             }
220         }
222         for (auto b : buffers)
223             __free_ddr(b);
225     }
226     catch (tidl::Exception &e)
227     {
228         std::cerr << e.what() << std::endl;
229         status = false;
230     }
233     input_data_file.close();
234     output_data_file.close();
236     return status;
239 namespace tidl {
240 extern bool CompareFiles (const std::string &F1, const std::string &F2);
241 extern bool CompareFrames(const std::string &F1, const std::string &F2,
242                           int numFrames, int width, int height);
245 bool RunAllConfigurations(int32_t num_devices, DeviceType device_type)
247     std::vector<std::string> configurations;
249     if (device_type == DeviceType::DLA)
250         configurations = {"dense_1x1",  "j11_bn", "j11_cifar",
251                           "j11_controlLayers", "j11_prelu", "j11_v2",
252                           "jseg21", "jseg21_tiscapes", "smallRoi", "squeeze1_1"};
253     else
254         configurations = {"j11_bn",
255                           "j11_controlLayers", "j11_v2",
256                           "jseg21", "jseg21_tiscapes", "smallRoi", "squeeze1_1"};
258     int errors = 0;
259     for (auto config : configurations)
260     {
261         std::string config_file = "testvecs/config/infer/tidl_config_"
262                                   + config + ".txt";
263         std::cout << "Running " << config << " on " << num_devices
264                   << " devices, type "
265                   << ((device_type == DeviceType::DLA) ? "EVE" : "DSP")
266                   << std::endl;
268         Configuration configuration;
269         bool status = configuration.ReadFromFile(config_file);
270         if (!status) { errors++; continue; }
272         status = RunConfiguration(config_file, num_devices, device_type);
274         if (!status) { errors++; continue; }
276         // Check output against reference output
277         std::string reference_output = "testvecs/reference/"
278                                        + config + "_ref.bin";
280         // Reference for jseg21_tiscapes only has one frame
281         if (config.compare("jseg21_tiscapes") == 0)
282             status = CompareFrames(configuration.outData, reference_output,
283                                    1, 1024, 512);
284         else
285             status = CompareFiles(configuration.outData, reference_output);
287         if (status) std::cout << config << " : PASSED" << std::endl;
288         else        std::cout << config << " : FAILED" << std::endl;
290         if (!status) errors++;
291     }
293     if (errors > 0) return false;
295     return true;
300 bool ReadFrame(ExecutionObject &eo, int frame_idx,
301                const Configuration& configuration,
302                std::istream& input_file)
304     if (frame_idx >= configuration.numFrames)
305         return false;
307     char*  frame_buffer = eo.GetInputBufferPtr();
308     assert (frame_buffer != nullptr);
310     input_file.read(eo.GetInputBufferPtr(),
311                     eo.GetInputBufferSizeInBytes());
313     if (input_file.eof())
314         return false;
316     assert (input_file.good());
318     // Set the frame index  being processed by the EO. This is used to
319     // sort the frames before they are output
320     eo.SetFrameIndex(frame_idx);
322     if (input_file.good())
323         return true;
325     return false;
328 bool WriteFrame(const ExecutionObject &eo, std::ostream& output_file)
330     output_file.write(
331             eo.GetOutputBufferPtr(), eo.GetOutputBufferSizeInBytes());
332     assert(output_file.good() == true);
334     if (output_file.good())
335         return true;
337     return false;
341 void ProcessArgs(int argc, char *argv[], std::string& config_file,
342                  int& num_devices, DeviceType& device_type)
344     const struct option long_options[] =
345     {
346         {"config_file", required_argument, 0, 'c'},
347         {"num_devices", required_argument, 0, 'n'},
348         {"device_type", required_argument, 0, 't'},
349         {"help",        no_argument,       0, 'h'},
350         {"verbose",     no_argument,       0, 'v'},
351         {0, 0, 0, 0}
352     };
354     int option_index = 0;
356     while (true)
357     {
358         int c = getopt_long(argc, argv, "c:n:t:hv", long_options, &option_index);
360         if (c == -1)
361             break;
363         switch (c)
364         {
365             case 'c': config_file = optarg;
366                       break;
368             case 'n': num_devices = atoi(optarg);
369                       assert (num_devices > 0 && num_devices <= 4);
370                       break;
372             case 't': if (*optarg == 'e')
373                           device_type = DeviceType::DLA;
374                       else if (*optarg == 'd')
375                           device_type = DeviceType::DSP;
376                       else
377                       {
378                           std::cerr << "Invalid argument to -t, only e or d"
379                                        " allowed" << std::endl;
380                           exit(EXIT_FAILURE);
381                       }
382                       break;
384             case 'v': __TI_show_debug_ = true;
385                       break;
387             case 'h': DisplayHelp();
388                       exit(EXIT_SUCCESS);
389                       break;
391             case '?': // Error in getopt_long
392                       exit(EXIT_FAILURE);
393                       break;
395             default:
396                       std::cerr << "Unsupported option: " << c << std::endl;
397                       break;
398         }
399     }
402 void DisplayHelp()
404     std::cout << "Usage: tidl\n"
405                  "  Will run all available networks if tidl is invoked without"
406                  " any arguments.\n  Use -c to run a single network.\n"
407                  "Optional arguments:\n"
408                  " -c                   Path to the configuration file\n"
409                  " -n <number of cores> Number of cores to use (1 - 4)\n"
410                  " -t <d|e>             Type of core. d -> DSP, e -> DLA\n"
411                  " -v                   Verbose output during execution\n"
412                  " -h                   Help\n";