Added Python 3 bindings for TIDL API
authorAjay Jayaraj <ajayj@ti.com>
Fri, 21 Sep 2018 15:53:30 +0000 (10:53 -0500)
committerAjay Jayaraj <ajayj@ti.com>
Wed, 26 Sep 2018 21:37:31 +0000 (16:37 -0500)
* Using pybind11 v2.2 to add Python 3 bindings to TIDL API classes/methods
  https://pybind11.readthedocs.io/en/stable/index.html
  https://github.com/pybind/pybind11/tree/v2.2
* Leveraging the Python buffer protocol to expose input/output buffers
  from ExecutionObject/ExecutionObjectPipeline to Python application
  code. This eliminates copies between the Python application and the TIDL
  API library. (see examples/pybind/one_eo_per_frame.py).
* Methods renamed to follow Python style guide (PEP8)
* Bindings split across multiple pybind_* source files to reduce compile
  time
* tidl_api/Makefile builds a shared object - tidl.so. Add this so to
  PYTHONPATH to make the tidl module available to the Python interpreter.
  >>> import tidl
  >>> help (tidl)
* See examples/pybind for examples of using the Python bindings

(MCT-1009)

15 files changed:
examples/pybind/layer_output.py [new file with mode: 0755]
examples/pybind/one_eo_per_frame.py [new file with mode: 0755]
examples/pybind/tidl_app_utils.py [new file with mode: 0755]
examples/pybind/two_eo_per_frame.py [new file with mode: 0755]
makefile
tidl_api/Makefile
tidl_api/inc/configuration.h
tidl_api/make.buildid
tidl_api/make.inc
tidl_api/src/pybind_common.h [new file with mode: 0644]
tidl_api/src/pybind_configuration.cpp [new file with mode: 0644]
tidl_api/src/pybind_eo.cpp [new file with mode: 0644]
tidl_api/src/pybind_eop.cpp [new file with mode: 0644]
tidl_api/src/pybind_executor.cpp [new file with mode: 0644]
tidl_api/src/pybind_helpers.cpp [new file with mode: 0644]

diff --git a/examples/pybind/layer_output.py b/examples/pybind/layer_output.py
new file mode 100755 (executable)
index 0000000..56f1d4a
--- /dev/null
@@ -0,0 +1,113 @@
+#!/usr/bin/python3
+
+# Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Texas Instruments Incorporated nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+
+from tidl import DeviceId, DeviceType, Configuration, Executor, TidlError
+from tidl import allocate_memory, free_memory
+
+from tidl_app_utils import read_frame, write_output, report_time
+
+import argparse
+
+def main(config_file, num_frames):
+    c = Configuration()
+    c.read_from_file(config_file)
+    c.enable_layer_dump = True
+    c.num_frames = num_frames
+
+    num_dsp = Executor.get_num_devices(DeviceType.DSP)
+    num_eve = Executor.get_num_devices(DeviceType.EVE)
+
+    if (num_dsp == 0 and num_eve == 0):
+        print('No TIDL API capable devices available')
+        return
+
+    if (num_eve > 0):
+        device_type = DeviceType.EVE
+    else:
+        device_type = DeviceType.DSP
+
+    # Since we are dumping layer outputs, just run on one device
+    run(device_type, 1, c)
+
+    return
+
+def run(device_type, num_devices, c):
+    """ Run the network on a single device and dump output of each layer"""
+
+    print('Running network on {} {}'.format(num_devices, device_type))
+
+    device_ids = set([DeviceId.ID0])
+
+    try:
+        print('TIDL API: performing one time initialization ...')
+
+        executor = Executor(device_type, device_ids, c, 1)
+
+        # Collect all EOs from EVE and DSP executors
+        eos = []
+        for i in range(executor.get_num_execution_objects()):
+            eos.append(executor.at(i))
+
+        allocate_memory(eos)
+
+        # Open input, output files
+        f_in  = open(c.in_data, 'rb')
+
+
+        print('TIDL API: processing input frames ...')
+
+        num_eos = len(eos)
+        for frame_index in range(c.num_frames+num_eos):
+            eo = eos [frame_index % num_eos]
+
+            if (eo.process_frame_wait()):
+                eo.write_layer_outputs_to_file()
+
+            if (read_frame(eo, frame_index, c, f_in)):
+                eo.process_frame_start_async()
+
+
+        f_in.close()
+
+        free_memory(eos)
+    except TidlError as err:
+        print (err)
+
+    return
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description=
+                       'Dump output of each network layer to file. ')
+    parser.add_argument('-c', '--config_file',
+               default='../test/testvecs/config/infer/tidl_config_j11_v2.txt',
+               help='Path to TIDL config file')
+    args = parser.parse_args()
+
+    # Run network for 1 frame since we interested in intermediate layer outputs
+    main(args.config_file, 1)
diff --git a/examples/pybind/one_eo_per_frame.py b/examples/pybind/one_eo_per_frame.py
new file mode 100755 (executable)
index 0000000..96eb420
--- /dev/null
@@ -0,0 +1,126 @@
+#!/usr/bin/python3
+
+# Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Texas Instruments Incorporated nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+
+from tidl import DeviceId, DeviceType, Configuration, Executor, TidlError
+from tidl import allocate_memory, free_memory
+
+from tidl_app_utils import read_frame, write_output, report_time
+
+import argparse
+
+def main(config_file, num_frames):
+    c = Configuration()
+    c.read_from_file(config_file)
+    c.enable_api_trace = False
+    c.num_frames = num_frames
+
+    num_dsp = Executor.get_num_devices(DeviceType.DSP)
+    num_eve = Executor.get_num_devices(DeviceType.EVE)
+
+    if (num_dsp == 0 and num_eve == 0):
+        print('No TIDL API capable devices available')
+        return
+
+    run(num_eve, num_dsp, c)
+
+    return
+
+def run(num_eve, num_dsp, c):
+    """ Run the network on the specified device type and number of devices"""
+
+    print('Running network across {} EVEs, {} DSPs'.format(num_eve, num_dsp))
+
+    dsp_device_ids = set([DeviceId.ID0, DeviceId.ID1,
+                          DeviceId.ID2, DeviceId.ID3][0:num_dsp])
+    eve_device_ids = set([DeviceId.ID0, DeviceId.ID1,
+                          DeviceId.ID2, DeviceId.ID3][0:num_eve])
+
+    # Heap sizes for this network determined using Configuration.showHeapStats
+    c.param_heap_size   = (3 << 20)
+    c.network_heap_size = (20 << 20)
+
+
+    try:
+        print('TIDL API: performing one time initialization ...')
+
+        eve = Executor(DeviceType.EVE, eve_device_ids, c, 1)
+        dsp = Executor(DeviceType.DSP, dsp_device_ids, c, 1)
+
+        # Collect all EOs from EVE and DSP executors
+        eos = []
+        for i in range(eve.get_num_execution_objects()):
+            eos.append(eve.at(i))
+
+        for i in range(dsp.get_num_execution_objects()):
+            eos.append(dsp.at(i))
+
+        allocate_memory(eos)
+
+        # Open input, output files
+        f_in  = open(c.in_data, 'rb')
+        f_out = open(c.out_data, 'wb')
+
+
+        print('TIDL API: processing input frames ...')
+
+        num_eos = len(eos)
+        for frame_index in range(c.num_frames+num_eos):
+            eo = eos [frame_index % num_eos]
+
+            if (eo.process_frame_wait()):
+                report_time(eo)
+                write_output(eo, f_out)
+
+            if (read_frame(eo, frame_index, c, f_in)):
+                eo.process_frame_start_async()
+
+
+        f_in.close()
+        f_out.close()
+
+        free_memory(eos)
+    except TidlError as err:
+        print (err)
+
+    return
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description=
+                       'Process frames using all available Execution Objects. '
+                       'Each ExecutionObject processes a single frame')
+    parser.add_argument('-n', '--num_frames',
+                        type=int,
+                        default=1,
+                        help='Number of frames to process')
+    args = parser.parse_args()
+
+    # Heaps are sized for the j11_v2 network. Changing the network will
+    # require updating network_heap_size and param_heap_size
+    main('../test/testvecs/config/infer/tidl_config_j11_v2.txt', 
+         args.num_frames)
diff --git a/examples/pybind/tidl_app_utils.py b/examples/pybind/tidl_app_utils.py
new file mode 100755 (executable)
index 0000000..e8be16c
--- /dev/null
@@ -0,0 +1,78 @@
+#!/usr/bin/python3
+
+# Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Texas Instruments Incorporated nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+
+from tidl import DeviceId
+from tidl import DeviceType
+from tidl import Configuration
+from tidl import Executor
+from tidl import TidlError
+
+import tidl
+
+def read_frame(eo, frame_index, c, f):
+    """Read a frame into the ExecutionObject input buffer"""
+
+    if (frame_index >= c.num_frames):
+        return False
+
+    # Read into the EO's input buffer
+    arg_info = eo.get_input_buffer()
+    bytes_read = f.readinto(arg_info)
+
+    if (bytes_read != arg_info.size()):
+        print("Expected {} bytes, read {}".format(size, bytes_read))
+        return False
+
+    if (len(f.peek(1)) == 0):
+        f.seek(0)
+
+    eo.set_frame_index(frame_index)
+
+    return True
+
+def write_output(eo, f):
+    """Write the output buffer to file"""
+
+    arg_info = eo.get_output_buffer()
+    f.write(arg_info)
+
+def report_time(eo):
+    """Report execution time on host and device"""
+
+    elapsed_host   = eo.get_host_process_time_in_ms()
+    elapsed_device = eo.get_process_time_in_ms()
+    overhead = elapsed_host - elapsed_device
+
+    # https://pyformat.info/
+    print('frame{:3d}: Time on {}: {:4.2f} ms, host: {:4.2f} ms '
+          'API overhead: {:2.2f} ms'.format(eo.get_frame_index(),
+                                           eo.get_device_name(),
+                                           elapsed_device,
+                                           elapsed_host,
+                                           overhead))
diff --git a/examples/pybind/two_eo_per_frame.py b/examples/pybind/two_eo_per_frame.py
new file mode 100755 (executable)
index 0000000..c6b16c6
--- /dev/null
@@ -0,0 +1,134 @@
+#!/usr/bin/python3
+
+# Copyright (c) 2018 Texas Instruments Incorporated - http://www.ti.com/
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Texas Instruments Incorporated nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+
+from tidl import DeviceId, DeviceType, Configuration, TidlError
+from tidl import Executor, ExecutionObjectPipeline
+from tidl import allocate_memory, free_memory
+
+from tidl_app_utils import read_frame, write_output, report_time
+
+import argparse
+
+def main(config_file, num_frames):
+    c = Configuration()
+    c.read_from_file(config_file)
+    c.enable_api_trace = False
+    c.num_frames = num_frames
+
+    num_dsp = Executor.get_num_devices(DeviceType.DSP)
+    num_eve = Executor.get_num_devices(DeviceType.EVE)
+
+    if (num_dsp == 0 or num_eve == 0):
+        print('This example required EVEs and DSPs.')
+        return
+
+    run(num_eve, num_dsp, c)
+
+    return
+
+def run(num_eve, num_dsp, c):
+    """ Run the network on the specified device type and number of devices"""
+
+    print('Running on {} EVEs, {} DSPs'.format(num_eve, num_dsp))
+
+    dsp_device_ids = set([DeviceId.ID0, DeviceId.ID1,
+                          DeviceId.ID2, DeviceId.ID3][0:num_dsp])
+    eve_device_ids = set([DeviceId.ID0, DeviceId.ID1,
+                          DeviceId.ID2, DeviceId.ID3][0:num_eve])
+
+    EVE_LAYER_GROUP_ID = 1
+    DSP_LAYER_GROUP_ID = 2
+    c.layer_index_to_layer_group_id = {12:DSP_LAYER_GROUP_ID, 
+                                       13:DSP_LAYER_GROUP_ID, 
+                                       14:DSP_LAYER_GROUP_ID};
+    
+    # Heap sizes for this network determined using Configuration.showHeapStats
+    c.param_heap_size   = (3 << 20)
+    c.network_heap_size = (20 << 20)
+
+
+    try:
+        print('TIDL API: performing one time initialization ...')
+
+        eve = Executor(DeviceType.EVE, eve_device_ids, c, EVE_LAYER_GROUP_ID)
+        dsp = Executor(DeviceType.DSP, dsp_device_ids, c, DSP_LAYER_GROUP_ID)
+
+        num_eve_eos = eve.get_num_execution_objects()
+        num_dsp_eos = dsp.get_num_execution_objects()
+
+        eops = []
+        num_pipe = max(num_eve_eos, num_dsp_eos)
+        for i in range(num_pipe):
+            eops.append(ExecutionObjectPipeline( [ eve.at(i % num_eve_eos),
+                                                   dsp.at(i % num_dsp_eos)]))
+
+        allocate_memory(eops)
+
+        # Open input, output files
+        f_in  = open(c.in_data, 'rb')
+        f_out = open(c.out_data, 'wb')
+
+
+        print('TIDL API: processing input frames ...')
+
+        num_eops = len(eops)
+        for frame_index in range(c.num_frames+num_eops):
+            eop = eops [frame_index % num_eops]
+
+            if (eop.process_frame_wait()):
+                report_time(eop)
+                write_output(eop, f_out)
+
+            if (read_frame(eop, frame_index, c, f_in)):
+                eop.process_frame_start_async()
+
+
+        f_in.close()
+        f_out.close()
+
+        free_memory(eops)
+    except TidlError as err:
+        print (err)
+
+    return
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description=
+                       'Process frames using ExecutionObjectPipeline. '
+                       'Each ExecutionObjectPipeline processes a single frame')
+    parser.add_argument('-n', '--num_frames',
+                        type=int,
+                        default=1,
+                        help='Number of frames to process')
+    args = parser.parse_args()
+
+    # Heaps are sized for the j11_v2 network. Changing the network will
+    # require updating network_heap_size and param_heap_size
+    main('../test/testvecs/config/infer/tidl_config_j11_v2.txt', 
+         args.num_frames)
index e94e5869ce12a1360de5c214039be59570b69ffd..fec1e200d3f7f80198dd062633c7676c84276ef5 100644 (file)
--- a/makefile
+++ b/makefile
@@ -71,6 +71,9 @@ install-examples: build-examples
 build-viewer:
        $(MAKE) TARGET=$(VIEWER_TARGET) -C viewer
 
+build-python-so:
+       $(MAKE) -C tidl_api tidl.so
+
 clean:
        $(MAKE) -C tidl_api     clean
        $(MAKE) -C examples     clean
index 3fc6a2c1922f86dc09aa5e64d83cf7e0c5873f21..77b5a4b7b80e69c34217eb2029133fcc7f5ea035 100644 (file)
@@ -28,6 +28,7 @@ LIB_NAME = tidl_api.a
 DSP_SRCFILE = dsp/ocl_wrapper.cl
 DSP_OUTFILE = dsp/ocl_wrapper.dsp_h
 LIB_IMGUTIL_NAME = tidl_imgutil.a
+PY_LIB_NAME = tidl.so
 
 all: $(LIB_NAME) $(LIB_IMGUTIL_NAME)
 
@@ -40,16 +41,18 @@ AR = ar
 
 SRCS = ocl_device.cpp configuration_parser.cpp configuration.cpp\
           executor.cpp execution_object.cpp trace.cpp util.cpp \
-           execution_object_pipeline.cpp
+       execution_object_pipeline.cpp
 SRCS_IMGUTIL = imgutil.cpp
+SRCS_PYBIND  = pybind_eo.cpp pybind_eop.cpp pybind_executor.cpp \
+                          pybind_configuration.cpp pybind_helpers.cpp
 
 OBJS = $(SRCS:.cpp=.o)
 OBJS_IMGUTIL = $(SRCS_IMGUTIL:.cpp=.o)
+OBJS_PYBIND  = $(SRCS_PYBIND:.cpp=.o)
 
-HOST_SRC_FILES = $(addprefix src/,$(SRCS))
-HOST_OBJ_FILES = $(addprefix obj/,$(OBJS))
-HOST_SRC_IMGUTIL_FILES = $(addprefix src/,$(SRCS_IMGUTIL))
+HOST_OBJ_FILES         = $(addprefix obj/,$(OBJS))
 HOST_OBJ_IMGUTIL_FILES = $(addprefix obj/,$(OBJS_IMGUTIL))
+HOST_OBJ_PYBIND_FILES  = $(addprefix obj/,$(OBJS_PYBIND))
 
 HEADERS  = src/common_defines.h src/executor_impl.h src/ocl_device.h
 HEADERS += src/parameters.h src/tidl_create_params.h src/trace.h src/util.h
@@ -63,10 +66,12 @@ else
 endif
 
 CXXFLAGS += -std=c++11 -Wall -Werror -Wno-error=ignored-attributes
+CXXFLAGS += -fPIC
 CXXFLAGS += -I$(TI_OCL_INSTALL)/usr/share/ti/opencl
 CXXFLAGS += -I$(TARGET_ROOTDIR)/usr/share/ti/opencl
 CXXFLAGS += -Isrc -Iinc
 CXXFLAGS += $(BUILD_ID)
+PY_INCLUDE = -I$(TARGET_ROOTDIR)/usr/include/python3.5m -I$(PYBIND11_INC_DIR)
 
 $(DSP_OUTFILE): $(DSP_SRCFILE)
        $(MAKE) -C dsp
@@ -74,6 +79,12 @@ $(DSP_OUTFILE): $(DSP_SRCFILE)
 src/ocl_device.cpp: $(DSP_OUTFILE)
        touch $@
 
+$(HOST_OBJ_PYBIND_FILES): obj/%.o: src/%.cpp $(HEADERS) src/pybind_common.h
+       @mkdir -p obj
+       @echo Compiling pybind $< ...
+       $(CXX) -c $(PY_INCLUDE) $(CXXFLAGS) $< -o $@
+
+
 obj/%.o: src/%.cpp $(HEADERS)
        @mkdir -p obj
        @echo Compiling $< ...
@@ -82,13 +93,15 @@ obj/%.o: src/%.cpp $(HEADERS)
 $(LIB_NAME): $(HOST_OBJ_FILES)
        $(AR) cr $@ $(HOST_OBJ_FILES)
 
+$(PY_LIB_NAME): $(HOST_OBJ_PYBIND_FILES) $(LIB_NAME)
+       $(CXX) $(CXXFLAGS) -Wl,-Bsymbolic -shared -lOpenCL -locl_util $^ -o $@
 
 $(LIB_IMGUTIL_NAME): $(HOST_OBJ_IMGUTIL_FILES)
        $(AR) cr $@ $(HOST_OBJ_IMGUTIL_FILES)
 
 clean::
        $(MAKE) -C dsp clean
-       $(RM) -f $(LIB_NAME) $(HOST_OBJ_FILES)
+       $(RM) -f $(LIB_NAME) $(PY_LIB_NAME) $(HOST_OBJ_FILES)
        $(RM) -f $(LIB_IMGUTIL_NAME) $(HOST_OBJ_IMGUTIL_FILES)
        $(RM) -rf obj
 
index 531bb6dc2bb9798156a4717d7305f338aeb72e68..1fc033ceb32e7faa40d4ea8d5073c3b4b688aed5 100644 (file)
@@ -72,6 +72,7 @@ class Configuration
     //! Force to run all layers, regardless of layersGroupId partitioning
     bool    runFullNet;
 
+    //! Deprecated, do not use.
     //! When set, inputs are taken from TIDL internal buffers that contain
     //! outputs of previous layersGroupId, instead of from user application
     bool     enableInternalInput;
index b114cc299690fd3c87d3ab393dd05ade30668139..474309c010884744426ea74321c1d807fcf83743 100644 (file)
@@ -27,7 +27,6 @@
 MAJOR_VER=1
 MINOR_VER=2
 PATCH_VER=0
-BUILD_VER=0
 
 ifeq ($(shell git rev-parse --short HEAD 2>&1 1>/dev/null; echo $$?),0)
 BUILD_SHA?=$(shell git rev-parse --short HEAD)
@@ -35,6 +34,6 @@ endif
 
 .PHONY: $(BUILD_ID)
 BUILD_ID := -D_BUILD_VER=$(shell echo "" | \
-                awk '{ printf ("%02d.%02d.%02d.%02d", $(MAJOR_VER), \
-                $(MINOR_VER), $(PATCH_VER), $(BUILD_VER)); }') \
+                awk '{ printf ("%02d.%02d.%02d", $(MAJOR_VER), \
+                $(MINOR_VER), $(PATCH_VER)); }') \
                        -D_BUILD_SHA=$(BUILD_SHA)
index aee34fe37be9e891f908385396d97a5a6fb67a92..e1f221fa710ad8b76322584b8e652c07f11060eb 100644 (file)
 # - TI_OCL_CGT_INSTALL : TI C6x CGT installation directory if not system package
 #
 # ------------------------------------------------------------------------------
+#
+# PYBIND11_INC_DIR : Path to pybind11 include files used to generate Python
+#                    bindings for TIDL API. E.g. pybind11.git/include
+#
+# ------------------------------------------------------------------------------
+
+PYBIND11_INC_DIR ?= pybind11/include
 
 ifeq ($(MAKECMDGOALS),clean)
     # config not required for clean
diff --git a/tidl_api/src/pybind_common.h b/tidl_api/src/pybind_common.h
new file mode 100644 (file)
index 0000000..68a793e
--- /dev/null
@@ -0,0 +1,52 @@
+/******************************************************************************
+ * Copyright (c) 2018-2018 Texas Instruments Incorporated - http://www.ti.com/
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *      * Neither the name of Texas Instruments Incorporated nor the
+ *        names of its contributors may be used to endorse or promote products
+ *        derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#pragma once
+
+#include <vector>
+#include <sstream>
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+#include <pybind11/stl_bind.h>
+
+#include "configuration.h"
+#include "executor.h"
+#include "execution_object.h"
+#include "execution_object_pipeline.h"
+
+using EO = tidl::ExecutionObject;
+using EOP = tidl::ExecutionObjectPipeline;
+
+using std::vector;
+
+using namespace tidl;
+using namespace pybind11;
+
+void init_configuration(module &m);
+void init_eo(module &m);
+void init_eop(module &m);
diff --git a/tidl_api/src/pybind_configuration.cpp b/tidl_api/src/pybind_configuration.cpp
new file mode 100644 (file)
index 0000000..b5cab74
--- /dev/null
@@ -0,0 +1,90 @@
+/******************************************************************************
+ * Copyright (c) 2018-2018 Texas Instruments Incorporated - http://www.ti.com/
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *      * Neither the name of Texas Instruments Incorporated nor the
+ *        names of its contributors may be used to endorse or promote products
+ *        derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include "pybind_common.h"
+
+void init_configuration(module &m)
+{
+    class_<Configuration>(m, "Configuration")
+               .def(init<>())
+        .def_readwrite("num_frames", &Configuration::numFrames,
+            "Number of frames of input data. Can be 0 if data is\n"
+            "not read from file")
+
+        .def_readwrite("height", &Configuration::inHeight)
+
+        .def_readwrite("width", &Configuration::inWidth)
+
+        .def_readwrite("channels", &Configuration::inNumChannels)
+
+        .def_readwrite("pre_proc_type", &Configuration::preProcType,
+            "Pre-processing type applied to the input frame")
+
+        .def_readwrite("run_full_net", &Configuration::runFullNet,
+            "Force to run all layers, regardless of layersGroupId partitioning")
+
+        .def_readwrite("network_binary", &Configuration::netBinFile,
+            "Path to the TIDL network binary file")
+
+        .def_readwrite("parameter_binary", &Configuration::paramsBinFile,
+            "Path to the TIDL parameter binary file")
+
+        .def_readwrite("network_heap_size", &Configuration::NETWORK_HEAP_SIZE,
+            "Size of the device side network heap\n"
+            "This heap is used for allocating memory required to"
+            "run the network on the device. One per ExecutionObject.")
+
+        .def_readwrite("param_heap_size", &Configuration::PARAM_HEAP_SIZE,
+            "Size of the device side heap used for parameter data.\n"
+            "The size depends on the size of the parameter binary file.\n"
+            "There is one parameter heap for each instance of Executor\n")
+
+        .def_readwrite("enable_api_trace", &Configuration::enableApiTrace,
+            "Debug - Set to True to generate a trace of host/device\n"
+            "function calls")
+
+        .def_readwrite("enable_layer_dump", &Configuration::enableOutputTrace,
+            "Debug - Enable dump of output buffers associated with each layer")
+
+        .def_readwrite("show_heap_stats", &Configuration::showHeapStats,
+            "Debug - Shows total size of PARAM and NETWORK heaps. Also \n"
+            "shows bytes free after all allocations. Used to adjust heap sizes")
+
+        .def_readwrite("layer_index_to_layer_group_id",
+                       &Configuration::layerIndex2LayerGroupId,
+            "Map of layer index to layer group id. Used to override \n"
+            "layer group assigment for layers. Any layer not specified\n"
+            "in this map will retain its existing mapping")
+
+        .def_readwrite("in_data", &Configuration::inData)
+
+        .def_readwrite("out_data", &Configuration::outData)
+
+        .def("read_from_file", &Configuration::ReadFromFile,
+             "Read a configuration from the specified file");
+}
diff --git a/tidl_api/src/pybind_eo.cpp b/tidl_api/src/pybind_eo.cpp
new file mode 100644 (file)
index 0000000..edd58c8
--- /dev/null
@@ -0,0 +1,90 @@
+/******************************************************************************
+ * Copyright (c) 2018-2018 Texas Instruments Incorporated - http://www.ti.com/
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *      * Neither the name of Texas Instruments Incorporated nor the
+ *        names of its contributors may be used to endorse or promote products
+ *        derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include "pybind_common.h"
+
+
+PYBIND11_MAKE_OPAQUE(EO);
+
+// Class, method names follow PEP8, Style Guide for Python Code (Reference #3)
+void init_eo(module &m)
+{
+    class_<EO>(m, "ExecutionObject")
+        .def("get_input_buffer",
+              [](const EO &eo)
+              {
+                 ArgInfo ai(eo.GetInputBufferPtr(),
+                            eo.GetInputBufferSizeInBytes());
+                 return ai;
+              },
+              "Exposes the input buffer in the ExecutionObject as\n"
+              "a Python object using the buffer protocol. This object can\n"
+              "be specified as an argument to the readinto method")
+
+        .def("get_output_buffer",
+              [](const EO &eo)
+              {
+                 ArgInfo ai(eo.GetOutputBufferPtr(),
+                            eo.GetOutputBufferSizeInBytes());
+                 return ai;
+              },
+              "Exposes the output buffer in the ExecutionObject as\n"
+              "a Python object using the buffer protocol. This object can\n"
+              "be specified as an argument to the write method")
+
+        .def("set_frame_index", &EO::SetFrameIndex,
+             "Set the frame index of the frame currently processed.\n"
+             "Used for trace/debug messages")
+
+        .def("get_frame_index", &EO::GetFrameIndex)
+
+        .def("process_frame_start_async", &EO::ProcessFrameStartAsync,
+             "Start processing a frame. The call is asynchronous and\n"
+             "returns immediately")
+
+        .def("process_frame_wait", &EO::ProcessFrameWait,
+             "Wait for the executor pipeline to complete processing a frame\n"
+             "returns false if process_frame_wait() was called without a\n"
+             " corresponding call to process_frame_start_async")
+
+        .def("get_device_name", &EO::GetDeviceName)
+
+        .def("get_host_process_time_in_ms",
+             &EO::GetHostProcessTimeInMilliSeconds,
+             "Milliseconds taken on the host to process a frame")
+
+        .def("get_process_time_in_ms",
+             &EO::GetProcessTimeInMilliSeconds,
+             "Milliseconds taken on the device to process a frame")
+
+        .def("write_layer_outputs_to_file",
+             &EO::WriteLayerOutputsToFile,
+             "Write the output buffer for each layer to a file\n"
+             "<filename_prefix>_<ID>_HxW.bin",
+             arg("filename_prefix")="trace_dump_");
+}
diff --git a/tidl_api/src/pybind_eop.cpp b/tidl_api/src/pybind_eop.cpp
new file mode 100644 (file)
index 0000000..e387794
--- /dev/null
@@ -0,0 +1,97 @@
+/******************************************************************************
+ * Copyright (c) 2018-2018 Texas Instruments Incorporated - http://www.ti.com/
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *      * Neither the name of Texas Instruments Incorporated nor the
+ *        names of its contributors may be used to endorse or promote products
+ *        derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#include "pybind_common.h"
+
+// Note: Overloaded methods must be disambiguated by casting them to
+// function pointers. See
+// https://pybind11.readthedocs.io/en/stable/classes.html#overloaded-methods
+void init_eop(module &m)
+{
+    class_<EOP>(m, "ExecutionObjectPipeline")
+        .def(init<std::vector<ExecutionObject *>>())
+
+        .def("get_input_buffer",
+              [](const EOP &eo)
+              {
+                 ArgInfo ai(eo.GetInputBufferPtr(),
+                            eo.GetInputBufferSizeInBytes());
+                 return ai;
+              },
+              "Exposes the input buffer in the ExecutionObjectPipeline as\n"
+              "a Python object using the buffer protocol. This object can\n"
+              "be specified as an argument to the readinto method")
+
+        .def("get_output_buffer",
+              [](const EOP &eo)
+              {
+                 ArgInfo ai(eo.GetOutputBufferPtr(),
+                            eo.GetOutputBufferSizeInBytes());
+                 return ai;
+              },
+              "Exposes the output buffer in the ExecutionObjectPipeline as\n"
+              "a Python object using the buffer protocol. This object can\n"
+              "be specified as an argument to the write method")
+
+        .def("set_frame_index", &EOP::SetFrameIndex,
+             "Set the frame index of the frame currently processed.\n"
+             "Used for trace/debug messages")
+
+        .def("get_frame_index", &EOP::GetFrameIndex)
+
+        .def("process_frame_start_async", &EOP::ProcessFrameStartAsync,
+             "Start processing a frame. The call is asynchronous and\n"
+             "returns immediately")
+
+        .def("process_frame_wait", &EOP::ProcessFrameWait,
+             "Wait for the executor pipeline to complete processing a frame\n"
+             "returns false if process_frame_wait() was called without a\n"
+             " corresponding call to process_frame_start_async")
+
+        .def("get_device_name", &EOP::GetDeviceName)
+
+        .def("get_host_process_time_in_ms",
+             (float (EOP::*)() const) &EOP::GetHostProcessTimeInMilliSeconds,
+             "Milliseconds taken on the host to process a frame")
+
+        .def("get_process_time_in_ms",
+             (float (EOP::*)() const) &EOP::GetProcessTimeInMilliSeconds,
+             "Milliseconds taken on the device to process a frame")
+
+        .def("get_host_process_time_in_ms",
+             (float (EOP::*)(uint32_t) const)
+             &EOP::GetHostProcessTimeInMilliSeconds,
+             "Milliseconds taken on the host to process\n"
+             "a frame by the specified EO")
+
+        .def("get_process_time_in_ms",
+             (float (EOP::*)(uint32_t) const)
+             &EOP::GetProcessTimeInMilliSeconds,
+             "Milliseconds taken on the device to process\n"
+             "a frame by the specified EO");
+}
diff --git a/tidl_api/src/pybind_executor.cpp b/tidl_api/src/pybind_executor.cpp
new file mode 100644 (file)
index 0000000..1eb3559
--- /dev/null
@@ -0,0 +1,128 @@
+/******************************************************************************
+ * Copyright (c) 2018-2018 Texas Instruments Incorporated - http://www.ti.com/
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *      * Neither the name of Texas Instruments Incorporated nor the
+ *        names of its contributors may be used to endorse or promote products
+ *        derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *  THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+/*! \file pybind_executor.cpp */
+
+// Requires https://github.com/pybind/pybind11, branch v2.2
+// References:
+// 1. https://pybind11.readthedocs.io/en/stable/advanced/functions.html#return-value-policies
+// 2. https://pybind11.readthedocs.io/en/stable/advanced/pycpp/numpy.html#buffer-protocol
+// 3. https://www.python.org/dev/peps/pep-0008/
+
+#include "pybind_common.h"
+
+void AllocateMemory(const vector<ExecutionObject *>& eos);
+void AllocateMemory(const vector<ExecutionObjectPipeline *>& eos);
+void FreeMemory(const vector<ExecutionObject *>& eos);
+void FreeMemory(const vector<ExecutionObjectPipeline *>& eos);
+
+#define STRING(S)  XSTRING(S)
+#define XSTRING(S) #S
+
+PYBIND11_MAKE_OPAQUE(EO);
+
+// Class, method names follow PEP8, Style Guide for Python Code (Reference #3)
+PYBIND11_MODULE(tidl, m)
+{
+    m.doc() = std::string("TIDL API Python bindings ") + STRING(_BUILD_VER);
+    m.attr("__version__") = STRING(_BUILD_VER);
+
+    register_exception<Exception>(m, "TidlError");
+
+    enum_<DeviceType>(m, "DeviceType")
+        .value("DSP", DeviceType::DSP)
+        .value("EVE", DeviceType::EVE);
+
+    enum_<DeviceId>(m, "DeviceId")
+        .value("ID0", DeviceId::ID0)
+        .value("ID1", DeviceId::ID1)
+        .value("ID2", DeviceId::ID2)
+        .value("ID3", DeviceId::ID3);
+
+    init_configuration(m);
+    init_eo(m);
+    init_eop(m);
+
+    // For an explanation of return_value_policy, see Reference #1
+       class_<Executor>(m, "Executor")
+        .def(init<DeviceType, std::set<DeviceId>, Configuration, int>())
+
+        .def("get_num_execution_objects", &Executor::GetNumExecutionObjects,
+             "Returns number of ExecutionObjects created by this Executor")
+
+        .def_static("get_num_devices", &Executor::GetNumDevices,
+             "Returns number of devices of the specified type\n"
+             "available for TI DL offload")
+
+        .def_static("get_api_version", &Executor::GetAPIVersion)
+
+        .def("at", &Executor::operator[], return_value_policy::reference,
+            "Returns the ExecutionObject at the specified index");
+
+    // Used to expose the EO's internal input and output buffers to the
+    // python application using the Python buffer protocol (Reference #2)
+    class_<ArgInfo>(m, "ArgInfo", buffer_protocol())
+        .def_buffer([](ArgInfo& ai) -> buffer_info
+                {
+                    return buffer_info(
+                            ai.ptr(),
+                            sizeof(char),
+                            pybind11::format_descriptor<char>::format(),
+                            1,
+                            {ai.size()},
+                            {1}
+                           );
+                })
+        .def("size", &ArgInfo::size, "Size of the buffer in bytes")
+        .def("__repr__",
+             [](const ArgInfo& ai)
+             {
+                std::stringstream ss;
+                ss << "<ArgInfo: ptr= " << ai.ptr() << " size= " << ai.size()
+                   << ">";
+                return ss.str();
+             });
+
+    // Helper functions to allocate memory from Python
+    m.def("allocate_memory",
+          (void (*)(const vector<EO *>&)) &AllocateMemory,
+          "Allocate input and output buffers for all ExecutionObjects");
+
+    m.def("free_memory",
+           (void (*)(const vector<EO *>&)) &FreeMemory,
+          "Free input and output buffers of all ExecutionObjects");
+
+    m.def("allocate_memory",
+          (void (*)(const vector<EOP *>&)) &AllocateMemory,
+          "Allocate input and output buffers for all ExecutionObjectPipelines");
+
+    m.def("free_memory",
+           (void (*)(const vector<EOP *>&)) &FreeMemory,
+          "Free input and output buffers of all ExecutionObjectPipelines");
+}
+
diff --git a/tidl_api/src/pybind_helpers.cpp b/tidl_api/src/pybind_helpers.cpp
new file mode 100644 (file)
index 0000000..2d8f28a
--- /dev/null
@@ -0,0 +1,57 @@
+#include "pybind_common.h"
+
+template<typename T>
+void AllocateMemoryT(const vector<T *>& eos)
+{
+    // Allocate input and output buffers for each execution object
+    for (auto eo : eos)
+    {
+        size_t in_size  = eo->GetInputBufferSizeInBytes();
+        size_t out_size = eo->GetOutputBufferSizeInBytes();
+        void*  in_ptr   = malloc(in_size);
+        void*  out_ptr  = malloc(out_size);
+        assert(in_ptr != nullptr && out_ptr != nullptr);
+
+        ArgInfo in  = { ArgInfo(in_ptr,  in_size)};
+        ArgInfo out = { ArgInfo(out_ptr, out_size)};
+        eo->SetInputOutputBuffer(in, out);
+    }
+}
+
+// Allocate input and output memory for each EO
+void AllocateMemory(const vector<ExecutionObject *>& eos)
+{
+    AllocateMemoryT<ExecutionObject>(eos);
+}
+
+// Allocate input and output memory for each EO
+void AllocateMemory(const vector<ExecutionObjectPipeline *>& eos)
+{
+    AllocateMemoryT<ExecutionObjectPipeline>(eos);
+}
+
+
+
+// Free the input and output memory associated with each EO
+template<typename T>
+void FreeMemoryT(const vector<T *>& eos)
+{
+    for (auto eo : eos)
+    {
+        free(eo->GetInputBufferPtr());
+        free(eo->GetOutputBufferPtr());
+    }
+}
+
+void FreeMemory(const vector<ExecutionObject *>& eos)
+{
+    FreeMemoryT<ExecutionObject>(eos);
+}
+
+
+// Free the input and output memory associated with each EO
+void FreeMemory(const vector<ExecutionObjectPipeline *>& eos)
+{
+    FreeMemoryT<ExecutionObjectPipeline>(eos);
+}
+