From 66ea68b0961d6da730bfff6d21e771ed2ae781ff Mon Sep 17 00:00:00 2001 From: Christophe Favergeon Date: Thu, 19 Jan 2023 09:59:32 +0100 Subject: [PATCH] Added a dynamic mode to the compute graph. The compute graph hierarchy (Python and C++) has been modified. It will break the code using the CG and the include and import will have to be modified. New function prepareForRunning is needed in CG nodes even if not used in static mode. Without this function, code using compute graph won't build. --- ARM.CMSIS-DSP.pdsc | 10 +- ComputeGraph/README.md | 84 ++++- ComputeGraph/cg/{static => }/nodes/README.md | 0 ComputeGraph/cg/{static => }/nodes/cpp/CFFT.h | 36 +++ .../cg/{static => }/nodes/cpp/ICFFT.h | 36 +++ .../nodes/cpp/InterleavedStereoToMono.h | 39 ++- ComputeGraph/cg/{static => }/nodes/cpp/MFCC.h | 48 +++ .../cg/{static => }/nodes/cpp/NullSink.h | 11 + .../cg/{static => }/nodes/cpp/OverlapAndAdd.h | 12 + .../cg/{static => }/nodes/cpp/SlidingBuffer.h | 12 + .../StreamingNodes/CMSIS_RTOS/RingPrivate.h | 0 .../StreamingNodes/CMSIS_RTOS/SchedEvents.h | 0 .../cpp/StreamingNodes/Config/AudioConfig.h | 0 .../cpp/StreamingNodes/Config/RingConfig.h | 0 .../cpp/StreamingNodes/Config/VideoConfig.h | 0 .../nodes/cpp/StreamingNodes/README.md | 2 + .../StreamingNodes/RingBuffer/RingBuffer.cpp | 0 .../StreamingNodes/RingBuffer/RingBuffer.h | 0 .../StreamingNodes/RingBuffer/RingInit.cpp | 0 .../cpp/StreamingNodes/RingBuffer/RingInit.h | 0 .../VHT/audio/AudioInterrupt.cpp | 0 .../cpp/StreamingNodes/VHT/audio/audio_drv.c | 0 .../cpp/StreamingNodes/VHT/audio/audio_drv.h | 0 .../VHT/video/VideoInterrupt.cpp | 0 .../cpp/StreamingNodes/VHT/video/video_drv.c | 0 .../cpp/StreamingNodes/VHT/video/video_drv.h | 0 .../cg/{static => }/nodes/cpp/StreamingSink.h | 13 +- .../{static => }/nodes/cpp/StreamingSource.h | 13 +- .../cg/{static => }/nodes/cpp/ToComplex.h | 12 + .../cg/{static => }/nodes/cpp/ToReal.h | 12 + .../cg/{static => }/nodes/cpp/Unzip.h | 13 + ComputeGraph/cg/{static => }/nodes/cpp/Zip.h | 12 + .../cg/{static => }/nodes/cpp/host/FileSink.h | 11 + .../{static => }/nodes/cpp/host/FileSource.h | 11 + .../cg/{static => }/src/GenericNodes.h | 218 ++++++++++++- ComputeGraph/cg/src/cg_status.h | 17 + .../documentation/supported_configs.png | Bin 0 -> 6781 bytes ComputeGraph/examples/CMakeLists.txt | 34 +- ComputeGraph/examples/example1/AppNodes.h | 34 ++ .../examples/example1/generated/scheduler.cpp | 6 +- ComputeGraph/examples/example1/graph.py | 2 +- ComputeGraph/examples/example10/AppNodes.h | 195 ++++++++++++ .../examples/example10/CMakeLists.txt | 13 + ComputeGraph/examples/example10/custom.h | 9 + .../example10/generated/scheduler.cpp | 297 ++++++++++++++++++ .../examples/example10/generated/scheduler.h | 26 ++ ComputeGraph/examples/example10/graph.py | 132 ++++++++ ComputeGraph/examples/example10/main.cpp | 11 + ComputeGraph/examples/example10/test.dot | 104 ++++++ ComputeGraph/examples/example10/test.pdf | Bin 0 -> 25246 bytes ComputeGraph/examples/example2/AppNodes.h | 34 ++ .../examples/example2/generated/scheduler.cpp | 78 ++--- ComputeGraph/examples/example2/graph.py | 2 +- .../examples/example3/generated/scheduler.cpp | 114 ++++++- ComputeGraph/examples/example3/graph.py | 3 +- ComputeGraph/examples/example4/CMakeLists.txt | 10 + ComputeGraph/examples/example4/appnodes.py | 14 +- ComputeGraph/examples/example4/graph.py | 2 +- ComputeGraph/examples/example4/sched.py | 2 +- ComputeGraph/examples/example5/CMakeLists.txt | 10 + ComputeGraph/examples/example5/appnodes.py | 10 +- ComputeGraph/examples/example5/graph.py | 2 +- ComputeGraph/examples/example5/sched.py | 2 +- ComputeGraph/examples/example5/test.pdf | Bin 29648 -> 29895 bytes .../examples/example6/generated/scheduler.cpp | 10 +- ComputeGraph/examples/example6/graph.py | 2 +- ComputeGraph/examples/example7/CMakeLists.txt | 10 + ComputeGraph/examples/example7/appnodes.py | 16 +- ComputeGraph/examples/example7/custom.py | 2 +- ComputeGraph/examples/example7/graph.py | 2 +- ComputeGraph/examples/example7/sched.py | 2 +- ComputeGraph/examples/example8/AppNodes.h | 35 +++ .../examples/example8/generated/scheduler.cpp | 14 +- ComputeGraph/examples/example8/graph.py | 2 +- ComputeGraph/examples/example9/AppNodes.h | 35 +++ .../examples/example9/generated/scheduler.cpp | 10 +- ComputeGraph/examples/example9/graph.py | 2 +- MANIFEST.in | 2 +- cmsisdsp/__init__.py | 2 +- cmsisdsp/cg/{static => }/nodes/CFFT.py | 2 +- cmsisdsp/cg/{static => }/nodes/Duplicate.py | 2 +- cmsisdsp/cg/{static => }/nodes/ICFFT.py | 2 +- .../nodes/InterleavedStereoToMono.py | 2 +- cmsisdsp/cg/{static => }/nodes/MFCC.py | 2 +- cmsisdsp/cg/{static => }/nodes/NullSink.py | 2 +- cmsisdsp/cg/{static => }/nodes/ToComplex.py | 2 +- cmsisdsp/cg/{static => }/nodes/ToReal.py | 2 +- cmsisdsp/cg/{static => }/nodes/Unzip.py | 2 +- cmsisdsp/cg/{static => }/nodes/Zip.py | 2 +- cmsisdsp/cg/{static => }/nodes/__init__.py | 4 +- .../cg/{static => }/nodes/host/FileSink.py | 2 +- .../cg/{static => }/nodes/host/FileSource.py | 2 +- .../cg/{static => }/nodes/host/NumpySink.py | 2 +- cmsisdsp/cg/{static => }/nodes/host/VHT.py | 2 +- .../cg/{static => }/nodes/host/VHTCGSTATIC.py | 0 .../cg/{static => }/nodes/host/WavSink.py | 2 +- .../cg/{static => }/nodes/host/WavSource.py | 0 .../cg/{static => }/nodes/host/__init__.py | 0 .../cg/{static => }/nodes/host/message.py | 2 +- cmsisdsp/cg/{static => }/nodes/simu.py | 2 +- .../cg/{static => }/scheduler/__init__.py | 0 cmsisdsp/cg/{static => }/scheduler/ccode.py | 10 +- cmsisdsp/cg/{static => }/scheduler/config.py | 20 ++ .../cg/{static => }/scheduler/description.py | 63 ++-- .../cg/{static => }/scheduler/graphviz.py | 2 +- cmsisdsp/cg/{static => }/scheduler/node.py | 93 ++++-- .../cg/{static => }/scheduler/pythoncode.py | 2 +- .../cg/{static => }/scheduler/standard.py | 12 +- cmsisdsp/cg/scheduler/templates/cmsis.cpp | 15 + .../{static => }/scheduler/templates/cmsis.py | 0 .../cg/scheduler/templates/cmsisCheck.cpp | 21 ++ .../scheduler/templates/cmsisNode.cpp | 0 .../{static => }/scheduler/templates/code.cpp | 0 .../{static => }/scheduler/templates/code.h | 0 .../{static => }/scheduler/templates/code.py | 2 +- .../scheduler/templates/codeArray.cpp | 0 .../scheduler/templates/codeSwitch.cpp | 46 +++ .../scheduler/templates/commonc.cpp | 8 +- .../scheduler/templates/defineConfig.h | 0 .../scheduler/templates/dot_template.dot | 0 .../cg/static/scheduler/templates/cmsis.cpp | 13 - cmsisdsp/cg/{static => }/types.py | 0 setup.py | 10 +- 123 files changed, 2045 insertions(+), 224 deletions(-) rename ComputeGraph/cg/{static => }/nodes/README.md (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/CFFT.h (81%) rename ComputeGraph/cg/{static => }/nodes/cpp/ICFFT.h (81%) rename ComputeGraph/cg/{static => }/nodes/cpp/InterleavedStereoToMono.h (79%) rename ComputeGraph/cg/{static => }/nodes/cpp/MFCC.h (81%) rename ComputeGraph/cg/{static => }/nodes/cpp/NullSink.h (87%) rename ComputeGraph/cg/{static => }/nodes/cpp/OverlapAndAdd.h (91%) rename ComputeGraph/cg/{static => }/nodes/cpp/SlidingBuffer.h (88%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/CMSIS_RTOS/RingPrivate.h (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/CMSIS_RTOS/SchedEvents.h (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/Config/AudioConfig.h (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/Config/RingConfig.h (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/Config/VideoConfig.h (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/README.md (94%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/RingBuffer/RingBuffer.cpp (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/RingBuffer/RingBuffer.h (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/RingBuffer/RingInit.cpp (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/RingBuffer/RingInit.h (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/VHT/audio/AudioInterrupt.cpp (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/VHT/audio/audio_drv.c (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/VHT/audio/audio_drv.h (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/VHT/video/VideoInterrupt.cpp (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/VHT/video/video_drv.c (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingNodes/VHT/video/video_drv.h (100%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingSink.h (88%) rename ComputeGraph/cg/{static => }/nodes/cpp/StreamingSource.h (89%) rename ComputeGraph/cg/{static => }/nodes/cpp/ToComplex.h (88%) rename ComputeGraph/cg/{static => }/nodes/cpp/ToReal.h (87%) rename ComputeGraph/cg/{static => }/nodes/cpp/Unzip.h (87%) rename ComputeGraph/cg/{static => }/nodes/cpp/Zip.h (86%) rename ComputeGraph/cg/{static => }/nodes/cpp/host/FileSink.h (87%) rename ComputeGraph/cg/{static => }/nodes/cpp/host/FileSource.h (91%) rename ComputeGraph/cg/{static => }/src/GenericNodes.h (56%) create mode 100644 ComputeGraph/cg/src/cg_status.h create mode 100644 ComputeGraph/documentation/supported_configs.png create mode 100644 ComputeGraph/examples/example10/AppNodes.h create mode 100644 ComputeGraph/examples/example10/CMakeLists.txt create mode 100644 ComputeGraph/examples/example10/custom.h create mode 100644 ComputeGraph/examples/example10/generated/scheduler.cpp create mode 100644 ComputeGraph/examples/example10/generated/scheduler.h create mode 100644 ComputeGraph/examples/example10/graph.py create mode 100644 ComputeGraph/examples/example10/main.cpp create mode 100644 ComputeGraph/examples/example10/test.dot create mode 100644 ComputeGraph/examples/example10/test.pdf create mode 100644 ComputeGraph/examples/example4/CMakeLists.txt create mode 100644 ComputeGraph/examples/example5/CMakeLists.txt create mode 100644 ComputeGraph/examples/example7/CMakeLists.txt rename cmsisdsp/cg/{static => }/nodes/CFFT.py (95%) rename cmsisdsp/cg/{static => }/nodes/Duplicate.py (95%) rename cmsisdsp/cg/{static => }/nodes/ICFFT.py (95%) rename cmsisdsp/cg/{static => }/nodes/InterleavedStereoToMono.py (95%) rename cmsisdsp/cg/{static => }/nodes/MFCC.py (95%) rename cmsisdsp/cg/{static => }/nodes/NullSink.py (94%) rename cmsisdsp/cg/{static => }/nodes/ToComplex.py (94%) rename cmsisdsp/cg/{static => }/nodes/ToReal.py (94%) rename cmsisdsp/cg/{static => }/nodes/Unzip.py (94%) rename cmsisdsp/cg/{static => }/nodes/Zip.py (95%) rename cmsisdsp/cg/{static => }/nodes/__init__.py (90%) rename cmsisdsp/cg/{static => }/nodes/host/FileSink.py (95%) rename cmsisdsp/cg/{static => }/nodes/host/FileSource.py (94%) rename cmsisdsp/cg/{static => }/nodes/host/NumpySink.py (95%) rename cmsisdsp/cg/{static => }/nodes/host/VHT.py (98%) rename cmsisdsp/cg/{static => }/nodes/host/VHTCGSTATIC.py (100%) rename cmsisdsp/cg/{static => }/nodes/host/WavSink.py (95%) rename cmsisdsp/cg/{static => }/nodes/host/WavSource.py (100%) rename cmsisdsp/cg/{static => }/nodes/host/__init__.py (100%) rename cmsisdsp/cg/{static => }/nodes/host/message.py (98%) rename cmsisdsp/cg/{static => }/nodes/simu.py (99%) rename cmsisdsp/cg/{static => }/scheduler/__init__.py (100%) rename cmsisdsp/cg/{static => }/scheduler/ccode.py (89%) rename cmsisdsp/cg/{static => }/scheduler/config.py (85%) rename cmsisdsp/cg/{static => }/scheduler/description.py (95%) rename cmsisdsp/cg/{static => }/scheduler/graphviz.py (95%) rename cmsisdsp/cg/{static => }/scheduler/node.py (92%) rename cmsisdsp/cg/{static => }/scheduler/pythoncode.py (95%) rename cmsisdsp/cg/{static => }/scheduler/standard.py (95%) create mode 100644 cmsisdsp/cg/scheduler/templates/cmsis.cpp rename cmsisdsp/cg/{static => }/scheduler/templates/cmsis.py (100%) create mode 100644 cmsisdsp/cg/scheduler/templates/cmsisCheck.cpp rename cmsisdsp/cg/{static => }/scheduler/templates/cmsisNode.cpp (100%) rename cmsisdsp/cg/{static => }/scheduler/templates/code.cpp (100%) rename cmsisdsp/cg/{static => }/scheduler/templates/code.h (100%) rename cmsisdsp/cg/{static => }/scheduler/templates/code.py (96%) rename cmsisdsp/cg/{static => }/scheduler/templates/codeArray.cpp (100%) rename cmsisdsp/cg/{static => }/scheduler/templates/codeSwitch.cpp (52%) rename cmsisdsp/cg/{static => }/scheduler/templates/commonc.cpp (85%) rename cmsisdsp/cg/{static => }/scheduler/templates/defineConfig.h (100%) rename cmsisdsp/cg/{static => }/scheduler/templates/dot_template.dot (100%) delete mode 100644 cmsisdsp/cg/static/scheduler/templates/cmsis.cpp rename cmsisdsp/cg/{static => }/types.py (100%) diff --git a/ARM.CMSIS-DSP.pdsc b/ARM.CMSIS-DSP.pdsc index 18afa622..6864687d 100644 --- a/ARM.CMSIS-DSP.pdsc +++ b/ARM.CMSIS-DSP.pdsc @@ -105,12 +105,12 @@ - - CMSIS-DSP Compute Graph - Static Flow + + CMSIS-DSP Compute Graph - - - + + + diff --git a/ComputeGraph/README.md b/ComputeGraph/README.md index 7d802a95..da38aefb 100644 --- a/ComputeGraph/README.md +++ b/ComputeGraph/README.md @@ -49,7 +49,7 @@ The CMSIS-DSP Compute Graph Tools are a set of Python scripts and C++ classes wi - The Python script will generate a C++ implementation of the static schedule - The Python script can also generate a Python implementation of the static schedule (for use with the CMSIS-DSP Python wrapper) - +(There is no FIFO underflow or overflow due to the scheduling. If there are not enough cycles to run the processing, the real-time will be broken and the solution won't work But this problem is independent from the scheduling itself. ) ## Why it is useful @@ -93,6 +93,10 @@ The schedule is (the size of the FIFOs after the execution of the node displayed At the end, both FIFOs are empty so the schedule can be run again : it is periodic ! +The latest version of the compute graph also supports dynamic scheduling. + +![supported_configs](documentation/supported_configs.png) + ## How to use the static scheduler generator First, you must install the `CMSIS-DSP` PythonWrapper: @@ -130,6 +134,8 @@ Example 7 is communicating with OpenModelica. The Modelica model (PythonTest) in Example 8 is showing how to define a new custom datatype for the IOs of the nodes. Example 8 is also demonstrating a new feature where an IO can be connected up to 3 inputs and the static scheduler will automatically generate duplicate nodes. + + ## Frequently asked questions: There is a [FAQ](FAQ.md) document. @@ -181,10 +187,60 @@ The drawback of cyclo static scheduling is that the schedule length is increased Since schedule tend to be bigger with cyclo static scheduling, a new code generation mode has been introduced and is enabled by default : now instead of having a sequence of function calls, the schedule is coded by an array of number and there is a switch / case to select the function to be called. +## Dynamic Data Flow + +Versions of the compute graph corresponding to CMSIS-DSP Version >= 1.14.4 and Python wrapper version >= 1.10.0 are supporting a new dynamic / asynchronous mode. + + With a dynamic flow, the flow of data is potentially changing at each execution. The IOs can generate or consume a different amount of data at each execution of their node (including no data). + +This can be useful for sample oriented use cases where not all samples are available but a processing must nevertheless take place each time a subset of samples is available (samples could come from sensors). + +With a dynamic flow and scheduling, there is no more any way to ensure that there won't be FIFO underflow of overflow due to scheduling. As consequence, the nodes must be able to check for this problem and decide what to do. + +* A sink may decide to generate fake data in case of FIFO underflow +* A source may decide to skip some data in case of FIFO overflow +* Another node may decide to do nothing and skip the execution +* Another node may decide to raise an error. + +With dynamic scheduling, a node must implement the function `prepareForRunning` and decide what to do. + +3 error / status codes are reserved for this. They are defined in the header `cg_status.h`. This header is not included by default, but if you define you own error codes, they should be coherent with `cg_status` and use the same values for the 3 status / error codes which are used in dynamic mode: + +* `CG_SUCCESS` = 0 : Node can execute +* `CG_SKIP_EXECUTION` = -5 : Node will skip the execution +* `CG_BUFFER_ERROR` = -6 : Unrecoverable error due to FIFO underflow / overflow (only raised in pure function like CMSIS-DSP ones called directly) + +Any other returned value will stop the execution. + +The dynamic mode (also named asynchronous), is enabled with option : `asynchronous` + +The system will still compute a scheduling and FIFO sizes as if the flow was static. We can see the static flow as an average of the dynamic flow. In dynamic mode, the FIFOs may need to be bigger than the ones computed in static mode. The static estimation is giving a first idea of what the size of the FIFOs should be. The size can be increased by specifying a percent increase with option `FIFOIncrease`. + +For pure compute functions (like CMSIS-DSP ones), which are not packaged into a C++ class, there is no way to customize the decision logic in case of a problem with FIFO. There is a global option : `asyncDefaultSkip`. + +When `true`, a pure function that cannot run will just skip the execution. With `false`, the execution will stop. For any other decision algorithm, the pure function needs to be packaged in a C++ class. + +`Duplicate` nodes are skipping the execution in case of problems with FIFOs. If it is not the wanted behavior, you can either: + +* Replace the Duplicate class by a custom one by changing the class name with option `duplicateNodeClassName` on the graph. +* Don't use the automatic duplication feature and introduce your duplicate nodes in the compute graph + +When you don't want to generate or consume data in a node, just don't call the functions `getReadBuffer` or `getWriteBuffer` for your IOs. + ## Options Several options and be used to control the schedule generation. Some options are used by the scheduling algorithm and other options are used by the code generator: +### Options for the graph + +#### defaultFIFOClass (default = "FIFO") + +Class used for FIFO by default. Can also be customized for each connection (`connect` of `connectWithDelay` call) + +#### duplicateNodeClassName(default="Duplicate") + +Prefix used to generate the duplicate node classes like `Duplicate2`, `Duplicate3` ... + ### Options for the scheduling #### memoryOptimization (default = False) @@ -235,6 +291,8 @@ Prefix to add before the FIFO buffer definition. Those buffers are not static an Another possibility would be to make the buffer static by redefining the macro `CG_BEFORE_BUFFER` + + #### Options for C Code Generation only ##### cOptionalArgs (default = "") @@ -289,6 +347,30 @@ By default, the scheduler function is callable from C. When false, it is a stand If you don't use any of the datatypes or functions of the CMSIS-DSP, you don't need to include the `arm_math.h` in the scheduler file. This option can thus be set to `False`. +##### asynchronous (default = False) + +When true, the scheduling is for a dynamic / asynchronous flow. A node may not always produce or consume the same amount of data. As consequence, a scheduling can fail. Each node needs to implement a `prepareForRunning` function to identify and recover from FIFO underflows and overflows. + +A synchronous schedule is used as start and should describe the average case. + +This implies `codeArray` and `switchCase`. This disables `memoryOptimizations`. + +Synchronous FIFOs that are just buffers will be considered as FIFOs in asynchronous mode. + +##### FIFOIncrease (default 0) + +In case of asynchronous scheduling, the FIFOs may need to be bigger than what is computed assuming a synchronous scheduling. This option is used to increase the FIFO size. It represents a percent increase. + +For instance, a value of 10 means the FIFO will have their size updated from `oldSize` to `1.1 * oldSize` which is ` (1 + 10%)* oldSize` + +##### asyncDefaultSkip (default True) + +Behavior of a pure function (like CMSIS-DSP) in asynchronous mode. When `True`, the execution is skipped if the function can't be executed. If `False`, an error is raised. + +If another error recovery is needed, the function must be packaged into a C++ class to implement a `prepareForRun` function. + + + #### Options for Python code generation only ##### pyOptionalArgs (default = "") diff --git a/ComputeGraph/cg/static/nodes/README.md b/ComputeGraph/cg/nodes/README.md similarity index 100% rename from ComputeGraph/cg/static/nodes/README.md rename to ComputeGraph/cg/nodes/README.md diff --git a/ComputeGraph/cg/static/nodes/cpp/CFFT.h b/ComputeGraph/cg/nodes/cpp/CFFT.h similarity index 81% rename from ComputeGraph/cg/static/nodes/cpp/CFFT.h rename to ComputeGraph/cg/nodes/cpp/CFFT.h index 59ad9737..2359b04a 100644 --- a/ComputeGraph/cg/static/nodes/cpp/CFFT.h +++ b/ComputeGraph/cg/nodes/cpp/CFFT.h @@ -45,6 +45,18 @@ public: status=arm_cfft_init_f32(&sfft,inputSize>>1); }; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ float32_t *a=this->getReadBuffer(); float32_t *b=this->getWriteBuffer(); @@ -72,6 +84,18 @@ public: status=arm_cfft_init_f16(&sfft,inputSize>>1); }; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ float16_t *a=this->getReadBuffer(); float16_t *b=this->getWriteBuffer(); @@ -98,6 +122,18 @@ public: status=arm_cfft_init_q15(&sfft,inputSize>>1); }; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ q15_t *a=this->getReadBuffer(); q15_t *b=this->getWriteBuffer(); diff --git a/ComputeGraph/cg/static/nodes/cpp/ICFFT.h b/ComputeGraph/cg/nodes/cpp/ICFFT.h similarity index 81% rename from ComputeGraph/cg/static/nodes/cpp/ICFFT.h rename to ComputeGraph/cg/nodes/cpp/ICFFT.h index dfcdee84..01f1f2ba 100644 --- a/ComputeGraph/cg/static/nodes/cpp/ICFFT.h +++ b/ComputeGraph/cg/nodes/cpp/ICFFT.h @@ -45,6 +45,18 @@ public: status=arm_cfft_init_f32(&sifft,inputSize>>1); }; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ float32_t *a=this->getReadBuffer(); float32_t *b=this->getWriteBuffer(); @@ -72,6 +84,18 @@ public: status=arm_cfft_init_f16(&sifft,inputSize>>1); }; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ float16_t *a=this->getReadBuffer(); float16_t *b=this->getWriteBuffer(); @@ -99,6 +123,18 @@ public: status=arm_cfft_init_q15(&sifft,inputSize>>1); }; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ q15_t *a=this->getReadBuffer(); q15_t *b=this->getWriteBuffer(); diff --git a/ComputeGraph/cg/static/nodes/cpp/InterleavedStereoToMono.h b/ComputeGraph/cg/nodes/cpp/InterleavedStereoToMono.h similarity index 79% rename from ComputeGraph/cg/static/nodes/cpp/InterleavedStereoToMono.h rename to ComputeGraph/cg/nodes/cpp/InterleavedStereoToMono.h index 9d3a4726..6bec929a 100644 --- a/ComputeGraph/cg/static/nodes/cpp/InterleavedStereoToMono.h +++ b/ComputeGraph/cg/nodes/cpp/InterleavedStereoToMono.h @@ -40,7 +40,18 @@ public: InterleavedStereoToMono(FIFOBase &src,FIFOBase &dst): GenericNode(src,dst){}; - + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ q15_t *a=this->getReadBuffer(); q15_t *b=this->getWriteBuffer(); @@ -60,7 +71,18 @@ public: InterleavedStereoToMono(FIFOBase &src,FIFOBase &dst): GenericNode(src,dst){}; - + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ q31_t *a=this->getReadBuffer(); q31_t *b=this->getWriteBuffer(); @@ -80,7 +102,18 @@ public: InterleavedStereoToMono(FIFOBase &src,FIFOBase &dst): GenericNode(src,dst){}; - + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ float32_t *a=this->getReadBuffer(); float32_t *b=this->getWriteBuffer(); diff --git a/ComputeGraph/cg/static/nodes/cpp/MFCC.h b/ComputeGraph/cg/nodes/cpp/MFCC.h similarity index 81% rename from ComputeGraph/cg/static/nodes/cpp/MFCC.h rename to ComputeGraph/cg/nodes/cpp/MFCC.h index 15ee1229..21c37d08 100644 --- a/ComputeGraph/cg/static/nodes/cpp/MFCC.h +++ b/ComputeGraph/cg/nodes/cpp/MFCC.h @@ -58,6 +58,18 @@ public: #endif }; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ float32_t *a=this->getReadBuffer(); float32_t *b=this->getWriteBuffer(); @@ -88,6 +100,18 @@ public: #endif }; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ float16_t *a=this->getReadBuffer(); float16_t *b=this->getWriteBuffer(); @@ -114,6 +138,18 @@ public: memory.resize(2*inputSize); }; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ q31_t *a=this->getReadBuffer(); q31_t *b=this->getWriteBuffer(); @@ -139,6 +175,18 @@ public: memory.resize(2*inputSize); }; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ q15_t *a=this->getReadBuffer(); q15_t *b=this->getWriteBuffer(); diff --git a/ComputeGraph/cg/static/nodes/cpp/NullSink.h b/ComputeGraph/cg/nodes/cpp/NullSink.h similarity index 87% rename from ComputeGraph/cg/static/nodes/cpp/NullSink.h rename to ComputeGraph/cg/nodes/cpp/NullSink.h index 5188c24e..57d9592c 100644 --- a/ComputeGraph/cg/static/nodes/cpp/NullSink.h +++ b/ComputeGraph/cg/nodes/cpp/NullSink.h @@ -35,6 +35,17 @@ class NullSink: public GenericSink public: NullSink(FIFOBase &src):GenericSink(src){}; + int prepareForRunning() override + { + if (this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run() { IN *b=this->getReadBuffer(); diff --git a/ComputeGraph/cg/static/nodes/cpp/OverlapAndAdd.h b/ComputeGraph/cg/nodes/cpp/OverlapAndAdd.h similarity index 91% rename from ComputeGraph/cg/static/nodes/cpp/OverlapAndAdd.h rename to ComputeGraph/cg/nodes/cpp/OverlapAndAdd.h index e59a521d..1c20769a 100644 --- a/ComputeGraph/cg/static/nodes/cpp/OverlapAndAdd.h +++ b/ComputeGraph/cg/nodes/cpp/OverlapAndAdd.h @@ -40,6 +40,18 @@ public: memory.resize(overlap); }; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ int i; IN *a=this->getReadBuffer(); diff --git a/ComputeGraph/cg/static/nodes/cpp/SlidingBuffer.h b/ComputeGraph/cg/nodes/cpp/SlidingBuffer.h similarity index 88% rename from ComputeGraph/cg/static/nodes/cpp/SlidingBuffer.h rename to ComputeGraph/cg/nodes/cpp/SlidingBuffer.h index a9c1c338..ff1d833c 100644 --- a/ComputeGraph/cg/static/nodes/cpp/SlidingBuffer.h +++ b/ComputeGraph/cg/nodes/cpp/SlidingBuffer.h @@ -40,6 +40,18 @@ public: memory.resize(overlap); }; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ IN *a=this->getReadBuffer(); IN *b=this->getWriteBuffer(); diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/CMSIS_RTOS/RingPrivate.h b/ComputeGraph/cg/nodes/cpp/StreamingNodes/CMSIS_RTOS/RingPrivate.h similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/CMSIS_RTOS/RingPrivate.h rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/CMSIS_RTOS/RingPrivate.h diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/CMSIS_RTOS/SchedEvents.h b/ComputeGraph/cg/nodes/cpp/StreamingNodes/CMSIS_RTOS/SchedEvents.h similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/CMSIS_RTOS/SchedEvents.h rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/CMSIS_RTOS/SchedEvents.h diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/Config/AudioConfig.h b/ComputeGraph/cg/nodes/cpp/StreamingNodes/Config/AudioConfig.h similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/Config/AudioConfig.h rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/Config/AudioConfig.h diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/Config/RingConfig.h b/ComputeGraph/cg/nodes/cpp/StreamingNodes/Config/RingConfig.h similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/Config/RingConfig.h rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/Config/RingConfig.h diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/Config/VideoConfig.h b/ComputeGraph/cg/nodes/cpp/StreamingNodes/Config/VideoConfig.h similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/Config/VideoConfig.h rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/Config/VideoConfig.h diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/README.md b/ComputeGraph/cg/nodes/cpp/StreamingNodes/README.md similarity index 94% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/README.md rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/README.md index 866c6aa8..e894ca39 100644 --- a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/README.md +++ b/ComputeGraph/cg/nodes/cpp/StreamingNodes/README.md @@ -10,6 +10,8 @@ Those files are kept because they are used in the AVH-SystemModeling example. But there are simpler way to interface the compute graph to an audio interrupt. +Those nodes are considered as deprecated. Don't use them. + ## RingBuffer It is a way to connect the compute graph with static flow to an audio source or sink. diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/RingBuffer/RingBuffer.cpp b/ComputeGraph/cg/nodes/cpp/StreamingNodes/RingBuffer/RingBuffer.cpp similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/RingBuffer/RingBuffer.cpp rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/RingBuffer/RingBuffer.cpp diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/RingBuffer/RingBuffer.h b/ComputeGraph/cg/nodes/cpp/StreamingNodes/RingBuffer/RingBuffer.h similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/RingBuffer/RingBuffer.h rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/RingBuffer/RingBuffer.h diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/RingBuffer/RingInit.cpp b/ComputeGraph/cg/nodes/cpp/StreamingNodes/RingBuffer/RingInit.cpp similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/RingBuffer/RingInit.cpp rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/RingBuffer/RingInit.cpp diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/RingBuffer/RingInit.h b/ComputeGraph/cg/nodes/cpp/StreamingNodes/RingBuffer/RingInit.h similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/RingBuffer/RingInit.h rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/RingBuffer/RingInit.h diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/VHT/audio/AudioInterrupt.cpp b/ComputeGraph/cg/nodes/cpp/StreamingNodes/VHT/audio/AudioInterrupt.cpp similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/VHT/audio/AudioInterrupt.cpp rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/VHT/audio/AudioInterrupt.cpp diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/VHT/audio/audio_drv.c b/ComputeGraph/cg/nodes/cpp/StreamingNodes/VHT/audio/audio_drv.c similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/VHT/audio/audio_drv.c rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/VHT/audio/audio_drv.c diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/VHT/audio/audio_drv.h b/ComputeGraph/cg/nodes/cpp/StreamingNodes/VHT/audio/audio_drv.h similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/VHT/audio/audio_drv.h rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/VHT/audio/audio_drv.h diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/VHT/video/VideoInterrupt.cpp b/ComputeGraph/cg/nodes/cpp/StreamingNodes/VHT/video/VideoInterrupt.cpp similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/VHT/video/VideoInterrupt.cpp rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/VHT/video/VideoInterrupt.cpp diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/VHT/video/video_drv.c b/ComputeGraph/cg/nodes/cpp/StreamingNodes/VHT/video/video_drv.c similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/VHT/video/video_drv.c rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/VHT/video/video_drv.c diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingNodes/VHT/video/video_drv.h b/ComputeGraph/cg/nodes/cpp/StreamingNodes/VHT/video/video_drv.h similarity index 100% rename from ComputeGraph/cg/static/nodes/cpp/StreamingNodes/VHT/video/video_drv.h rename to ComputeGraph/cg/nodes/cpp/StreamingNodes/VHT/video/video_drv.h diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingSink.h b/ComputeGraph/cg/nodes/cpp/StreamingSink.h similarity index 88% rename from ComputeGraph/cg/static/nodes/cpp/StreamingSink.h rename to ComputeGraph/cg/nodes/cpp/StreamingSink.h index 7dca9701..3765fbcb 100644 --- a/ComputeGraph/cg/static/nodes/cpp/StreamingSink.h +++ b/ComputeGraph/cg/nodes/cpp/StreamingSink.h @@ -30,7 +30,7 @@ #include "RingBuffer.h" - +/* This is deprecated. Don't use it */ template class StreamingSink: public GenericSink { @@ -38,6 +38,17 @@ public: StreamingSink(FIFOBase &src,ring_config_t *config): GenericSink(src),mConfig(config){}; + int prepareForRunning() override + { + if (this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run() { IN *b=this->getReadBuffer(); diff --git a/ComputeGraph/cg/static/nodes/cpp/StreamingSource.h b/ComputeGraph/cg/nodes/cpp/StreamingSource.h similarity index 89% rename from ComputeGraph/cg/static/nodes/cpp/StreamingSource.h rename to ComputeGraph/cg/nodes/cpp/StreamingSource.h index d52b4b60..343b36b3 100644 --- a/ComputeGraph/cg/static/nodes/cpp/StreamingSource.h +++ b/ComputeGraph/cg/nodes/cpp/StreamingSource.h @@ -30,7 +30,7 @@ #include "RingBuffer.h" - +/* This is deprecated. Don't use it */ template class StreamingSource: public GenericSource { @@ -38,6 +38,17 @@ public: StreamingSource(FIFOBase &dst,ring_config_t *config): GenericSource(dst),mConfig(config){}; + int prepareForRunning() override + { + if (this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ OUT *b=this->getWriteBuffer(); /* diff --git a/ComputeGraph/cg/static/nodes/cpp/ToComplex.h b/ComputeGraph/cg/nodes/cpp/ToComplex.h similarity index 88% rename from ComputeGraph/cg/static/nodes/cpp/ToComplex.h rename to ComputeGraph/cg/nodes/cpp/ToComplex.h index 392bb8ae..e048416a 100644 --- a/ComputeGraph/cg/static/nodes/cpp/ToComplex.h +++ b/ComputeGraph/cg/nodes/cpp/ToComplex.h @@ -44,6 +44,18 @@ public: ToComplex(FIFOBase &src,FIFOBase &dst):GenericNode(src,dst){ }; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ IN *a=this->getReadBuffer(); IN *b=this->getWriteBuffer(); diff --git a/ComputeGraph/cg/static/nodes/cpp/ToReal.h b/ComputeGraph/cg/nodes/cpp/ToReal.h similarity index 87% rename from ComputeGraph/cg/static/nodes/cpp/ToReal.h rename to ComputeGraph/cg/nodes/cpp/ToReal.h index 82f03309..4dd26ea9 100644 --- a/ComputeGraph/cg/static/nodes/cpp/ToReal.h +++ b/ComputeGraph/cg/nodes/cpp/ToReal.h @@ -43,6 +43,18 @@ public: ToReal(FIFOBase &src,FIFOBase &dst):GenericNode(src,dst){ }; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ IN *a=this->getReadBuffer(); IN *b=this->getWriteBuffer(); diff --git a/ComputeGraph/cg/static/nodes/cpp/Unzip.h b/ComputeGraph/cg/nodes/cpp/Unzip.h similarity index 87% rename from ComputeGraph/cg/static/nodes/cpp/Unzip.h rename to ComputeGraph/cg/nodes/cpp/Unzip.h index 93d110fd..e49b3292 100644 --- a/ComputeGraph/cg/static/nodes/cpp/Unzip.h +++ b/ComputeGraph/cg/nodes/cpp/Unzip.h @@ -46,6 +46,19 @@ public: Unzip(FIFOBase &src,FIFOBase &dst1,FIFOBase &dst2): GenericNode12(src,dst1,dst2){}; + int prepareForRunning() override + { + if (this->willOverflow1() || + this->willOverflow2() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + /* 2*outputSize1 == 2*outSize2 == inputSize */ diff --git a/ComputeGraph/cg/static/nodes/cpp/Zip.h b/ComputeGraph/cg/nodes/cpp/Zip.h similarity index 86% rename from ComputeGraph/cg/static/nodes/cpp/Zip.h rename to ComputeGraph/cg/nodes/cpp/Zip.h index 1aa513a6..6447c075 100644 --- a/ComputeGraph/cg/static/nodes/cpp/Zip.h +++ b/ComputeGraph/cg/nodes/cpp/Zip.h @@ -39,6 +39,18 @@ public: Zip(FIFOBase &src1,FIFOBase &src2,FIFOBase &dst): GenericNode21(src1,src2,dst){}; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow1() || + this->willUnderflow2() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; int run(){ IN *a1=this->getReadBuffer1(); diff --git a/ComputeGraph/cg/static/nodes/cpp/host/FileSink.h b/ComputeGraph/cg/nodes/cpp/host/FileSink.h similarity index 87% rename from ComputeGraph/cg/static/nodes/cpp/host/FileSink.h rename to ComputeGraph/cg/nodes/cpp/host/FileSink.h index 9ac317f1..bda7c34d 100644 --- a/ComputeGraph/cg/static/nodes/cpp/host/FileSink.h +++ b/ComputeGraph/cg/nodes/cpp/host/FileSink.h @@ -35,6 +35,17 @@ class FileSink: public GenericSink public: FileSink(FIFOBase &src, std::string name):GenericSink(src),output(name){}; + int prepareForRunning() override + { + if (this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run() { IN *b=this->getReadBuffer(); diff --git a/ComputeGraph/cg/static/nodes/cpp/host/FileSource.h b/ComputeGraph/cg/nodes/cpp/host/FileSource.h similarity index 91% rename from ComputeGraph/cg/static/nodes/cpp/host/FileSource.h rename to ComputeGraph/cg/nodes/cpp/host/FileSource.h index b275ee2d..70252791 100644 --- a/ComputeGraph/cg/static/nodes/cpp/host/FileSource.h +++ b/ComputeGraph/cg/nodes/cpp/host/FileSource.h @@ -46,6 +46,17 @@ public: }; + int prepareForRunning() override + { + if (this->willOverflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ string str; int i; diff --git a/ComputeGraph/cg/static/src/GenericNodes.h b/ComputeGraph/cg/src/GenericNodes.h similarity index 56% rename from ComputeGraph/cg/static/src/GenericNodes.h rename to ComputeGraph/cg/src/GenericNodes.h index ff28f74c..f73464ba 100644 --- a/ComputeGraph/cg/static/src/GenericNodes.h +++ b/ComputeGraph/cg/src/GenericNodes.h @@ -1,6 +1,6 @@ /* ---------------------------------------------------------------------- * Project: CMSIS DSP Library - * Title: Sched.h + * Title: GenericNodes.h * Description: C++ support templates for the compute graph with static scheduler * * $Date: 29 July 2021 @@ -9,7 +9,7 @@ * Target Processor: Cortex-M and Cortex-A cores * -------------------------------------------------------------------- */ /* - * Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. + * Copyright (C) 2010-2022 ARM Limited or its affiliates. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -31,10 +31,20 @@ #include +/* +Defined in cg_status.h by default but user +may want to use a different header to define the +error codes of the application +*/ +#define CG_SKIP_EXECUTION_ID_CODE (-5) +#define CG_BUFFER_ERROR_ID_CODE (-6) + // FIFOS #ifdef DEBUGSCHED +#include + template struct debugtype{ typedef T type; @@ -55,23 +65,29 @@ class FIFOBase{ public: virtual T* getWriteBuffer(int nb)=0; virtual T* getReadBuffer(int nb)=0; + virtual bool willUnderflowWith(int nb)=0; + virtual bool willOverflowWith(int nb)=0; + }; +template +class FIFO; -template -class FIFO: public FIFOBase +/* Real FIFO, Synchronous */ +template +class FIFO: public FIFOBase { public: FIFO(T *buffer,int delay=0):mBuffer(buffer),readPos(0),writePos(delay) {}; FIFO(uint8_t *buffer,int delay=0):mBuffer((T*)buffer),readPos(0),writePos(delay) {}; + /* Not used in synchronous mode */ + bool willUnderflowWith(int nb) override {return false;}; + bool willOverflowWith(int nb) override {return false;}; + T * getWriteBuffer(int nb) override { - if (isArray==1) - { - return(mBuffer); - } - + T *ret; if (readPos > 0) { @@ -87,24 +103,148 @@ class FIFO: public FIFOBase T* getReadBuffer(int nb) override { - if (isArray==1) + + T *ret = mBuffer + readPos; + readPos += nb; + return(ret); + } + + #ifdef DEBUGSCHED + void dump() + { + int nb=0; + std::cout << std::endl; + for(int i=0; i < length ; i++) { - return(mBuffer); + std::cout << (typename Debug::type)mBuffer[i] << " "; + nb++; + if (nb == 10) + { + nb=0; + std::cout << std::endl; + } } + std::cout << std::endl; + std::cout << std::endl; + } + #endif + + protected: + T *mBuffer; + int readPos,writePos; +}; + +/* Buffer, Synchronous */ +template +class FIFO: public FIFOBase +{ + public: + FIFO(T *buffer,int delay=0):mBuffer(buffer),readPos(0),writePos(delay) {}; + FIFO(uint8_t *buffer,int delay=0):mBuffer((T*)buffer),readPos(0),writePos(delay) {}; + + bool willUnderflowWith(int nb) override {return false;}; + bool willOverflowWith(int nb) override {return false;}; + + T * getWriteBuffer(int nb) override + { + return(mBuffer); + }; + + T* getReadBuffer(int nb) override + { + return(mBuffer); + } + + #ifdef DEBUGSCHED + void dump() + { + int nb=0; + std::cout << std::endl; + for(int i=0; i < length ; i++) + { + std::cout << (typename Debug::type)mBuffer[i] << " "; + nb++; + if (nb == 10) + { + nb=0; + std::cout << std::endl; + } + } + std::cout << std::endl; + std::cout << std::endl; + } + #endif + + protected: + T *mBuffer; + int readPos,writePos; +}; + +/* Real FIFO, Asynchronous */ +template +class FIFO: public FIFOBase +{ + public: + FIFO(T *buffer,int delay=0):mBuffer(buffer),readPos(0),writePos(delay),nbSamples(delay) {}; + FIFO(uint8_t *buffer,int delay=0):mBuffer((T*)buffer),readPos(0),writePos(delay),nbSamples(delay) {}; + + /* + + Check for overflow must have been done + before using this function + + */ + T * getWriteBuffer(int nb) override + { + T *ret; + if (readPos > 0) + { + memcpy((void*)mBuffer,(void*)(mBuffer+readPos),(writePos-readPos)*sizeof(T)); + writePos -= readPos; + readPos = 0; + } + + ret = mBuffer + writePos; + writePos += nb; + nbSamples += nb; + return(ret); + }; + + /* + + Check for undeflow must have been done + before using this function + + */ + T* getReadBuffer(int nb) override + { + T *ret = mBuffer + readPos; readPos += nb; + nbSamples -= nb; return(ret); } + bool willUnderflowWith(int nb) override + { + return((nbSamples - nb)<0); + } + + bool willOverflowWith(int nb) override + { + return((nbSamples + nb)>length); + } + #ifdef DEBUGSCHED void dump() { int nb=0; std::cout << std::endl; + std::cout << "FIFO nb samples = " << nbSamples << std::endl; for(int i=0; i < length ; i++) { - std::cout << (Debug::type)mBuffer[i] << " "; + std::cout << (typename Debug::type)mBuffer[i] << " "; nb++; if (nb == 10) { @@ -120,6 +260,7 @@ class FIFO: public FIFOBase protected: T *mBuffer; int readPos,writePos; + int nbSamples; }; // GENERIC NODES @@ -128,6 +269,7 @@ class NodeBase { public: virtual int run()=0; + virtual int prepareForRunning()=0; }; template @@ -140,6 +282,9 @@ protected: OUT * getWriteBuffer(int nb = outputSize){return mDst.getWriteBuffer(nb);}; IN * getReadBuffer(int nb = inputSize){return mSrc.getReadBuffer(nb);}; + bool willOverflow(int nb = outputSize){return mDst.willOverflowWith(nb);}; + bool willUnderflow(int nb = inputSize){return mSrc.willUnderflowWith(nb);}; + private: FIFOBase &mSrc; FIFOBase &mDst; @@ -157,6 +302,11 @@ protected: OUT2 * getWriteBuffer2(int nb=output2Size){return mDst2.getWriteBuffer(nb);}; IN * getReadBuffer(int nb=inputSize){return mSrc.getReadBuffer(nb);}; + bool willOverflow1(int nb = output1Size){return mDst1.willOverflowWith(nb);}; + bool willOverflow2(int nb = output2Size){return mDst2.willOverflowWith(nb);}; + + bool willUnderflow(int nb = inputSize){return mSrc.willUnderflowWith(nb);}; + private: FIFOBase &mSrc; FIFOBase &mDst1; @@ -184,6 +334,12 @@ protected: IN * getReadBuffer(int nb=inputSize){return mSrc.getReadBuffer(nb);}; + bool willOverflow1(int nb = output1Size){return mDst1.willOverflowWith(nb);}; + bool willOverflow2(int nb = output2Size){return mDst2.willOverflowWith(nb);}; + bool willOverflow3(int nb = output3Size){return mDst3.willOverflowWith(nb);}; + + bool willUnderflow(int nb = inputSize){return mSrc.willUnderflowWith(nb);}; + private: FIFOBase &mSrc; FIFOBase &mDst1; @@ -205,6 +361,10 @@ protected: IN1 * getReadBuffer1(int nb=input1Size){return mSrc1.getReadBuffer(nb);}; IN2 * getReadBuffer2(int nb=input2Size){return mSrc2.getReadBuffer(nb);}; + bool willOverflow(int nb = outputSize){return mDst.willOverflowWith(nb);}; + bool willUnderflow1(int nb = input1Size){return mSrc1.willUnderflowWith(nb);}; + bool willUnderflow2(int nb = input2Size){return mSrc2.willUnderflowWith(nb);}; + private: FIFOBase &mSrc1; FIFOBase &mSrc2; @@ -222,6 +382,8 @@ public: protected: OUT * getWriteBuffer(int nb=outputSize){return mDst.getWriteBuffer(nb);}; + bool willOverflow(int nb = outputSize){return mDst.willOverflowWith(nb);}; + private: FIFOBase &mDst; }; @@ -235,6 +397,8 @@ public: protected: IN * getReadBuffer(int nb=inputSize){return mSrc.getReadBuffer(nb);}; + bool willUnderflow(int nb = inputSize){return mSrc.willUnderflowWith(nb);}; + private: FIFOBase &mSrc; }; @@ -253,7 +417,19 @@ public: Duplicate2(FIFOBase &src,FIFOBase &dst1,FIFOBase &dst2): GenericNode12(src,dst1,dst2){}; - int run(){ + int prepareForRunning() override + { + if (this->willUnderflow() || + this->willOverflow1() || + this->willOverflow2()) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + + int run() override { IN *a=this->getReadBuffer(); IN *b1=this->getWriteBuffer1(); IN *b2=this->getWriteBuffer2(); @@ -293,7 +469,21 @@ public: IN,inputSize, IN,inputSize>(src,dst1,dst2,dst3){}; - int run(){ + int prepareForRunning() override + { + if (this->willUnderflow() || + this->willOverflow1() || + this->willOverflow2() || + this->willOverflow3() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + + int run() override { IN *a=this->getReadBuffer(); IN *b1=this->getWriteBuffer1(); IN *b2=this->getWriteBuffer2(); diff --git a/ComputeGraph/cg/src/cg_status.h b/ComputeGraph/cg/src/cg_status.h new file mode 100644 index 00000000..430dc437 --- /dev/null +++ b/ComputeGraph/cg/src/cg_status.h @@ -0,0 +1,17 @@ +#ifndef _CG_STATUS_H_ + + + typedef enum + { + CG_SUCCESS = 0, /**< No error */ + CG_BUFFER_UNDERFLOW = -1, /**< FIFO underflow */ + CG_BUFFER_OVERFLOW = -2, /**< FIFO overflow */ + CG_MEMORY_ALLOCATION_FAILURE = -3, /**< Memory allocation failure */ + CG_INIT_FAILURE = -4, /**< Node initialization failure */ + CG_SKIP_EXECUTION = -5, /**< Skip node execution (asynchronous mode) */ + CG_BUFFER_ERROR = -6, /**< Stop execution due to FIFO overflow or underflow (asynchronous mode for pure function) */ + } cg_status; + + + +#endif /* _CG_STATUS_H_ */ \ No newline at end of file diff --git a/ComputeGraph/documentation/supported_configs.png b/ComputeGraph/documentation/supported_configs.png new file mode 100644 index 0000000000000000000000000000000000000000..d1b536ec65f38c2671e6fedcd5fe45a83bc43839 GIT binary patch literal 6781 zcmeI1X*3&JyT_wPtL3EHs%lY=R?K5fRWm(?7;07&RZ~r&W+kFp6je3UJT)kSP;*Ri zN)ck71vy$xkVaG_h8V8%-uvOM``)|Od)IqEydUm|z4qFB?e(l@?X}mlfB*lJU}~ZV zWaVZB002M({rlzs02AVL$eurUx+5t?2sxdY0?qZb0mu>F_0t8jhnBGx0Prb=?a=w` z>H31dzHJ}?a1Hd=U@|Zl+yVf&uNd6dvJ7)1FZsRZ361LiS&NK~F#X}tDMuH&bQ`r^ zPcY&Wbe)=DQC1QrbEHR8v`~B(obMmH29L%1U)Sz_d-C1EbGK@m@dc=oi_XmnQ$IM$L0Jx6 z0st}&UNG1I(O_myfTltWE1;NL>ny-Dx%dpALf4cDV0pU&06<)}1OTSbixtmjeB|-< z&IILw4G^ZG9730B(fqi`;h*yV!T<>_$y2kOh92+mvoeB*sWw;tHk{`s$gdi z2|wFAkf6+w>g;bn(_ZE|2R+zz{r)EJvPvpx_b5Ax{w0$?ZW+`%yhWlHGu!32C75Ga_2O%GTYQ;bZoG;M z4w^G8zt9l-9CfLN>!K|tlhojSou7VlX{To*S(nQ(hH+Ui8eY)t`R5$0f*RqWqAit-JjN zA>T`C%;Th{VXh80>eMv|%o{mkZM)Q zCV%$MfjyR_vP*EQsu*J%nP?}^aMQ&hWzOVtJqz$S&{Z?OE0CvdDp!U)=r-;F=>C? z!|<3aAfv%jvXLYjYQ|qB8tR8b7e0Da@YOf~Z9THH_!?WYh6V4<+Vd^%?}xMtYeznE zU$E9t{ZAZAX2$vvA? zUk+<)+92h&fIt;Q@`u_Mi%x_*`<_SESU&vpj7V3nYkQNzX|a4Zb<*aNYd- z(;}F1_2#;T%uXvWL*i}jF6m1dLf1?&jRs@!KO$ltIZVo)URkI;az~!o!{P=8HxgPX z-2|?uIGDg;dC6P*Sw5|iiW{Qt_@--84G$9XT>I4TdhVt+F=Ujqsn7-BF;&PWwQgQo z6=}Kmb#^UMk3E0E<5~06;T_6OG-i=0tPU5JDWSMVAsA(vb~sW!ucUld0OxGU1aB_% zr*}}-FolWn^0)J)B&9zDywSRq&&xvbBW9PjflmapHckg*^-N?>Z( zlQuc7G0jHG4sW+v&{+PbYWt7p@DHZ?uX8MU(g!@5%va%VTVI(`${MW11h|%-R_q1t ze3uNtLI&K~%#r-y`$1D|X@jnU>0$#xU*S@*y|Q%n$pv-*?@iX2!Q~FQv|7c|mGG~O zt{bYJ@(6RRM`^p}ct*g?@>qj;Avn&sRqbydh!^(Lb7)U}-UYzxo6k)ZhHOf`^bT_y zn_J4vE3=*L?X--+1Ol`ofYY+{ zPE9P-eF9}QDZU-@!Lfb&O960xZ`J(C&6Z_hp6x#hc(yz3uV}8mdx;R8RpeR8hJ7Gj zD;UA#qQY%j19d)7DA~oRk}_Wsm8z%Plv4Jfka+=0g-!VBNCv zLy)7()cVOv7d3Mq{mv#1mSNBEjhw1&Ivaoc3ZkRqFh4&i&}HRbQBE|gMow_1Q`YPP zDSPG6Dx92?wOaXY z+XWO=gOnMoZPn9;bD3;ZrZmOi!NkmUdY}QKz(h6Ce4RZo(U0Ap4P)Hlf6E8a97t8A{*{6$@)qvB0)}?=%tRFz4pi=0;TOB4(TjexPC9%y zLZhIabhxJxmJ#ta-NXFIPD<`yDdm1s`rvBmCTH0+%a zNG4zgb3{XL<%G6x??cMvgSYowAU+v90aZ)2UmNpDVo;{+SBb^-KY(5JOKHDV1fQKL zT+sJXdGFgJdKK>MGU?-Mmm9F#d_=-*7c#0DPgs@AQa-QBG!qB*p#2{*e5l?m!SKY| z8;}7X4>bh0kB_7GsCT@xA4^v3Zp|X>Pu*mWexdUP@%RC#lGyeu+x}#3r$r#ffE;D3LnwIIzWRG3-_R<2-XI*Pi$wYBR9h!7j(qD1rHk0=ar1=ND;+ zQ5U7p1Rj*>_&$jqq^JlIyNg=RT>jnG*h3K8-1m0=xx6&1j?+M;sLFBc;Xv}4?EJRV z@Uv2S1%TD&H|v+?tfgH`uZNV17)a`8SjJ3TtvX&zKDyjByJOVlS6emkK66^65-!$7 zP)G>yn#Lv&Q5)JyqgTtrK6)?kMc#(_m(0wUt8^Ya+N+a4o>?nXtl)Dfx2xV+79`(~ z;0&$w)2|;3km)10Qy(pWAEb&SkJ+mBVhZZSBMBNM^46g>Zd zso&>M{oh~laPBWuf74|$`r-d_P`zA!t00PQTM;=`%y0c5mLs|I)vNg#UD=L(I?jHr zQAPNXXkJaI_YCQ1^p3IFVgWjr0&_>$;$IiC$$)MdRXpo?w|GSxeFd2eqbwt zG9-8!b#4_U-viIZ`}F8mo6s_$?qWP4rFHDbeK>BPJ$jsBZKtp^hh52ay2%^5l?Q{7K5H-?SaI9nvxK8$eDb^{3qZdSVaapSwlsF%AUAxdr^}f>B>{dg3o9BpARN4@E z_xbIWfKo zT__bG1ud_)h!^d4nqSbAn6B>xM&D50ePRHCJ`5^)o9KW##)z9623b+KI$yw#LRcAh z7V`WC+Y^X&amuJjtC^yFJhA32L81R{L&v6Gh1^+x)6CVw2O1$10*PD`YG)$VDJWfZ z5`3%luR6D*vQ6Kg1)*It*P-P_^CpeUk?~h$*fLI0U{RCD(UETTr1=0NL zV8VZqpZ^pPWqRF!@MgD_OJ#mPInFxiy#^lq_0e`L^`(X(hN3k4|9tj$x}T2tOYW&z`qy` zTXezD@;@Yfp*%OR@*0)k?t7-H>)kUqlc9Ro#kHyIKoSi&1Q7yA6j~1XfBR+5=^(r^ zQM$?c_W+1jsVHq*8sy~js_4+pP;80q@spB~Kz4gtmMFNV*1-Fs$sUT!Fo4tpX9O{; zb_mR(4>cj=DA4YrVCrN*z&d5s16J_9p+9r6vD9Jz>j)at-|jT@7gl*@+EVs70-mh< ztx}32t)qT?9BIEDIxL%N>$nSTG%EwOG)rn>*fgwaGq@VKFR^`NsAR3$%;MkK+34F6 zsGEM@mq>ntw0KAw^(d65{^z*(_U1^rnt)pljW1FRjStsZZVT@*{$jskgej?S8Zl=ZQ(YA)>Vn$PXvXma@Q^ZCe=-cLDy6sC7A*y2XqiR^dd_72|U zD*agCvr;TwUPyRJXjG+Pt-=|xDNFuVj*d|#CT*xs{sp5(nz2RLdqcHD-sE7c|1qM2 zjy2q#`svRE=$S*HSG*)Sv?VX&-vDi>!5yU^P_(L`m93>BFb|HrU>{$NxI%Sli zA^sCphihT5ws1sc&P0DHz>EYw?d>vNpCPN<8`=CuS~}T+Hm5ju(~=0lyJ(%m)!3G$ zIo^mZN)0}8FS2SaZZ~vk$Ee-RqQLW)F%pi_pUe zw6~sz207Z`B8Ib{jB~6iJYvR#LvlG;%y&P2r96pW)(oJYI|khNA+p%G3ETHlDNQU= zSezPHKasdtNwi1rZ`&&c%U-ff^30rt>W-R~xzXUL0`BRW&5e=+YlTCS)Ag~--QotO zn4Bh+Q(yxvPLSJ|tUOSOJ~lIgS&!zPN^2P5)$ie_7Z2ScBvF6lSI7JM1LrTmMN1k} z$N{kU*FngWcDlEIiQRiIN}WO!kqn*1c14UPDBB2Syg>*`>$!(_DS$& z=#48&51=@POwrqCzX!<%X**GxbrgOLYga=$LY^X;i58tJNH2(PS z{>Ym-?DghZr-y@NzCQXU51Mi}BduR{O;1y9@!kB$LG0Fnf?{nGG5Ij{nJZL$m%r8Q zTW_}`RE;m{_U=-}0EhccC9lY5RFCbdl!N$W6|(CuV=iJ^oImc5k3maDF|o@|KYvOG zoP1uBn&a@)y?N`j+z^uzt_%!h=YG(HPuAV?xY*GuyWhowfjd99`uy3|#R48SZHoOyvsi ze3uFbf@%euelt5b4>l8B=|=IiFJ^vJ$!D>y$HLW$$yo`+d4BF;Z?pFs zPplZWDVT)&yFVH1!WD28dTYLa$t|b6;6;&WwfP8Blt=cRl{`^+_u6PutDLqK(zQf0 zDXL~Pao>dJ%U0a;>k9=O=Tr|DtMe)foeioSo)7P8$W2%K=?CSUJJf&rHx%46yk*wJ zH7cheuL%D-b;g<@8GLrSlLg=Q$*k~wAo*i;?dnWuqXrX0*4fkwR5TnX4F|F?WGZ}l zSaC0n^v~iZrfsD@93^#Lmc-eswJH^eRXyp8EULR5d*n!0`Q4B=Zf7ClO7InlIEv<* zE*k+=r<_Pfp|y^d(1b$h{u`+2e6SQ|0UEDyIqoof&GM&JPd?b1`~{>SCylNAQcYKh z*icq-{}DB|OHa^BBQ4_AVJ>H*F~*MYJ6bM1LcCbLQEHhbo8VI*d!^}N)grg0TC3jr z7R|T|b=2q3Hr#RtUwLf`zR{}< z^$ww|anek>^mE$V(IjuKk@0Y?b>yRyRnAIK8)8>Qa-T{W*bfQaIsKamdE1b$%GJ&$ zQcmw_!YO@J>yC^NSy8AcxwKDJekmBs*ox1~2&-jg3a=I^~Zd8*>0 z1uhmRvjUXFUTUmo6RH^l7!-m^1WJz=7}5IyK4tl~~vMgW7~P3|MLAIJO~%eJBE literal 0 HcmV?d00001 diff --git a/ComputeGraph/examples/CMakeLists.txt b/ComputeGraph/examples/CMakeLists.txt index 7b7f529d..06f30c0c 100644 --- a/ComputeGraph/examples/CMakeLists.txt +++ b/ComputeGraph/examples/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.14) +cmake_minimum_required (VERSION 3.15) include(CMakePrintHelpers) set(Python_FIND_REGISTRY "LAST") @@ -26,13 +26,33 @@ function(sdf TARGET) target_sources(${TARGET} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/generated/scheduler.cpp) endfunction() -set(CGSTATICDIR ${CMAKE_CURRENT_SOURCE_DIR}/../cg/static) +function(sdfpython TARGET) + if (DOT) + add_custom_command(TARGET ${TARGET} PRE_BUILD + BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/test.pdf + COMMAND ${DOT} -Tpdf -o ${CMAKE_CURRENT_SOURCE_DIR}/test.pdf ${CMAKE_CURRENT_SOURCE_DIR}/test.dot + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/test.dot + VERBATIM + ) + endif() + + add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/sched.py + ${CMAKE_CURRENT_SOURCE_DIR}/test.dot + COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/graph.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/graph.py + VERBATIM + ) +endfunction() + +set(CGDIR ${CMAKE_CURRENT_SOURCE_DIR}/../cg) set(DSP ${CMAKE_CURRENT_SOURCE_DIR}/../..) function(add_sdf_dir TARGET) - target_include_directories(${TARGET} PRIVATE ${CGSTATICDIR}/src) - target_include_directories(${TARGET} PRIVATE ${CGSTATICDIR}/nodes/cpp) + target_include_directories(${TARGET} PRIVATE ${CGDIR}/src) + target_include_directories(${TARGET} PRIVATE ${CGDIR}/nodes/cpp) target_include_directories(${TARGET} PRIVATE ${CMSISCORE}) target_include_directories(${TARGET} PRIVATE ${DSP}/Include) endfunction() @@ -52,3 +72,9 @@ add_subdirectory(example3 bin_example3) add_subdirectory(example6 bin_example6) add_subdirectory(example8 bin_example8) add_subdirectory(example9 bin_example9) +add_subdirectory(example10 bin_example10) + +# Python examples +add_subdirectory(example4 bin_example4) +add_subdirectory(example5 bin_example5) +add_subdirectory(example7 bin_example7) diff --git a/ComputeGraph/examples/example1/AppNodes.h b/ComputeGraph/examples/example1/AppNodes.h index 1bdd4c45..653d5e82 100644 --- a/ComputeGraph/examples/example1/AppNodes.h +++ b/ComputeGraph/examples/example1/AppNodes.h @@ -36,6 +36,17 @@ class Sink: public GenericSink public: Sink(FIFOBase &src):GenericSink(src){}; + int prepareForRunning() override + { + if (this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run() { IN *b=this->getReadBuffer(); @@ -55,6 +66,17 @@ class Source: GenericSource public: Source(FIFOBase &dst):GenericSource(dst),mCounter(0){}; + int prepareForRunning() override + { + if (this->willOverflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ OUT *b=this->getWriteBuffer(); @@ -76,6 +98,18 @@ class ProcessingNode: public GenericNode public: ProcessingNode(FIFOBase &src,FIFOBase &dst,int,const char*,int):GenericNode(src,dst){}; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ printf("ProcessingNode\n"); IN *a=this->getReadBuffer(); diff --git a/ComputeGraph/examples/example1/generated/scheduler.cpp b/ComputeGraph/examples/example1/generated/scheduler.cpp index e3825ee0..0fcded00 100644 --- a/ComputeGraph/examples/example1/generated/scheduler.cpp +++ b/ComputeGraph/examples/example1/generated/scheduler.cpp @@ -72,6 +72,7 @@ The support classes and code is covered by CMSIS-DSP license. CG_AFTER_INCLUDES + /* Description of the scheduling. @@ -111,8 +112,8 @@ uint32_t scheduler(int *error,int someVariable) /* Create FIFOs objects */ - FIFO fifo0(buf1); - FIFO fifo1(buf2); + FIFO fifo0(buf1); + FIFO fifo1(buf2); CG_BEFORE_NODE_INIT; /* @@ -131,6 +132,7 @@ uint32_t scheduler(int *error,int someVariable) for(unsigned long id=0 ; id < 17; id++) { CG_BEFORE_NODE_EXECUTION; + switch(schedule[id]) { case 0: diff --git a/ComputeGraph/examples/example1/graph.py b/ComputeGraph/examples/example1/graph.py index 29cd45fc..d4242c0f 100644 --- a/ComputeGraph/examples/example1/graph.py +++ b/ComputeGraph/examples/example1/graph.py @@ -1,4 +1,4 @@ -from cmsisdsp.cg.static.scheduler import * +from cmsisdsp.cg.scheduler import * ### Define new types of Nodes diff --git a/ComputeGraph/examples/example10/AppNodes.h b/ComputeGraph/examples/example10/AppNodes.h new file mode 100644 index 00000000..e588337c --- /dev/null +++ b/ComputeGraph/examples/example10/AppNodes.h @@ -0,0 +1,195 @@ +/* ---------------------------------------------------------------------- + * Project: CMSIS DSP Library + * Title: AppNodes.h + * Description: Application nodes for Example 1 + * + * $Date: 29 July 2021 + * $Revision: V1.10.0 + * + * Target Processor: Cortex-M and Cortex-A cores + * -------------------------------------------------------------------- */ +/* + * Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _APPNODES_H_ +#define _APPNODES_H_ + +#include +#include + +static int count=0; + +template +class SourceOdd; + +template +class SourceEven; + +template<> +class SourceOdd: public GenericSource +{ +public: + SourceOdd(FIFOBase &dst): + GenericSource(dst){}; + + int prepareForRunning() override + { + if (this->willOverflow()) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + + int run() override{ + if (count & 1) + { + int16_t *b=this->getWriteBuffer(); + b[0] = count; + } + return(0); + }; + + + +}; + +template<> +class SourceEven: public GenericSource +{ +public: + SourceEven(FIFOBase &dst): + GenericSource(dst){}; + + int prepareForRunning() override + { + if (this->willOverflow()) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + + int run() override{ + if (!(count & 1)) + { + int16_t *b=this->getWriteBuffer(); + b[0] = count; + } + return(0); + }; + + +}; + +template +class ProcessingOddEven; + +template<> +class ProcessingOddEven: +public GenericNode21 +{ +public: + ProcessingOddEven(FIFOBase &srcOdd, + FIFOBase &srcEven, + FIFOBase &dst): + GenericNode21(srcOdd,srcEven,dst){}; + + int prepareForRunning() override + { + if (this->willOverflow()) + { + return(CG_SKIP_EXECUTION_ID_CODE); + } + return(0); + }; + + int run() override{ + int16_t oddVal=0; + int16_t evenVal=0; + if (!this->willUnderflow1()) + { + int16_t *odd=this->getReadBuffer1(); + oddVal=odd[0]; + } + + if (!this->willUnderflow2()) + { + int16_t *even=this->getReadBuffer2(); + evenVal=even[0]; + } + + int16_t *dst=this->getWriteBuffer(); + + dst[0] = (evenVal + oddVal); + + return(0); + }; + +}; + +template +class SinkAsync; + +template +class SinkAsync: +public GenericSink +{ +public: + SinkAsync(FIFOBase &src): + GenericSink(src){ + }; + + int prepareForRunning() override + { + if (this->willUnderflow()) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + + int run() override + { + int16_t *b=this->getReadBuffer(); + for(int i=0;i fifo0(buf1); + FIFO fifo1(buf2); + FIFO fifo2(buf3); + FIFO fifo3(buf4); + FIFO fifo4(buf5); + FIFO fifo5(buf6); + + CG_BEFORE_NODE_INIT; + /* + Create node objects + */ + Duplicate2 dup0(fifo3,fifo4,fifo5); + ProcessingOddEven proc(fifo0,fifo1,fifo2); + SinkAsync sinka(fifo4); + SinkAsync sinkb(fifo5); + SourceEven sourceEven(fifo1); + SourceOdd sourceOdd(fifo0); + + /* Run several schedule iterations */ + CG_BEFORE_SCHEDULE; + while((cgStaticError==0) && (debugCounter > 0)) + { + /* Run a schedule iteration */ + CG_BEFORE_ITERATION; + for(unsigned long id=0 ; id < 7; id++) + { + CG_BEFORE_NODE_EXECUTION; + + cgStaticError = 0; + switch(schedule[id]) + { + case 0: + { + + bool canRun=true; + canRun &= !fifo2.willUnderflowWith(1); + canRun &= !fifo3.willOverflowWith(1); + + if (!canRun) + { + cgStaticError = CG_SKIP_EXECUTION_ID_CODE; + } + else + { + cgStaticError = 0; + } + } + break; + + case 1: + { + cgStaticError = dup0.prepareForRunning(); + } + break; + + case 2: + { + cgStaticError = proc.prepareForRunning(); + } + break; + + case 3: + { + cgStaticError = sinka.prepareForRunning(); + } + break; + + case 4: + { + cgStaticError = sinkb.prepareForRunning(); + } + break; + + case 5: + { + cgStaticError = sourceEven.prepareForRunning(); + } + break; + + case 6: + { + cgStaticError = sourceOdd.prepareForRunning(); + } + break; + + default: + break; + } + + if (cgStaticError == CG_SKIP_EXECUTION_ID_CODE) + continue; + + CHECKERROR; + + switch(schedule[id]) + { + case 0: + { + + { + + int16_t* i0; + int16_t* o1; + i0=fifo2.getReadBuffer(1); + o1=fifo3.getWriteBuffer(1); + compute(i0,o1,1); + cgStaticError = 0; + } + } + break; + + case 1: + { + cgStaticError = dup0.run(); + } + break; + + case 2: + { + cgStaticError = proc.run(); + } + break; + + case 3: + { + cgStaticError = sinka.run(); + } + break; + + case 4: + { + cgStaticError = sinkb.run(); + } + break; + + case 5: + { + cgStaticError = sourceEven.run(); + } + break; + + case 6: + { + cgStaticError = sourceOdd.run(); + } + break; + + default: + break; + } + CG_AFTER_NODE_EXECUTION; + CHECKERROR; + } + debugCounter--; + CG_AFTER_ITERATION; + nbSchedule++; + } + +errorHandling: + CG_AFTER_SCHEDULE; + *error=cgStaticError; + return(nbSchedule); +} diff --git a/ComputeGraph/examples/example10/generated/scheduler.h b/ComputeGraph/examples/example10/generated/scheduler.h new file mode 100644 index 00000000..d98d9e63 --- /dev/null +++ b/ComputeGraph/examples/example10/generated/scheduler.h @@ -0,0 +1,26 @@ +/* + +Generated with CMSIS-DSP Compute Graph Scripts. +The generated code is not covered by CMSIS-DSP license. + +The support classes and code is covered by CMSIS-DSP license. + +*/ + +#ifndef _SCHEDULER_H_ +#define _SCHEDULER_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + + +extern uint32_t scheduler(int *error); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/ComputeGraph/examples/example10/graph.py b/ComputeGraph/examples/example10/graph.py new file mode 100644 index 00000000..83371029 --- /dev/null +++ b/ComputeGraph/examples/example10/graph.py @@ -0,0 +1,132 @@ +from cmsisdsp.cg.scheduler import * + +### Define new types of Nodes + + + +class SinkAsync(GenericSink): + def __init__(self,name,theType,inLength): + GenericSink.__init__(self,name) + self.addInput("i",theType,inLength) + + @property + def typeName(self): + return "SinkAsync" + +class SourceOdd(GenericSource): + def __init__(self,name,theType,inLength): + GenericSource.__init__(self,name) + self.addOutput("o",theType,inLength) + + @property + def typeName(self): + return "SourceOdd" + +class SourceEven(GenericSource): + def __init__(self,name,theType,inLength): + GenericSource.__init__(self,name) + self.addOutput("o",theType,inLength) + + @property + def typeName(self): + return "SourceEven" + +class ProcessingOddEven(GenericNode): + def __init__(self,name,theType,inLength,outLength): + GenericNode.__init__(self,name) + self.addInput("ia",theType,inLength) + self.addInput("ib",theType,inLength) + self.addOutput("o",theType,outLength) + + @property + def typeName(self): + return "ProcessingOddEven" + + + +### Define nodes +dataType=CType(SINT16) +odd=SourceOdd("sourceOdd",dataType,1) +even=SourceEven("sourceEven",dataType,1) + +proc=ProcessingOddEven("proc",dataType,1,1) +comp=Unary("compute",dataType,1) + +sinka=SinkAsync("sinka",dataType,1) +sinkb=SinkAsync("sinkb",dataType,1) + +g = Graph() + +# Option to customize the default class +# to use for Duplicate and FIFO +# FIFO class can also be changed in the connect +# function to change the class for a specific +# connection +g.defaultFIFOClass = "FIFO" +g.duplicateNodeClassName = "Duplicate" + +g.connect(odd.o,proc.ia) +g.connect(even.o,proc.ib) + +g.connect(proc.o,comp.i) +g.connect(comp.o,sinka.i) +g.connect(comp.o,sinkb.i) + + +print("Generate graphviz and code") + +conf=Configuration() +conf.debugLimit=10 +conf.cOptionalArgs="" + +# It is disabled in asynchronous mode +# since the schedule has no mor any reason to +# be periodic and thus the FIFO content can +# overlap several executions of the schedule +conf.memoryOptimization=True + +conf.codeArray = True +conf.switchCase = True + +# Asynchronous mode enable. It implies +# switchCase and codeArray true +conf.asynchronous = True + +# Increase size of synchronous FIFOs by 100% +# for the asynchronous case (so 2 samples +# instead of 1 in this example) +conf.FIFOIncrease = 100 # percent + + +#conf.displayFIFOSizes=True +# Prefix for global FIFO buffers +#conf.prefix="sched1" + +#conf.dumpSchedule = True +sched = g.computeSchedule(config=conf) +#print(sched.schedule) +print("Schedule length = %d" % sched.scheduleLength) +print("Memory usage %d bytes" % sched.memory) +# + + +#conf.postCustomCName = "post.h" +#conf.CAPI = True +#conf.prefix="global" +#conf.dumpFIFO = True +#conf.CMSISDSP = False +#conf.codeArray = False +#conf.switchCase = False + +# For pure functions (like CMSIS-DSP) which are not +# packaged in a C++ class, we have to decide of the +# asynchronous policy. What must be done in case of +# FIFO overflow or underflow. +# By default, the execution is skipped. +conf.asyncDefaultSkip = True + +sched.ccode("./generated",conf) + +with open("test.dot","w") as f: + sched.graphviz(f) + diff --git a/ComputeGraph/examples/example10/main.cpp b/ComputeGraph/examples/example10/main.cpp new file mode 100644 index 00000000..a1fd4028 --- /dev/null +++ b/ComputeGraph/examples/example10/main.cpp @@ -0,0 +1,11 @@ +#include +#include +#include "scheduler.h" + +int main(int argc, char const *argv[]) +{ + int error; + printf("Start\n"); + uint32_t nbSched=scheduler(&error); + return 0; +} \ No newline at end of file diff --git a/ComputeGraph/examples/example10/test.dot b/ComputeGraph/examples/example10/test.dot new file mode 100644 index 00000000..490fa64b --- /dev/null +++ b/ComputeGraph/examples/example10/test.dot @@ -0,0 +1,104 @@ + + + + +digraph structs { + node [shape=plaintext] + rankdir=LR + edge [arrowsize=0.5] + fontname="times" + + +compute1 [label=< + + + + +
compute
(Function)
>]; + +dup0 [shape=point,label=dup0] + + +proc [label=< + + + + + + + + + + + + +
iaproc
(ProcessingOddEven)
o
ib
>]; + +sinka [label=< + + + + +
sinka
(SinkAsync)
>]; + +sinkb [label=< + + + + +
sinkb
(SinkAsync)
>]; + +sourceEven [label=< + + + + +
sourceEven
(SourceEven)
>]; + +sourceOdd [label=< + + + + +
sourceOdd
(SourceOdd)
>]; + + + +sourceOdd:i -> proc:ia [label="s16(2)" +,headlabel=<
1 +
> +,taillabel=<
1 +
>] + +sourceEven:i -> proc:ib [label="s16(2)" +,headlabel=<
1 +
> +,taillabel=<
1 +
>] + +proc:o -> compute1:i [label="s16(2)" +,headlabel=<
1 +
> +,taillabel=<
1 +
>] + +compute1:i -> +dup0 [label="s16(2)" + +,taillabel=<
1 +
>] + + +dup0 -> sinka:i [label="s16(2)" +,headlabel=<
1 +
> +] + + +dup0 -> sinkb:i [label="s16(2)" +,headlabel=<
1 +
> +] + + +} diff --git a/ComputeGraph/examples/example10/test.pdf b/ComputeGraph/examples/example10/test.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b5508a129dfe002979efc7f3b2c0acd0e020ca54 GIT binary patch literal 25246 zcmZs>18^rn_xBsywr$(CZQHi9v2FfhV`JO4oeej3a`*Y;t@qx#Q(fKDXU_Du&s5i^ z>P#JSB{2zlW(IZ`^49CR4;WS=CL%`@8yG%5B1SoL2TM0ABDQ}f6&NBSB1Q>oJ2!Ke zf1{nTo4J^|siT=WjDP@)tDB3tu|14eZfl0D>oy16*zG$SpG<@~6bjjZDPM{Q1#6x_ zQyUrN*k!x)QJMRDX28ccu=TaSwdcqBomRRS2uU#EPf*}>$Wj4eug6?nXxGPL(g*6T z!QqSP%X=-XK)br+fd5guMgjiFg^VjK!p*^IPxckz&Gt$Y>2JtS_8x#45(pf(W5VF4JiLWb}(qpH(onIu4ae`^dZYme*Fsjd0up3Hb!U|ZNdXmEOX21jBQCy`VL&)wn_z4X2 zy?xro^mrgOqwzJKY?jMp_k|rLWjhZQwX1AI04YGtaT7yav1;<$8BF3~@W2uif_Kgu?Gp>S|LU!R^+=_SQ5cP23cOIsfTjOX?X2vof}$l~Tq!X(EQ zga^NoNp4poOEJjTrieq-)Oe{zB2?g#&Pyt2$O@n%OHac`T3Kk~%priKs-($5Yp%e~ z3MyR1qc>#wb1ib2UYBM-Dl$t+&d7GpeIUEqS1nA$AvxCUG7QCVrL)ENKF)~!h_T|U*Wbd;(9_ZClvT~B$)`L;Xjy+=ZuKmmiEZ^;j}{7G}nKYW^HhIaBwZ;1rI-f63(3Z^BxsCj&i8X~JGT zCt!g<=!eLxsY(KcSLCXk%_;JeWR}Tizr5;sCQyd^ULnu9`e6BppoRo>Wb+@@?1hpo z@9C)pwfd#(Gm9h??JS6gfSpz-###Tu&jXtW^H*JpCmyX}{tOGgDEdhB84MC>=5>$9 zrqL4a0%M`#8F%3qmR-HJX+u?FQ(|0%W0p^2DtVhKPZW-T;{3_aYMr+J*8G8G+9G%w zf^=TEQIJLSDNu{W6jolrprR%&?ZErt7E;I=P?G2H>u1g)9qm{8lQ>UdCIQHTQZAA% zlApht7Lb5n5RZP2jJtW`OA~gnk&c?e;M(&H1br3*i-j?FF#8`!{JZ-PUjD&@<695wZU-!Tve@YyTt0@qfgK7}dO;%!wG4j4jRoOIXR+#r)qo z%>VlyqrAD9wXuk!7m@Bi6B7{^D;E(96C08Ke|d}k)BNY|O7tIi{x>NlT^!w={;z%f z_xk^3{og=T|EandJGeUiN6Xavf6dZFZZ7WswTu5mEBen%%-qA;)LcbU;HxHt8Q;2+>stG zf#$AF(X)$QK$^R56Pa#0J~zeY{&u{y2m^0aq^l4JmPr>eyI4t$1LV|Dg&Bb1kwcPgn^ag%)R_DE4U0vNwU!CR8 zK^cc7V!#t&P~+BVt$kEG3a*FGhqncGl-ub z&N!*AmE~VD3>4&k^YR@yhoyh|Jd9j&1k9JjleU~trLx=q0 zY)dWpFzcP@Uuxk!yE^@K^}l-c(|Kz`PleZpUbm)E4?tfbUnK_N6l9z61zqH*rRfMjU%Ulo$ulf{U(`MwoWH;Q^E8huNaGY^nHvKpe?~uQRci5 z54D1dD@2td0f&3PIpgE>&Kx1=UJK<%uhFD0_lot((tO3MzsKpVExU$#Fn{g+YyI)T zBQf>mZ|Z-N;{R1r0al`ggMXc(nDOzhsPp{rxMh{`&iN{MX4!W*R6vbVBm0h%h|{#_Z#SB*Za!u(FO}`^VLH<9{f^*Iu_@5q7Esb z6&Vebz>LIG6%hLRa?JIVy*As%-GRh8l#vD1m?q9j* z2M6V9_`uu@g=>(Yrx%%}W?lv|0_nw!!I0I>^q?M}kw*+TBR9qfr}3zyR#q&H*pztspCj(#hw__4dP2lhPs96hDm z;%j!*Jx84|^y~NQsNYYV7Gomt!o3IJi!ayJF&H_sh%1ss&PM^T- zmcYQt$W{ z3Xbsj*}@d)wXpazfNOyDGt>LS!V4zQZ_moc9)?;Q^)yHSF9{v@%#&k5csbdbVK>3xVk8Jf^S9@dt9$g%Z68gxT2!5-Y??TW=8Z+&6PJrjJ`NEM zndVdz)yhB!U>W?h1Q8m-G*z}hg8O$p8g}vB+P_|2oeJ|%Q|a|cdQYx1v2+jf)bVCA z(UfHQ#pac#S&w!U{5c&+?{{OA&;ztGl(9>akkZWHQuILIGW8sso`amPsPtg_Xtfai zl8+aI;re-DszOK8mMZ%gE1;c0Rs{)ZGA#3Y{s@TJn;Gr`up)2zM!#;0>F_$DJQ2P1 z95SZZYdhV&)eTFWx-aJtJPKiRx;lg7O5wa3U}=k!;o51Y%OP#EQ-Xbf)bwWt&5UkB za9mmTYZ^vF57bx^s~diW9%N1Q;{h#WADrC9XH9`jSw>BqBD7}8MM_1^>Nh6)Wm@Uf zuR!yaBcIMz&UNH&GL$co=8zWUhWMrthbz;x7iMRtWT~0y)M3AHAhNU0luj=+x>%SU z3>u9cB3KlfHiQb}yFe}h$6hzgEUYi=Y^WP+;6w3u@2C&pI!Xf=xbi@DS2l@56p@4WmTi7>#b7~Te%U{&ezwslC`($%3S z1Ke2=Fz#AaZsi&}hx#w!Uq5_^AcX$@(|xCL`Ix9W<$Xj8lfh_)rAAqVqFzX!XO zNihS*v5yKgJSp-`gsNKe>(8n4M-_!5*>Sr)Je(oVusy}tu7BK)KjWHJ70|#XGZIYt6^6pS_!Jkk>oy zr~U4F=ze;+$QgqOeH%q6`)m7g)=$$^k+fW4Ogl-Qj9!CC@1>SqgJ{nSYeY2W5ST5d zu@r?aBPoZ;8k#hbqeFl6*L$Id(v+JmWQ(Ge_VovhY3;szm8LQVYO5bnSQL-0x-omv zP5Rkp)Q(VO%>1fW7FAm9e959!--dQ7b_CC5Pu+vA*rQ!{ygaNSRn@ z^SVG?d(M$Y7al@;al0~$L8IGIEV=FStRBZ8#)n~_gZ|u1eVjnUjzbl0;BB!V5zYvL z{+EP@zvis@w!rre{XzOHECcubQZK9?M&cLTd6DUrKGmdKe&|~~SK1dznO3~JjETy( zKMyq_Xv%tkN~;Rl%X~BeY+7r2)p?;%8UqlQf23NASlM|0dGtNc$ob)yb%_E}RIvEC z&~ovWZl&qp86Z$$H9sZtU{iHvu6v9$A5{@YKwoL+;!A*SH!heVTLKI-El#q9fkX{S2Q6}R z$OLZ=4djqc9P=p#olm#bMw#uxW9b{Km8Vt#Qw8cBcL8aXVms#LP?I@=cAC{@`S4kc zE`4NGn3ff2A~8)|W1|X)$%NnGq;{wA?6>O5TkJ75?Mv{uby}3Od|0g9v-0A-G)vJV zv9CWx^W5pCDwca9!>w%9YMBBGSERVw-C@jY#ee}dc@1h8s0|mxEfdB^H0!*vObIe$qw$1O z@dSCx8@nL8xd6X6ID^hSpAZ3r?9I900TKTUlXOcMC)B(pi9!SX)vWIIfJ*qeHsg1m zl?ZRIw%Hi=)HGYFd>bkj+Zr*<61eBggzdnlr_ z1r{p9mXrR?ZplPo941vmy_hu?xrxAnH-VStXG?tamo5k6dcxbspozZm293VeznmGr zJU8^6F)tgswh1s&hEI1Qw0#|#TJ7SEnd%u6C3p>5`|N+3^I8q<9(qIE&_9E-H6FDJ zCf*}HV+oC?owFnD8_^N9n1xqqs`81(FMRFJ&`1fZNi5#nu+@l#429MjQ@~75l-u1< zKn*3Gw4$H5jb>6sUr>knu12b6f;*!B4h#@X=L|pI*6iTTTMe5b0o(Nh;K1P%Q^K zN8T_cwbTa*_yF3B_=Qxhj2%d#ZI$ z1KcU0mWTC3x*h_2P6=Gp7L-`pmWYjCI&M|di9)5IK!b+HIwd)PWK(D#p01Mr=XZ^J z+RjeJ)-zAFGi`4%Dni5sErfJ+i#;rk_=O5lIV4Jygid!0$I|)9j!O9x$(YU=&6-U@ zkaiR%nR0#O+?gj!+XL@o*Rk&Bo(X{~epBygxAi-~FZ+#g)#eX|P^{3chm0VS0aG4| z7y|6C9Bd6An?tZeN`(s8>{9M&J!@qr>}ENgxkpysTHc)(?LK;cU0q~EY0*tW=0GEb zb46SGLuX*pgZBQ%q}(l9J&X~dM(CYwm0eIBHD7&1p#Y$%}FapV41gdyZ_8r-S#$5M`ZHndQMb>X0iUrmcD)j57u5*oP zaZo%pNzqDIjzckSGAN#AeD$zB!i&Z&cMC^xuOFiX?RjT(X0GiL-pe~#WIHeiqm#T; zRIYznG{)ga%rBE=zk|RiPKWKri}cRIFMuzh&ktk7ZD4>MWPloJ7bhEiPgK_Tq`Ibk z`|9t9Hmtrk0Stq02U$zAY#-7)UPfHQ{_VmJP;F)20~niEhX+!NQ;jbL){-7gmJ(lP zCZW5Qm?wR5o8pZkeK}!?8IOF+D-O#bV~6&$ z!-4vPUx3;Acu%9r++mI9{&D}aGKW1s#y1A-OAEB_#Y zGFpoYJ!#nEJ4}2f!~*SWsDWk0FGOZIYH6G9Zsv3Mu)*L$P7IjxMd;d!Gd3d1!G7vo zcPczlXzl?rn(4V^E4qMw23PI&cW1%M9ksFX|^5sAU50?J3B`nM`J8%aN~khKu_ zEAkd(BFv*AS%UnDIy5NRf%?;gnOsR!R59x>c=Ut%2p=YoQJ^)62wxLrrAUOQVXT}b z@fqN9E^CC`0rm%M#^x*;0#~k~upzN8NU+?drpg^S521FXrsT_4-Jc%6B;d!l*DLKG z`+1r_dSC0R9p=m#!nk1OabGdSS6Tjd<~SBtU(fVM;o`8xN3(8`yy=kA#1_+mt-6%3k+#+yO!7Z38tNic#*G`1v9jZp}{(73IV zLN6Qk$Ht$VMe zhv?`c!46-Iqhnxp!uEs@gp|b((S$?p+g0=Q#FJ^FqNdCuLl@wXG;Qj-JGc(p0Z_E|G}^>hVMs@{ z_swRKrAS1LoLG1>Tv+4@!0}X$vYtGGp!RG-3bL?@g$2DRg&Hlg5H!fBRYoA&n1SLF zAA2{9mBv{?Q<5XptEClvH&<6*ERvMHc7&@w^W0gs1|H2m6O0%yuM`Lh>{Ok$ZcoSC z{=SGEHaE{vb%asyu8}CPHC*r+dUdA_buc7DNyx%Du>Jky@4Rj3@!FF#z8&YaA^O0# zOe%gFp+k%;WO1$p-+V&nP9Tho#VOcY37_f)M~qpNoEVcRHGW}2F2WTS0>mZ!N1Q~5 zqyw%$0WoDWc|hDMWf^4Ovn4%7jj|QUD-Y+AAzUflRoP0`$~O{z#SS=s0UksK;9B|@ zxPT0C-`0zEGTi`@|3OPTpt{N?KPxCXK|pc4PZHWjAt_0?5$OtHA(ijU$oGSc$<+c~ zX=i5U(tGK~heFL*B$SSV0zr6y97^lythJQ{}B+ zmmN{UrpfA{SVD{4z)-R7%3sgnHwi zRh=ce_0w5*>!@uJ$3ZGUtPY_LN8Tybt-_3cZ&rjg*;J!c9b*Gn?OT7@Ar!vVQ z2ek0cXwhM-Ysyi&iD%Tx_R%~C`Du!kptnHVH}US}5RN~T`GM13AS4Pi;u>%e#Now# zlYIId&ph&O^B#DT+ydOwRXTHfn;dj~5O;^Jj3KW;^MP-J94&Y9V+X?IW7E?fyA^W+ zicUuw&Q0+-V&)D!DSQ5Wlzy}zQW-)Ak{36Z!bLrXJQja$ezs27F16th!V4w^X84bl zD@H#7KfqyE;>RqiKD^Yi;ZaoTOi3T?@*O>|r%FQivFtqBk4y zz$+R7MWK7#v)wH#+Xzx8D9ple$JpmpVURfw7dB(Vb>ff8O}5JmY@UeRLzYWUeHHfP z6Vwi()sQR<#FAj+C|AYEs2P%AtC_Jfg3S;Uo~9HdQoHm=goTYpR5Sd=btGFv-zE1a z@uLc~`X(Dl>v9qVF@_Dl>G5K0&e*B$+GDaJe1!9X^nY2|M%~pdXY@>xrot&-sJv)8 zuru>~4;}}#mhXs>3(i4}?xG^!7O%01fy3La>NqP>5z?I&QqCq&QD%gsBQFs_G5TXf zyg02Ns}^_a#J(izv92T6RkLELT{XuNtWmGCkDhL#)0pV15KI(mQ$|9IyU!1CAEa6} z0(8qPgwLgC;o~>j@>vc)S_Zoy%>g=O#z!DoX5eIN%Z`4K7s}rnLJrjLlwiKNO_w%p zX(~5uY@@vyb$FW#!wL+6%W)Pp)lxZ+6*I3Lz~U{mjBLS#)@3o1i!S4IoyREX`qY%4 zxMK|!?uS@c(Brc6>UeQM@am^3xl5<}c!hez=y%ct?!JUFF})4Hx9wplbUEPa*k>y3 z{45?yKa%GE!Xqz%*$S!*PER=*+A=*BHjK)VTwAQ6&Er zPz%+9i2;f@045|x?M34*1Uu-fl7RbEG-;4Pd4h|{AN+w|IjhDfL1vp0nQg(hGA)Q= z?Z{JPM)qsMfmVZ9k|W@k&Ra0+MMe6UwxewI|Crnj)gqPBp{XbHS2sZJC7eM;J^@2! z59KB;QaAu7w!O@DF7wS6r4{2}c<3PXKT5&>S^|+{n|!^jw+!&ttVMxR(XVzr)b|j4 z+zUtH5|=D|wy~~$#yP?SaF*PPd*_dAtzULr;fXS6Ea~lx#^1=+o{m=AXcf{Vz>V8@-V!P(y3ehLdy^CK2}+gEoiP< z6}}XxHgcyd7zB>}Q{ks`gT?C|tatO#VEV|0?pwPo&9Nyc{+3c&bJW8JN+l)DeH|l% z(Z{%N>gBqO6eDx+#6{)1R+5NZ7|HRQ;Dc_`M@g7S*pfukrc#mcmvkFJ~^+`_1*9zAMlN z;*s}Jo@uiO-SzkN+lJ%b$NFAc)l4>ooPZ`iwD!%8N%sz~v^ScmH8(@fUCRKym-ajHnV4BjJx88Bw>%|*Qvr|7 z>-D(~c7v|11CMw8kmKbJw{M9Ty%&Wu0&%Af?9o5Ympk3T?yN>0^MCkgEi3kCs>mui z{H=i=J797&pPELS$KqZ(3!GSQ%Gddj`ZExWBnW}vl;I~@H4 zf8(>Ggy+33PGu^fc?k!mg91yW(-fZ>CDa&i1Hrv8y$8bOn2LgjRS_CMampJ<^g|KP z6Xk4-tp@mj)DKODYC3G=#&jC`?-#YXz!H3WK^W{cyTn7XNXY1dPjdu(PN#zyOV^XJJgN;~ksm zlv8FGGKMq_FJ0X#!J+r3H?dWs@D){Jk8Gzj`uEQv$ohb`&aUrWuiX#(3TBfuvQ#R2 z)3LL#xNx8tEq(?P)X<8)2Hle7SY#g%j$zxpIyZ9A#%N-2f%XBY3#(#2Zv+nURzUDsWJ`1+ulpiOC zej7P)xb{Y^Wyx!VO5SpU#|j5fcjf9<{YlQq%Fy<1o47>;%V60q)yT1TSy%_7Yu@w% z9zBQPUkxOXl9}o{^5O615v%P%%2R_HDC`rx?w(%$)U>eqrcI$%;Jsuh+C@2};D&&p zc}nG~EFZ3B$dJ!u)~(Ulz<&hW&N3q+s9#`F5yF*$Fu8M5<>PHevdRu`lCn%Yi9~6k z&QuPe(4I9O{UuzlmWa|~!Z4fJ(sZ@RL3&Kast>FGuIa`@kGKfMCQ-{!PB_n6{`lJd z9hU!RB_R<@r`)w3wu^6ogy9znHrj-RvcxQ$xdh7?YxW2>Um`XbZkex6V@N$elFzFQ zW)vPsHd(R?MD*=XVo>>j-Z}3PebIvnK!fuSOCqN*UPmjsnh53!v}a@rPzX$&G^{eg z$h{Q%j&L15I#?oEdtYBd*!uGk%Z||8Wl4ZvmYkN38^8#}3koj)9f{GHhDB^`5#61P z6;pmiuFAp(CBl~Ba6(%$x=Fuq*+(5e9i0Q=sBBGUIbp5jq=WzxSFwL1n7~s{Rx~9t zrL0J-7f(IM3JN8Q9bv=U0I3Gyf(snIFe*%8pE}%#M$LNCO8;>eqz5bdfR~}$!-gs`0cg+^zB}9NZ&?I#S`nMS#3 zhTIv>Q8vI<*@9{nFl;dHo6|PfVRe@m75XaJVPLAV61G|&+#g`CXYA=8=5#B!O1s26wSAjs zTc({;LZQ=F(=#2I6L54{Cpt&V8!`V#+ua&Bfc5`fR=t&y6<%drWu+n8@zE&S(BLn3 zbWfsgDXnWCZ(VI28L`mc(vQ7_6~;~>IMZ3tfau)Og_0C99h;#H)a2>E&L+*;h}k<(IGwP+CcbB5YD| zelea$!?1kR3Yax!hd>b4K`XAI`CDbLSL}y1~Q&$%Kb-Y1-P_ zMnSJ5j_MJfLkwTxGH3@yJ9qGmc{ULEz+icsXq9akn>K1QClh^oMm&-67G3#>vM4fh?VoaVPx8F3!OIrs7^NN(lx9u;W1ZSHZVgHIctU z`ML4eWX|3?sBJH||MXBXs`cp16h9(CzTg!D5^I&APgUQ&a#QY_ZU};nPi}&QU)Ix!Hd9vEM+PUVl>f;?J zY%11DO24h%Sq-O&-InbG+B2|g=jc%GRqYUA9C2r{6u(@uUE;lAGLDM_4<5L?cg|?$ zoL+g98iXQ8ae2@B7p`phWV=I%Arm%l&C9?z-`L=qWd~+R##`J)e3Ecjcr*tQqg#GT zZ)$srPaod)-WF#KZI!LohNFo`f3fO;Vu%M|?!&US==i7YCU%>4JEw>7#ma;1E_Cxu zm6X0*#||@csoM(n$RDH+Cd7?*^p#r8cuQUM;M=EQt&jX19xVSpC0XL->$gt7M9)Im z8m{WQ4k>1e)o8>WmWW?*K0Kuz$^(}MD0>_h+l&^opmD&~bT;jD zJY{GCqzcQza*>FWBZOeio2v!MkVTNw@^rP+U~z2nL@?ed%ifTy67t+YowR;eZq>VW zWXO)-Dq=gk%uZ76S-WW&9+Uddlw~)gKaeISj+jdbJMt0$z$cS!;*HlRLUO#XrdvKo znkSGKJ=N-M$3kISlI}zhr7fVy#~jA2mQuANk;_t1@NUj=&cJM!%vsv*f(acat-Lm! zt)O1-niZ`#t2R9(oPp(~Zd;^u>#@cv=TBr*T`>9%@aZM_JTX$_w}sUV6GVJjFAa3k z%4M~dE+eJf!2q_fafe%Zop}RNZT&oKl)BPe1OL!kRtfN%Io(oW-TnB!c@_+G^fCP_ z$mh_96z9W(_+-X_nwyxcF;>%Gs zTZJel(B&+n%h6!PNt)*`q2^BS)ocO!_5fYuk$D)Ks1UCJnpt`tsV6-bmJvt*7zZ=R zHu%)~^Op@83CjL_vNS@{9k^D?x$CQP112Jnh{?imB$X}+#!KVTSd;d>{mH_Dpnp*F ziu~^HqjP0!ghPy{Xo-{wB3n=uC+EnvoSLSwt2-Zm20=xQ1b9hUGTy{XyHal&(T4-G zR;?)Y82(_J#uN<1E$gyfQe9kl3Vgn9D;=Y3(m6yr822oV$tnB1;3>JBYxcKX+#KfrGS51_%MWu*74`J}-a@{ALVuZA$6laIeXd;jRa?eve8B^)Pw z5kf8|SME%Kfo8&?+(Uv0OjJC{AQUWJB4H2Ef=E7Y*28C-9U?uhWSZx6ti{eWO<2`1 zF;-bn7@1}Y&Ho^YtOS!TJ@_bdGWufI z>xG*aLBz@#F@wg2npi|6+7AK4f#i!KznMbVZ!k$ftEzf$2c;z>L)uX;R*_-D zOD=gpfT^rE+pH!Idu+5Uhpp{zESWW3vF@W3bq*zkI32Pg9XmXyHq35E`plxmHQ+{; z54SsKX)Suqy580BsAJOC-lyE7{KLSl-YwzmW_jt{>*)5afh?+0t@F1=j5tM5t%GiV z^dda4*+MpGdS*pCf4*MrDg^psrJ5LuJ(G)*Y^~&BiwTZPn+D2A!-~lO)}1qsJXmd< z<&bz^<-(uGc3Lg99^aN%`)g_xpWW)O=JXW@TIrb5Ruo!$RND25ch&kFG&_}Nn%E{w zdmPIp09%U38E&6wx;K(&?e?B?)3&F}m0Q-ZRGd7+^*I)ARu#z;wl%Q^0K9d9h|(eJ z2fY$p+YTLC1T;_)c@OT6=TbG9uvb5HW0WL))}C~ius(9qDPd3TF^9ke!VWd|s?5?G)Y#^nnn+we^iG=r_3&++1-wGDscN0fId<)rsY1WU(- zNT9_^xWTOpNqkbxa2eZH#oCC4>sTJiMX3Q^bdeFhK}z z`PeWPn#Ssl+U?qZHL=Fe%*vUx`xpxtoYO53cFlSU{D<8LWD$R}(w8CGLvhM+J%`CwLexvig@3*&4%}~y#7`` zp_Gtq&Rx~~(o-@gKhV(1LsDH23gOquKBB27Rn zM6!&iv)lxXB;_LOGK|0`WRFTqsqy`3o{KLeAo4!syuvrGxQ-+-y!gJ-Kl&b*9B%^b z?nG(jqNg-%hgUm4(MMJcf8Iw$hzI<s|@274ta-BfIC0kLj3bzsI zcVm_A4(?~EqX^SAGeqRH|L9Rktn$%CJ4QJsG~b2oFE$5-kB!sxD|_1=7nmeTZ4aA$ zE}#kgnm96Xy2Y^W8kYJCgo#KG7`G~u#gW4OQAlEz(vU|fzyhYyfCz1!aP>2}S5~r* zmwz9MMx$;6Vq|4!f~QDRmNz^GYsILq9T9F)h^VXVV#iCMWi<_cr7K-o$+nM<<;HaO z#x&<&^=za=(NnnAA*kj%WgXDbibBvB$XGcW*-= zzn?+k$Pq4-ybh+Sls;apVs{(A^kc~ab_<~19X7)3#Pl_?*({3mR68BMHbhYOUfUT`1FIbVKpo8pb=1dbZ zg(=PBUuQ*cKdwaTfR3Z;}oNXfMCFr z5EFi!0<6KvGg5{B>w823?wL*N9@cqx`Hsvt`Vrs!T>!YB6;F0T=X&D>U;0SnYyP^w zJY7|aCabP(8-uwLeZc|acC8cORJ^*4%@F@mzb9g=q+`On*;dhbEjGvE*1FGqK{LdA-i9 z&T~7ifAKC*b5-lP8YUJly;cTzq>vEOFcJ8*x0Ur#kBO3!S3hLh)e1~8o2N}MNHr|5 zQII_PuN_Od`O@;D*Gc{vajtPl+nr9Pl&VRX1^SNsa2&*G$Bb^WHDeB*U75q5Oc^1 z|3;cSsr*Wp7MuuJ&_EVYE8!2S3Tm(w@0J=SN46BlBU`9jAB^taY$1y4zWE*f_qm9m z%@=r2hS)$Wnd05E_adxx=qM?nwx@!xLm7eXgtT%rKLlbpVkjkv$j_Z5@5-`lJ?a6) z!w0jj(}?2d4fx?Kf56*#qssKd;Ua*++{_!^Rz{!o`dqa6v@EnJxBg}9EUiuNxm7X1 zNw#ZN4{4R7EPB1y=%{9N1a@t1HX$%|rq#~xbl>!C^dm5aQ7Qm%`Mad*S4J*D?XQA0 zUZ-^o+Rf?c{zjaMd0*qbU%Z+eY#n+F4*>XqccWJli?P*9LOt(*pC@05}2MR(+(dYx|dB_^%wnwy5yX!<(qx z$2Ray+Udk%MGf2<=ImE~f=^!=FwurjtO2gobkr!O$svW9GcWcvqaUH*9Gk>1!A2na zSAl+PM)lNHG2SMtnaYlJq6Sx0Lma?F5fDV$wiQDT5GXNL^`Z(0$!Zt5KQHab8B@Do z<3K!gS~_%&<$?19vX(!EqQVx~WayiIZ_KYtlE!yViATn`#6wS|rHP`Fsl^#Ldd9eUO}&&$ zFF}>cQq77Zp}QpU&H$DL5e!u@`r*jr+A1@%bX|KYoU|~vYyhjo@UO{1=E|<*rw*35 z5<$znPM;$18PAK3ZuF%hYkz3u+=U%DQcB&e z&R$RW3TEU+X%%Q%NXJwj4Xt-+G7{1xr2ii44#)F`H1+eG&Du|+*l`P{SzpcW_U{yK zjKG@}jK`aSv-2jJE3GQqD$=jfVP*hsFv7e+G@52MJ2ufbeLEy=Ud+Rjt}4+=sHC8i zR*X8MP%o}D%#&P_T%vwUb{K5LJk#u<$3xAnU6>TJ*A-YpYgul5IgiO1_6yQNwf$*t z$&1%vid*_aR1Hg_FQu4quXD8%4?Kp7z;TY{#uWdO664)jO1~s^RDFi=`de=6dE$f? zzr^&*yvYKS1Lb%DQD30B#$QFfjOr!|z{N}u7bOi2;F3ZSQ5$H@e#|<#B3qB}vaNst zuX69ep?4G&&O@t(x?KpAkkJ&I*$9*3)rpXK$7@`p6)O?j(7 zq)XB7k#LP3#C0Zx#mF^}`uOkapHW5g*H!q9*^TN59}UNB*Ii1%PvZQoyS#eU>g$xY zUg3=Q-pIYo62Ll{rS zJz6`=Hd)Zmpa#ipQt70^HNvVWh@MI%tx`6UU$LWw$F+4%a7ie{!%V`vw6A82p+oI0<*`KXuxfkK!IT@0jBu(U2=oI(Kt$VkEFg_K`Z-% z7Y};P)L<=t{up}wh5y!+PX+_SBgQlOI!JP5C%vjq%D;Y1j5Jk^w1r7z<$63cjxv59 z|1n`cGFhJ8zT?x_`_b*~cs)W)rEO7*@C~$~fC#soVex{5f+X%wHlRv;= zQfW5Qc6svxxF83uV;x}hcete$bf|YY2#oh^atZ zzPx4yF;@GmK|_aqeuG*Gd=|RwFrXb>F-C3qw^nprNY_ZSP%h)l;;hGv$J~n^O@Ms2 z;(O;=!0m4KZK32yM5$wE-yiSg5C6MFH!a`wr_vAVxuxBt?1C^|AcfYAT2CPDV@uLk^+*7gmf>k z)Y40LcSs5@2qH*#hosUV5>g9Dr_%k~#mDFKc|Ok{zwgY>HFMwlJ~QXs_c>>;Yp&VX zm^Jt2D%S8h6s9LXw(DAUU1cg(PN*`74HYqGRz(QX$z9fIz>0c`oixe}tBbw`0AehU ztw^j5zgksIGjT5@SM*vOhs$*`L>AR4I~r~?GNR)rWHqxW<_HwU(WS&G(?lH z#4)s?EvBQUg=vI&r*Ia=u-FT^ zK89SMZmOuh`WmJdT~h|VSpRWyNAA9*ozZSY_iKB*3=6`q7qVY+BV|EOMuQ)GO$3#- zwMh&0Qe}S3vmJ(+a``>>Gh^d@w{3~R-L<(_eL`@evf{onp`i5TA!qMWc6vD8FY$Ow zhL+o~jzinTiDLU^x0SQ&7Gt-b(w~c>_bDR+L3Y^DO|%lIht5VS)dIX;W%K6SOj>*gFc4~u8AG6=megP?2h2Qme^hc zPGStDMSCn*Q?SkjR^6Kwa=2v$QKdbD9gy#QQdFftxGT|oKn!@Y$Rhe!SYni$&@Bw* zT6^NdFD>aMcH53)F_pcj?U(otMsHnOXjIWZ34pfkX5ZeBZLF8Io9JA&6cGvXiJ3;< zkeGnX#Sv%I*KO)fhRdfnQUKpf+a|AI*>l#m_ee)L3F46!3oZmboz`N zq+Q4ueu6n{&$$squ_Ghy2g~RN zDGbp>{chLt+HzPqcbWLgS$$8Pq1B4@IE0>TJlYU+l^A{${^rht_v0$|n9=+@f|yr& z7?aMRs#3}$A7{*lJKIt)w3eChqwJon`Js1|BSRy)YnUe(xS?4CDecpxXhn@!QN5yj z-dRqUXxFz}!+ayfnMBnvJp&*n3QDy6p;MhR80Y%^4l6Dz`r|!zMzxigW#XUd&F6pI z?r|VX$DUk;xLVUM7*dcLaTQYqep_e9n)?C%8J6v)A#b3d-%qaFYSqF-hQ&dvi>GBU1ald8s5i3# z*11R%Jv78yl)lZd6S(l^XUg8Ibsz47ZN`gxUQ|b{Kh?^v=~|=bKlyh0g3-dMDat^- zooO6uL2qk#ozi6bgUg^MHEnZ!2mHDV1W(%OjAD37D>a3B$ICY-#g4>|=BHLCCnjS- zA&O4gB}uxUY!~eo4VHb{n?HHK!?QBC7P6)vd)(uBDqE(^y|lXwN6asaV-+-NX>rd0 znpl*=R1uou0X__8gnfO#N*9M9HZ#@z(-M?iHLof$xkK+PrGafoJaNv|2AEIC3lWBk z`dja}mdWN#R(Tl8b@%XBwDmf$1v$5v*IeToV|iP_D*V2PEjQ|YdKG)a?&o~ElR_aM zybdigG8^Uhxup8JUl3Fh#qx+Fkr&W(H|%Ol`c9}dy_rt{`OYm<87QX4uF;PF8=ro? z-YwZoy_^*?=Z8_}FoB77w;?0Dj_NJznZUJUo77WoFZ_8Ty(KDxHks7O)cZb!!aG#N zS*kOk9zS2jN5H$G3;69Psz-O4;zl|qXiNy}sL8`jZE#z=tyC!?4`O&-n^Hkt92TuT z3>E}g`1Eq(G83j0t7x#i8V6_Q7j61VbZ*~m5G>*yM1ag)bRS=j3(p`^>$%}A?8oQW zZ)7R&m${~icY; zzY16Mla59=!!3?$!3JEjLErZCC;4~3*W!BzDoPjLMf7{y_e^xJGTuGX9qCJzoX#5H z_Zy^i+V|U^d*VQSu=g@fl|YiEC5$Vx4yXE4ZKM5SUihr#b|;W!JWJVfy)&oHw^oHP zhowsTYgf^5Zk;TmoO>|`5bF~R!<>H`>VtQ|7NNfuTXrp4*55f|0*10!~HD3b|e8g8wY4b3OzqSTfL@(s%YFwSJlLsMQQtfd|oh%#2a0p0=>+o1(#-& zcoOC>=!r9V2@5oLCRuxa-X?oNVQC#3nCyhc5s5Dn&_Yr(Op-r)B3&lnbBkt1aSc4& zWgsbzeZK!;&BDm@g7gyKxpF?MR-13k=Ee+Q7A2}=Uu$L@-sZ_3$fCkZATHC@ybtk$6CMe zQN6baU3F%cF5h9~qIe4|jmZd|ko?r(gx?TK&X&GEB_gc=EY9<)$-@`qJ`GK`s?PJs zV@XcZ9MehBG!IS6Hmz(|z$(=l#oJ3U4{m&&t%?tO;TG49?I+DxCz-bW{jnl-`)u!4 zHb@t^n?C!5fWFr7gDpX=eo?Vv&ZW*hKM}u>jD@+&Nf4%*pTnTt)DU0UT}W>qbs@8_xJ>g<$PZ#R}iHg#kK91qes8u*U79Ff!8K{jU`b~Lyft!p-4 z250Piua>6%UYol=Q^=U$>hg&{By}xgN8bt2Roij9hFb%n-_NO^DSblWmMwKAd8WQS z+qE#2n|sa&eufWcm}}asush*-t1rFQzA4o~AM0VZcLK3av}usf~baz5JqGhwCk`QH#Xj^Xy@;kHY1)J-fIk=n7{3Hy|;z~=hOyj zx63WJGGdT&fC4S;z47T4-iKIO;|1W_?#X0F^RKyQb2V4D69#*NvG}OOs;IhM^MQ8tXX{W1c@q*m7yVLGDh^-$(I+_3qts(V3TizKBM9AeFlTiZ7y5}g1glhA z3)z_;D|}aaNY5y3d?i*!mBv?K6#>SZ{Ba@vqC%9%|9!A$qYKPD^x@YevH9zY7mzE8 zq=XIEa!2^V5n}XWocWY_pm2~tU~)8{DfDQ0KS|MRcCzT#solVI=Fks0g?;{<^oio- z)u#%%zsSP-+ZVV841ET3VB&CDR{F*50mf(~U(6qa%s=}z{ULdsQkBb-l=1UGh%y6_ zl^CaBhywn0(RbkMbtwhoOsr^zX^pU7rh(@#o$R`sdO}`?v?rA)Su_lz8CnhYX{qbn ztw@OHGw4Nt#l{n|g_-90DZw_f9-O0q{V7t5(G{&Shh)x&c>}hZV{Gj244?KUbL6o$ zwAHk&@YwRRG#2!`wk@#?fkP^>6IJ$AjaFhAt7WEhbaSR(6JtHN--?dk2ez+u)ST%S zt^44Y+W5Y{(*j>N=PigD0!ss7vP_v%*jj={c5s8v`eWvO8*z?z74O#acBYU}CmTTNvD$G(ois-;O5tyvIs%r^Q-?p_A2)dN#! zYR9+|sqTJ$rgV0>(0A^n>D)vSq;aO<{vA;?%<%(XWd;M&0J0|9Eke7q2|fKjw%y>xupJefW}O;#QpMGUz#j zR@US)S$I!iLxmN8#+bXxd3n9)SA;g^6RJ5j^Q2G(PZw~0lUn++XMVoLf?ryfQM{ts zTb1(TTPNlIqA3@cXZl}e4G)Zr`i{Ab4;b1ptIy%|t};IBTAbJLUW4nbEB(>ylYViq z*qe)i{hiAg{m+_yL)!Hjv&Yl7rZ>&u%06euJD1?|?H-+JiWb?Xwg%29W*gl}K7u#b zcoii;m)$|bkMif=Pi zM4j-yxqIDfheojy(iN?a!{dMO*eJ90k&n6(Q~L<1^B#Rcbl%|t$dL+Gigz7+67LvO zoCBvN14bp$ue;B$ZE>*fOxv(T(580)O_CP9kC0EXA9oRrn(t)XmDt|} zQk#1Cb4Uq?oZ_xHhmb&sMQK=T@b&qkr`xBeT`d;EDZzL48W)GOIU8W|dK{iy6xPYX z+8m(|dZ#o#Z?-8TD2$Ak7KbJU?CFF_v{DIPV(Iw#RD@M7a(s8ES=Nu^Y!kMpD>E{i z54u!j>Y`n|vm!UO+9h9uaR&PNZfxGah4##4aze_Nz zM{JDw38n;gDx0Tmdca6`%d{W8T&8CLhA0=>j2$j$@U6r@GS{hRpr9ZaavZpT# z^FEnoKY9dR*v)WM33@l^`6}OLSz+s}x{q!rRkxs9(&ZO0*Gs$C;{8@!1*6W_&5PWL z)0F1H(gTqR#%zIdDmvEp2Qq~wg9N-5zWw!LD^II5`g<~Z@gLeUoj%W?Z55ks``RAbQS&#w9NRPhs8KH#SY>hlY5 zNB0^8oQjCyWBJ_+aktpuUkoXSL+&J^1l|N^w-2Z(7SPvrsp3C`UE99)#X6lxo0q(R zmZNLiB_BUK2lg$u7zfF!ZWhX}#lF~N=U}qFOuut>dz>J+g<|EFEFhn^Vl8>~Oq6i@;nQ+`T(A5qiyyWd!spB|>e2#+rogLh$s7$1i?{wM#n}|mj2_9n3&%6~A z=f2W336E9An;&Y}!p{!TeubQ=uidl4_9T2VhY1s@Qr?ct&}hVDCWZ+F^u9~7YCJmM zphyL#xIO<_>4F;1{?iW~o7K1QU@0RaYK&zou zz-c5d?40J@QLeaSJKsTF^wB-y&;eryHsTd66$TE%(WD=aZ*+a~JEcbNf3*wFH8HS! zG?Q&~xCRT)O`zNQ;bEJ=94c~HV^m=VpD%_Iv$*uMQL14LUNc2!l+blpFJzPFv8Ba$ zta;ahy50LmC?DsNFnBX&uE<|nB{R!4pRoq6$uF09H)w1YnX<#z@;CEmYitx@7fw&} zICf7qRAv)sF}{6+ZE!Q=sCDN7=ZkigHjUHX!sSE?pdWxcE3bQ!em?b5NdqLQ4E*F~ zk7adD%=87lHOS>{6@X-6kdVEqoE=0Keywvz@wwGB}`zCRjUHkvBFE&6SIf1!0T z&*@^U{*_#9iE%OnM4M!_7NpFAlbGGFN{6_0EzV$?d5y!z9n%B@C_>@pRh?9EBXG~f zY!MI0HYc51=|zi`sky-UZAep{*K^v)O`NK_TAqYUI#}||1D&R}ituc8M%sIkv=2t& zgM>(*Qw}M?m{Z0Mw@mc)zrx9E(gR#n^(_+MODYNHgx`{K8xqtf($=W8Upc~9pA+Ze z6OYTofXNN{>KZ26W_un@PVYTU-snBh^>%sg!KF(zXsGe2%AnX~L&Z;TpnQd@zU~h-+AFv5rRs6g=jt#%`m`9j*nZhTxuU%KqloKtL+Yd0^ADFj zmz7uhKC~eEXZjJc=9dhoUF7EX;hhieqzq&RbSG9Ms*T0d%`%Cp@v8EwF|NHaD84%q z;g7#rmrBtSoCOVa3G?CQC_4B8^*on7LwM8{doP85RMYVI_U+@QhUTp6H`iI$udaRO zf>3sGqrOQN8~z%y8nv1TvKlg1W;H=oK{e(Xjk%BGA1%L0BxzJTg|sZA(c~07d_HDd z+x6{ZZ{4HUbvg@Sg#upd2=y}dyZe6s~g;h-Bh(}d)SlheCl@|t>*Wv^7=w~c8I#txu0J_R zu{;pl7CjoQ+h|u^<=5fcW*>Iy_m>`39t*$WFB*-Wlhs)QWA4Dny!a^`J1*;)A3I+LF(2bT1Rqgwc8(bS*x7V8pE#vyCs9JYN{g&X zqM++-K3J%ROPD`&pVyvYn6Qn7_}+o*qG{(I`j?uS6DY0`b?B!qDwe*ZMo*d}X`Y$j+#662$nH*kY0y;Y@yh_f!!6e!VE&#cFh< zKfP|zgZ*ReI9-l>=y}dd-@2o8t5$}5plw%(ayv_r8 z)U`J9;PfiYa~m|5BmSMtn+*MxO@2YppIq{y(7`A-idFm@{)-|LKLtCPIzsH7p^m8R z-@uAhjBSzRVM+N{nlBXC)giWEClxU4CDhi~?uDAN`rk5HV_S%gJAmy*0YFLsH=2jP z1vbVONcu3}-)gDfsA5h*Az=U~KaUU!RxF4lE%Sr^LKe$A8{0rkCG9M1zyO|`Vv$`ZFsma0$f%gzc%_V;z&B9m|4)n{j!w?fmd1{M!k}dQ zm(9!fyG8$uKj0=qzlWBL9g<`Zv9kbhzk=9F+Bre~IBqKN3S#DL>GT))88sgNto;1^ z0Mz!!{=FCELGq@N+kdP-$N#+t@}aoRs8tBrg9HUpEb4!*D8(QDpZz}-e=Gq3{+lcQ z_x?{E_<*Pc{H_lWdHkn#zdbiC5C#EIsz2YL&Wbv|L0cpHE>yz~f(#EBfSR8EJCpzJ z;V68z(@o42A!ewEp<4CwZ%DQaim3fxq-x&((7utNYvd~gki!DNW@-#^gaUZEcm=qG z0c@7e&h{eQNX|FXY2j#WZwWDV;(|I_JpA49UpPX|TuhO#B>2A<`w#z{7nQny_C;x9 zAOHWB z<>MCyA`|*QI$_YibOJ)4e_caBm=BrhKV_XAjgceY@n+^zgLr^Zu|iFDFQHIm{!uai jJ?iA`%#nHi8(;3^Z0zWK6J0(5VIT;fnOWw!EdKuk(sI$? literal 0 HcmV?d00001 diff --git a/ComputeGraph/examples/example2/AppNodes.h b/ComputeGraph/examples/example2/AppNodes.h index 9841f3df..28f1a430 100644 --- a/ComputeGraph/examples/example2/AppNodes.h +++ b/ComputeGraph/examples/example2/AppNodes.h @@ -39,6 +39,17 @@ class TFLite: public GenericSink public: TFLite(FIFOBase &src):GenericSink(src){}; + int prepareForRunning() override + { + if (this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run() { IN *b=this->getReadBuffer(); @@ -58,6 +69,17 @@ class StereoSource: GenericSource public: StereoSource(FIFOBase &dst):GenericSource(dst),mCounter(0){}; + int prepareForRunning() override + { + if (this->willOverflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ OUT *b=this->getWriteBuffer(); @@ -80,6 +102,18 @@ class MFCC: public GenericNode public: MFCC(FIFOBase &src,FIFOBase &dst):GenericNode(src,dst){}; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ printf("MFCC\n"); IN *a=this->getReadBuffer(); diff --git a/ComputeGraph/examples/example2/generated/scheduler.cpp b/ComputeGraph/examples/example2/generated/scheduler.cpp index d2c9a19f..1cb16aff 100644 --- a/ComputeGraph/examples/example2/generated/scheduler.cpp +++ b/ComputeGraph/examples/example2/generated/scheduler.cpp @@ -72,6 +72,7 @@ The support classes and code is covered by CMSIS-DSP license. CG_AFTER_INCLUDES + /* Description of the scheduling. @@ -153,15 +154,15 @@ uint32_t scheduler(int *error,int opt1,int opt2) /* Create FIFOs objects */ - FIFO fifo0(buf1,10); - FIFO fifo1(buf2); - FIFO fifo2(buf3); - FIFO fifo3(buf4); - FIFO fifo4(buf5); - FIFO fifo5(buf6); - FIFO fifo6(buf7); - FIFO fifo7(buf8); - FIFO fifo8(buf9); + FIFO fifo0(buf1,10); + FIFO fifo1(buf2); + FIFO fifo2(buf3); + FIFO fifo3(buf4); + FIFO fifo4(buf5); + FIFO fifo5(buf6); + FIFO fifo6(buf7); + FIFO fifo7(buf8); + FIFO fifo8(buf9); CG_BEFORE_NODE_INIT; /* @@ -183,6 +184,7 @@ uint32_t scheduler(int *error,int opt1,int opt2) for(unsigned long id=0 ; id < 302; id++) { CG_BEFORE_NODE_EXECUTION; + switch(schedule[id]) { case 0: @@ -193,42 +195,48 @@ uint32_t scheduler(int *error,int opt1,int opt2) case 1: { - { - float32_t* i0; - float32_t* i1; - float32_t* o2; - i0=fifo3.getReadBuffer(160); - i1=fifo4.getReadBuffer(160); - o2=fifo5.getWriteBuffer(160); - arm_add_f32(i0,i1,o2,160); - cgStaticError = 0; -} + + { + + float32_t* i0; + float32_t* i1; + float32_t* o2; + i0=fifo3.getReadBuffer(160); + i1=fifo4.getReadBuffer(160); + o2=fifo5.getWriteBuffer(160); + arm_add_f32(i0,i1,o2,160); + cgStaticError = 0; + } } break; case 2: { - { - float32_t* i0; - float32_t* o2; - i0=fifo1.getReadBuffer(160); - o2=fifo3.getWriteBuffer(160); - arm_scale_f32(i0,HALF,o2,160); - cgStaticError = 0; -} + + { + + float32_t* i0; + float32_t* o2; + i0=fifo1.getReadBuffer(160); + o2=fifo3.getWriteBuffer(160); + arm_scale_f32(i0,HALF,o2,160); + cgStaticError = 0; + } } break; case 3: { - { - float32_t* i0; - float32_t* o2; - i0=fifo2.getReadBuffer(160); - o2=fifo4.getWriteBuffer(160); - arm_scale_f32(i0,HALF,o2,160); - cgStaticError = 0; -} + + { + + float32_t* i0; + float32_t* o2; + i0=fifo2.getReadBuffer(160); + o2=fifo4.getWriteBuffer(160); + arm_scale_f32(i0,HALF,o2,160); + cgStaticError = 0; + } } break; diff --git a/ComputeGraph/examples/example2/graph.py b/ComputeGraph/examples/example2/graph.py index 9868d544..6127bcb4 100644 --- a/ComputeGraph/examples/example2/graph.py +++ b/ComputeGraph/examples/example2/graph.py @@ -1,4 +1,4 @@ -from cmsisdsp.cg.static.scheduler import * +from cmsisdsp.cg.scheduler import * AUDIO_INTERRUPT_LENGTH = 160 diff --git a/ComputeGraph/examples/example3/generated/scheduler.cpp b/ComputeGraph/examples/example3/generated/scheduler.cpp index 26710576..320a78ee 100644 --- a/ComputeGraph/examples/example3/generated/scheduler.cpp +++ b/ComputeGraph/examples/example3/generated/scheduler.cpp @@ -72,6 +72,7 @@ The support classes and code is covered by CMSIS-DSP license. CG_AFTER_INCLUDES + /* Description of the scheduling. @@ -141,14 +142,14 @@ uint32_t scheduler(int *error) /* Create FIFOs objects */ - FIFO fifo0(buf1); - FIFO fifo1(buf2); - FIFO fifo2(buf3); - FIFO fifo3(buf4); - FIFO fifo4(buf5); - FIFO fifo5(buf6); - FIFO fifo6(buf7); - FIFO fifo7(buf8); + FIFO fifo0(buf1); + FIFO fifo1(buf2); + FIFO fifo2(buf3); + FIFO fifo3(buf4); + FIFO fifo4(buf5); + FIFO fifo5(buf6); + FIFO fifo6(buf7); + FIFO fifo7(buf8); CG_BEFORE_NODE_INIT; /* @@ -172,18 +173,99 @@ uint32_t scheduler(int *error) for(unsigned long id=0 ; id < 25; id++) { CG_BEFORE_NODE_EXECUTION; + + cgStaticError = 0; switch(schedule[id]) { case 0: { - { - float32_t* i0; - float32_t* o2; - i0=fifo1.getReadBuffer(256); - o2=fifo2.getWriteBuffer(256); - arm_mult_f32(i0,HANN,o2,256); - cgStaticError = 0; -} + + bool canRun=true; + canRun &= !fifo1.willUnderflowWith(256); + canRun &= !fifo2.willOverflowWith(256); + + if (!canRun) + { + cgStaticError = CG_SKIP_EXECUTION_ID_CODE; + } + else + { + cgStaticError = 0; + } + } + break; + + case 1: + { + cgStaticError = audioOverlap.prepareForRunning(); + } + break; + + case 2: + { + cgStaticError = audioWin.prepareForRunning(); + } + break; + + case 3: + { + cgStaticError = cfft.prepareForRunning(); + } + break; + + case 4: + { + cgStaticError = icfft.prepareForRunning(); + } + break; + + case 5: + { + cgStaticError = sink.prepareForRunning(); + } + break; + + case 6: + { + cgStaticError = src.prepareForRunning(); + } + break; + + case 7: + { + cgStaticError = toCmplx.prepareForRunning(); + } + break; + + case 8: + { + cgStaticError = toReal.prepareForRunning(); + } + break; + + default: + break; + } + + if (cgStaticError == CG_SKIP_EXECUTION_ID_CODE) + continue; + + CHECKERROR; + + switch(schedule[id]) + { + case 0: + { + + { + + float32_t* i0; + float32_t* o2; + i0=fifo1.getReadBuffer(256); + o2=fifo2.getWriteBuffer(256); + arm_mult_f32(i0,HANN,o2,256); + cgStaticError = 0; + } } break; diff --git a/ComputeGraph/examples/example3/graph.py b/ComputeGraph/examples/example3/graph.py index 17600c54..b3edffe9 100644 --- a/ComputeGraph/examples/example3/graph.py +++ b/ComputeGraph/examples/example3/graph.py @@ -1,6 +1,6 @@ import numpy as np -from cmsisdsp.cg.static.scheduler import * +from cmsisdsp.cg.scheduler import * FS=16000 @@ -53,6 +53,7 @@ conf.debugLimit=40 #conf.memoryOptimization=True #conf.dumpSchedule = True + sched = g.computeSchedule(conf) #print(sched.schedule) print("Schedule length = %d" % sched.scheduleLength) diff --git a/ComputeGraph/examples/example4/CMakeLists.txt b/ComputeGraph/examples/example4/CMakeLists.txt new file mode 100644 index 00000000..b329c8a2 --- /dev/null +++ b/ComputeGraph/examples/example4/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required (VERSION 3.14) +include(CMakePrintHelpers) + +project(Example4) + + +add_custom_target(example4 ALL DEPENDS sched.py) + +sdfpython(example4) + diff --git a/ComputeGraph/examples/example4/appnodes.py b/ComputeGraph/examples/example4/appnodes.py index cd85148f..2e2a85fb 100644 --- a/ComputeGraph/examples/example4/appnodes.py +++ b/ComputeGraph/examples/example4/appnodes.py @@ -25,12 +25,12 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################ -from cmsisdsp.cg.static.nodes.simu import * +from cmsisdsp.cg.nodes.simu import * from custom import * -from cmsisdsp.cg.static.nodes.host.FileSink import * -from cmsisdsp.cg.static.nodes.host.FileSource import * -from cmsisdsp.cg.static.nodes.CFFT import * -from cmsisdsp.cg.static.nodes.ICFFT import * -from cmsisdsp.cg.static.nodes.ToComplex import * -from cmsisdsp.cg.static.nodes.ToReal import * +from cmsisdsp.cg.nodes.host.FileSink import * +from cmsisdsp.cg.nodes.host.FileSource import * +from cmsisdsp.cg.nodes.CFFT import * +from cmsisdsp.cg.nodes.ICFFT import * +from cmsisdsp.cg.nodes.ToComplex import * +from cmsisdsp.cg.nodes.ToReal import * diff --git a/ComputeGraph/examples/example4/graph.py b/ComputeGraph/examples/example4/graph.py index 17c70f8d..003e25fb 100644 --- a/ComputeGraph/examples/example4/graph.py +++ b/ComputeGraph/examples/example4/graph.py @@ -1,6 +1,6 @@ import numpy as np -from cmsisdsp.cg.static.scheduler import * +from cmsisdsp.cg.scheduler import * FS=16000 diff --git a/ComputeGraph/examples/example4/sched.py b/ComputeGraph/examples/example4/sched.py index 4fbeff85..17074b30 100644 --- a/ComputeGraph/examples/example4/sched.py +++ b/ComputeGraph/examples/example4/sched.py @@ -10,7 +10,7 @@ import sys import numpy as np import cmsisdsp as dsp -from cmsisdsp.cg.static.nodes.simu import * +from cmsisdsp.cg.nodes.simu import * from appnodes import * from custom import * diff --git a/ComputeGraph/examples/example5/CMakeLists.txt b/ComputeGraph/examples/example5/CMakeLists.txt new file mode 100644 index 00000000..bd0f4007 --- /dev/null +++ b/ComputeGraph/examples/example5/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required (VERSION 3.14) +include(CMakePrintHelpers) + +project(Example5) + + +add_custom_target(example5 ALL DEPENDS sched.py) + +sdfpython(example5) + diff --git a/ComputeGraph/examples/example5/appnodes.py b/ComputeGraph/examples/example5/appnodes.py index 51974c7b..c7f7667b 100644 --- a/ComputeGraph/examples/example5/appnodes.py +++ b/ComputeGraph/examples/example5/appnodes.py @@ -25,16 +25,16 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################ -from cmsisdsp.cg.static.nodes.simu import * +from cmsisdsp.cg.nodes.simu import * from custom import * # Host only nodes -from cmsisdsp.cg.static.nodes.host.NumpySink import * -from cmsisdsp.cg.static.nodes.host.WavSource import * +from cmsisdsp.cg.nodes.host.NumpySink import * +from cmsisdsp.cg.nodes.host.WavSource import * # Embedded nodes -from cmsisdsp.cg.static.nodes.StereoToMono import * -from cmsisdsp.cg.static.nodes.MFCC import * +from cmsisdsp.cg.nodes.InterleavedStereoToMono import * +from cmsisdsp.cg.nodes.MFCC import * diff --git a/ComputeGraph/examples/example5/graph.py b/ComputeGraph/examples/example5/graph.py index ad9f15d8..4e798db0 100644 --- a/ComputeGraph/examples/example5/graph.py +++ b/ComputeGraph/examples/example5/graph.py @@ -1,6 +1,6 @@ import numpy as np -from cmsisdsp.cg.static.scheduler import * +from cmsisdsp.cg.scheduler import * from sharedconfig import * diff --git a/ComputeGraph/examples/example5/sched.py b/ComputeGraph/examples/example5/sched.py index 3a96a154..c8673a90 100644 --- a/ComputeGraph/examples/example5/sched.py +++ b/ComputeGraph/examples/example5/sched.py @@ -10,7 +10,7 @@ import sys import numpy as np import cmsisdsp as dsp -from cmsisdsp.cg.static.nodes.simu import * +from cmsisdsp.cg.nodes.simu import * from appnodes import * from custom import * diff --git a/ComputeGraph/examples/example5/test.pdf b/ComputeGraph/examples/example5/test.pdf index ce5864fb507ed9942f67991c5f2bc005f86441a8..520728b3735d8ae88954139931aab796856b0646 100644 GIT binary patch delta 27381 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 delta 27170 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 fifo0(buf1); - FIFO fifo1(buf2); - FIFO fifo2(buf3); - FIFO fifo3(buf4); + FIFO fifo0(buf1); + FIFO fifo1(buf2); + FIFO fifo2(buf3); + FIFO fifo3(buf4); CG_BEFORE_NODE_INIT; /* @@ -145,6 +146,7 @@ uint32_t scheduler(int *error,arm_mfcc_instance_f32 *mfccConfig) for(unsigned long id=0 ; id < 17; id++) { CG_BEFORE_NODE_EXECUTION; + switch(schedule[id]) { case 0: diff --git a/ComputeGraph/examples/example6/graph.py b/ComputeGraph/examples/example6/graph.py index 4ff9fb28..75170d7d 100644 --- a/ComputeGraph/examples/example6/graph.py +++ b/ComputeGraph/examples/example6/graph.py @@ -1,6 +1,6 @@ import numpy as np -from cmsisdsp.cg.static.scheduler import * +from cmsisdsp.cg.scheduler import * from sharedconfig import * diff --git a/ComputeGraph/examples/example7/CMakeLists.txt b/ComputeGraph/examples/example7/CMakeLists.txt new file mode 100644 index 00000000..b6c73287 --- /dev/null +++ b/ComputeGraph/examples/example7/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required (VERSION 3.14) +include(CMakePrintHelpers) + +project(Example7) + + +add_custom_target(example7 ALL DEPENDS sched.py) + +sdfpython(example7) + diff --git a/ComputeGraph/examples/example7/appnodes.py b/ComputeGraph/examples/example7/appnodes.py index 1bea427e..42fd62ea 100644 --- a/ComputeGraph/examples/example7/appnodes.py +++ b/ComputeGraph/examples/example7/appnodes.py @@ -25,17 +25,17 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################ -from cmsisdsp.cg.static.nodes.simu import * +from cmsisdsp.cg.nodes.simu import * from custom import * -from cmsisdsp.cg.static.nodes.host.FileSink import * -from cmsisdsp.cg.static.nodes.host.FileSource import * -from cmsisdsp.cg.static.nodes.CFFT import * -from cmsisdsp.cg.static.nodes.ICFFT import * -from cmsisdsp.cg.static.nodes.ToComplex import * -from cmsisdsp.cg.static.nodes.ToReal import * +from cmsisdsp.cg.nodes.host.FileSink import * +from cmsisdsp.cg.nodes.host.FileSource import * +from cmsisdsp.cg.nodes.CFFT import * +from cmsisdsp.cg.nodes.ICFFT import * +from cmsisdsp.cg.nodes.ToComplex import * +from cmsisdsp.cg.nodes.ToReal import * -from cmsisdsp.cg.static.nodes.host.VHTCGSTATIC import * +from cmsisdsp.cg.nodes.host.VHTCGSTATIC import * diff --git a/ComputeGraph/examples/example7/custom.py b/ComputeGraph/examples/example7/custom.py index 2a5a31bd..d7b673ca 100644 --- a/ComputeGraph/examples/example7/custom.py +++ b/ComputeGraph/examples/example7/custom.py @@ -1,4 +1,4 @@ -from cmsisdsp.cg.static.nodes.simu import * +from cmsisdsp.cg.nodes.simu import * import numpy as np import cmsisdsp as dsp diff --git a/ComputeGraph/examples/example7/graph.py b/ComputeGraph/examples/example7/graph.py index 47b1a7d9..93f45b9b 100644 --- a/ComputeGraph/examples/example7/graph.py +++ b/ComputeGraph/examples/example7/graph.py @@ -1,6 +1,6 @@ import numpy as np -from cmsisdsp.cg.static.scheduler import * +from cmsisdsp.cg.scheduler import * class Processing(GenericNode): diff --git a/ComputeGraph/examples/example7/sched.py b/ComputeGraph/examples/example7/sched.py index ca78473a..62cecbdf 100644 --- a/ComputeGraph/examples/example7/sched.py +++ b/ComputeGraph/examples/example7/sched.py @@ -10,7 +10,7 @@ import sys import numpy as np import cmsisdsp as dsp -from cmsisdsp.cg.static.nodes.simu import * +from cmsisdsp.cg.nodes.simu import * from appnodes import * from custom import * diff --git a/ComputeGraph/examples/example8/AppNodes.h b/ComputeGraph/examples/example8/AppNodes.h index f1ca45fa..d818ba65 100644 --- a/ComputeGraph/examples/example8/AppNodes.h +++ b/ComputeGraph/examples/example8/AppNodes.h @@ -36,6 +36,17 @@ class Sink: public GenericSink public: Sink(FIFOBase &src):GenericSink(src){}; + int prepareForRunning() override + { + if (this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run() { IN *b=this->getReadBuffer(); @@ -58,6 +69,17 @@ class Source: GenericSource public: Source(FIFOBase &dst):GenericSource(dst),mCounter(0){}; + int prepareForRunning() override + { + if (this->willOverflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ complex *b=this->getWriteBuffer(); @@ -90,6 +112,19 @@ public: OUT1,outputSize1, OUT2,outputSize2>(src,dst1,dst2){}; + int prepareForRunning() override + { + if (this->willOverflow1() || + this->willOverflow2() || + this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ printf("ProcessingNode\n"); IN *a=this->getReadBuffer(); diff --git a/ComputeGraph/examples/example8/generated/scheduler.cpp b/ComputeGraph/examples/example8/generated/scheduler.cpp index ff2ea63c..557475e1 100644 --- a/ComputeGraph/examples/example8/generated/scheduler.cpp +++ b/ComputeGraph/examples/example8/generated/scheduler.cpp @@ -72,6 +72,7 @@ The support classes and code is covered by CMSIS-DSP license. CG_AFTER_INCLUDES + /* Description of the scheduling. @@ -131,12 +132,12 @@ uint32_t scheduler(int *error,int someVariable) /* Create FIFOs objects */ - FIFO fifo0(buf1); - FIFO fifo1(buf2); - FIFO fifo2(buf3); - FIFO fifo3(buf4); - FIFO fifo4(buf5); - FIFO fifo5(buf6); + FIFO fifo0(buf1); + FIFO fifo1(buf2); + FIFO fifo2(buf3); + FIFO fifo3(buf4); + FIFO fifo4(buf5); + FIFO fifo5(buf6); CG_BEFORE_NODE_INIT; /* @@ -159,6 +160,7 @@ uint32_t scheduler(int *error,int someVariable) for(unsigned long id=0 ; id < 37; id++) { CG_BEFORE_NODE_EXECUTION; + switch(schedule[id]) { case 0: diff --git a/ComputeGraph/examples/example8/graph.py b/ComputeGraph/examples/example8/graph.py index 118c6db3..f88ccbf9 100644 --- a/ComputeGraph/examples/example8/graph.py +++ b/ComputeGraph/examples/example8/graph.py @@ -1,4 +1,4 @@ -from cmsisdsp.cg.static.scheduler import * +from cmsisdsp.cg.scheduler import * ### Define new types of Nodes diff --git a/ComputeGraph/examples/example9/AppNodes.h b/ComputeGraph/examples/example9/AppNodes.h index 53e311ac..b90c1a76 100644 --- a/ComputeGraph/examples/example9/AppNodes.h +++ b/ComputeGraph/examples/example9/AppNodes.h @@ -36,6 +36,17 @@ class Sink: public GenericSink public: Sink(FIFOBase &src):GenericSink(src){}; + int prepareForRunning() override + { + if (this->willUnderflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run() { IN *b=this->getReadBuffer(); @@ -55,6 +66,17 @@ class Source: GenericSource public: Source(FIFOBase &dst):GenericSource(dst),mCounter(0){}; + int prepareForRunning() override + { + if (this->willOverflow() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ OUT *b=this->getWriteBuffer(); @@ -91,6 +113,19 @@ public: float32_t,inputSize, float32_t,inputSize>(src1,src2,dst){}; + int prepareForRunning() override + { + if (this->willOverflow() || + this->willUnderflow1() || + this->willUnderflow2() + ) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + int run(){ printf("ProcessingNode\n"); float32_t *a=this->getReadBuffer1(); diff --git a/ComputeGraph/examples/example9/generated/scheduler.cpp b/ComputeGraph/examples/example9/generated/scheduler.cpp index 48d4b8bc..d82d6848 100644 --- a/ComputeGraph/examples/example9/generated/scheduler.cpp +++ b/ComputeGraph/examples/example9/generated/scheduler.cpp @@ -72,6 +72,7 @@ The support classes and code is covered by CMSIS-DSP license. CG_AFTER_INCLUDES + /* Description of the scheduling. @@ -121,10 +122,10 @@ uint32_t scheduler(int *error,int someVariable) /* Create FIFOs objects */ - FIFO fifo0(buf1); - FIFO fifo1(buf2); - FIFO fifo2(buf3); - FIFO fifo3(buf4,5); + FIFO fifo0(buf1); + FIFO fifo1(buf2); + FIFO fifo2(buf3); + FIFO fifo3(buf4,5); CG_BEFORE_NODE_INIT; /* @@ -144,6 +145,7 @@ uint32_t scheduler(int *error,int someVariable) for(unsigned long id=0 ; id < 4; id++) { CG_BEFORE_NODE_EXECUTION; + switch(schedule[id]) { case 0: diff --git a/ComputeGraph/examples/example9/graph.py b/ComputeGraph/examples/example9/graph.py index 7aa824b0..4094058a 100644 --- a/ComputeGraph/examples/example9/graph.py +++ b/ComputeGraph/examples/example9/graph.py @@ -1,4 +1,4 @@ -from cmsisdsp.cg.static.scheduler import * +from cmsisdsp.cg.scheduler import * ### Define new types of Nodes diff --git a/MANIFEST.in b/MANIFEST.in index ce69175a..5e742107 100755 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,6 +2,6 @@ include PythonWrapper_README.md recursive-include Include *.h recursive-include PrivateInclude *.h recursive-include PythonWrapper/cmsisdsp_pkg/src *.h -include cmsisdsp/cg/static/scheduler/templates/* +include cmsisdsp/cg/scheduler/templates/* include Source/DistanceFunctions/arm_boolean_distance_template.h diff --git a/cmsisdsp/__init__.py b/cmsisdsp/__init__.py index 277cc25b..970b6bdc 100755 --- a/cmsisdsp/__init__.py +++ b/cmsisdsp/__init__.py @@ -20,7 +20,7 @@ from cmsisdsp_window import * __version__ = cmsisdsp.version.__version__ # CMSIS-DSP Version used to build the wrapper -cmsis_dsp_version="1.15.0" +cmsis_dsp_version="1.14.4" # CMSIS-DSP Commit hash used to build the wrapper diff --git a/cmsisdsp/cg/static/nodes/CFFT.py b/cmsisdsp/cg/nodes/CFFT.py similarity index 95% rename from cmsisdsp/cg/static/nodes/CFFT.py rename to cmsisdsp/cg/nodes/CFFT.py index 082ffaed..fc710b03 100644 --- a/cmsisdsp/cg/static/nodes/CFFT.py +++ b/cmsisdsp/cg/nodes/CFFT.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/Duplicate.py b/cmsisdsp/cg/nodes/Duplicate.py similarity index 95% rename from cmsisdsp/cg/static/nodes/Duplicate.py rename to cmsisdsp/cg/nodes/Duplicate.py index ab86284c..aa977999 100644 --- a/cmsisdsp/cg/static/nodes/Duplicate.py +++ b/cmsisdsp/cg/nodes/Duplicate.py @@ -8,7 +8,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2022 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/ICFFT.py b/cmsisdsp/cg/nodes/ICFFT.py similarity index 95% rename from cmsisdsp/cg/static/nodes/ICFFT.py rename to cmsisdsp/cg/nodes/ICFFT.py index 81a30e3c..575b1b22 100644 --- a/cmsisdsp/cg/static/nodes/ICFFT.py +++ b/cmsisdsp/cg/nodes/ICFFT.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/InterleavedStereoToMono.py b/cmsisdsp/cg/nodes/InterleavedStereoToMono.py similarity index 95% rename from cmsisdsp/cg/static/nodes/InterleavedStereoToMono.py rename to cmsisdsp/cg/nodes/InterleavedStereoToMono.py index 20a5bae1..10e9d126 100644 --- a/cmsisdsp/cg/static/nodes/InterleavedStereoToMono.py +++ b/cmsisdsp/cg/nodes/InterleavedStereoToMono.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/MFCC.py b/cmsisdsp/cg/nodes/MFCC.py similarity index 95% rename from cmsisdsp/cg/static/nodes/MFCC.py rename to cmsisdsp/cg/nodes/MFCC.py index cace21b3..e90f2345 100644 --- a/cmsisdsp/cg/static/nodes/MFCC.py +++ b/cmsisdsp/cg/nodes/MFCC.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/NullSink.py b/cmsisdsp/cg/nodes/NullSink.py similarity index 94% rename from cmsisdsp/cg/static/nodes/NullSink.py rename to cmsisdsp/cg/nodes/NullSink.py index 71055484..27133448 100644 --- a/cmsisdsp/cg/static/nodes/NullSink.py +++ b/cmsisdsp/cg/nodes/NullSink.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/ToComplex.py b/cmsisdsp/cg/nodes/ToComplex.py similarity index 94% rename from cmsisdsp/cg/static/nodes/ToComplex.py rename to cmsisdsp/cg/nodes/ToComplex.py index 9a22923c..d8d696e1 100644 --- a/cmsisdsp/cg/static/nodes/ToComplex.py +++ b/cmsisdsp/cg/nodes/ToComplex.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/ToReal.py b/cmsisdsp/cg/nodes/ToReal.py similarity index 94% rename from cmsisdsp/cg/static/nodes/ToReal.py rename to cmsisdsp/cg/nodes/ToReal.py index d379a700..9a65ac61 100644 --- a/cmsisdsp/cg/static/nodes/ToReal.py +++ b/cmsisdsp/cg/nodes/ToReal.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/Unzip.py b/cmsisdsp/cg/nodes/Unzip.py similarity index 94% rename from cmsisdsp/cg/static/nodes/Unzip.py rename to cmsisdsp/cg/nodes/Unzip.py index 1fdf0497..f3f98395 100644 --- a/cmsisdsp/cg/static/nodes/Unzip.py +++ b/cmsisdsp/cg/nodes/Unzip.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/Zip.py b/cmsisdsp/cg/nodes/Zip.py similarity index 95% rename from cmsisdsp/cg/static/nodes/Zip.py rename to cmsisdsp/cg/nodes/Zip.py index a7147baa..dedfa023 100644 --- a/cmsisdsp/cg/static/nodes/Zip.py +++ b/cmsisdsp/cg/nodes/Zip.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/__init__.py b/cmsisdsp/cg/nodes/__init__.py similarity index 90% rename from cmsisdsp/cg/static/nodes/__init__.py rename to cmsisdsp/cg/nodes/__init__.py index ac29b6d4..a6459239 100644 --- a/cmsisdsp/cg/static/nodes/__init__.py +++ b/cmsisdsp/cg/nodes/__init__.py @@ -1,7 +1,7 @@ ########################################### # Project: CMSIS DSP Library # Title: __init__.py -# Description: CG static flow module +# Description: CG default nodes # # $Date: 30 August 2021 # $Revision: V1.10.0 @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/host/FileSink.py b/cmsisdsp/cg/nodes/host/FileSink.py similarity index 95% rename from cmsisdsp/cg/static/nodes/host/FileSink.py rename to cmsisdsp/cg/nodes/host/FileSink.py index f9b0b752..612fd282 100644 --- a/cmsisdsp/cg/static/nodes/host/FileSink.py +++ b/cmsisdsp/cg/nodes/host/FileSink.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/host/FileSource.py b/cmsisdsp/cg/nodes/host/FileSource.py similarity index 94% rename from cmsisdsp/cg/static/nodes/host/FileSource.py rename to cmsisdsp/cg/nodes/host/FileSource.py index 18b41039..348684e3 100644 --- a/cmsisdsp/cg/static/nodes/host/FileSource.py +++ b/cmsisdsp/cg/nodes/host/FileSource.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/host/NumpySink.py b/cmsisdsp/cg/nodes/host/NumpySink.py similarity index 95% rename from cmsisdsp/cg/static/nodes/host/NumpySink.py rename to cmsisdsp/cg/nodes/host/NumpySink.py index 5283a306..c69eacde 100644 --- a/cmsisdsp/cg/static/nodes/host/NumpySink.py +++ b/cmsisdsp/cg/nodes/host/NumpySink.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/host/VHT.py b/cmsisdsp/cg/nodes/host/VHT.py similarity index 98% rename from cmsisdsp/cg/static/nodes/host/VHT.py rename to cmsisdsp/cg/nodes/host/VHT.py index d3c10c23..102d7d1e 100644 --- a/cmsisdsp/cg/static/nodes/host/VHT.py +++ b/cmsisdsp/cg/nodes/host/VHT.py @@ -1,7 +1,7 @@ from multiprocessing import Process,Semaphore import multiprocessing as mp import socket -import cmsisdsp.cg.static.nodes.host.message as msg +import cmsisdsp.cg.nodes.host.message as msg HOST = '127.0.0.1' # The remote host PORT = 50007 diff --git a/cmsisdsp/cg/static/nodes/host/VHTCGSTATIC.py b/cmsisdsp/cg/nodes/host/VHTCGSTATIC.py similarity index 100% rename from cmsisdsp/cg/static/nodes/host/VHTCGSTATIC.py rename to cmsisdsp/cg/nodes/host/VHTCGSTATIC.py diff --git a/cmsisdsp/cg/static/nodes/host/WavSink.py b/cmsisdsp/cg/nodes/host/WavSink.py similarity index 95% rename from cmsisdsp/cg/static/nodes/host/WavSink.py rename to cmsisdsp/cg/nodes/host/WavSink.py index cae8ed34..2ba6519c 100644 --- a/cmsisdsp/cg/static/nodes/host/WavSink.py +++ b/cmsisdsp/cg/nodes/host/WavSink.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/nodes/host/WavSource.py b/cmsisdsp/cg/nodes/host/WavSource.py similarity index 100% rename from cmsisdsp/cg/static/nodes/host/WavSource.py rename to cmsisdsp/cg/nodes/host/WavSource.py diff --git a/cmsisdsp/cg/static/nodes/host/__init__.py b/cmsisdsp/cg/nodes/host/__init__.py similarity index 100% rename from cmsisdsp/cg/static/nodes/host/__init__.py rename to cmsisdsp/cg/nodes/host/__init__.py diff --git a/cmsisdsp/cg/static/nodes/host/message.py b/cmsisdsp/cg/nodes/host/message.py similarity index 98% rename from cmsisdsp/cg/static/nodes/host/message.py rename to cmsisdsp/cg/nodes/host/message.py index 25df7d16..f7c77704 100644 --- a/cmsisdsp/cg/static/nodes/host/message.py +++ b/cmsisdsp/cg/nodes/host/message.py @@ -107,7 +107,7 @@ def getBufferMsg(conn,nbBytes): data = receiveBytes(conn,nbBytes) return(data) -# Stop the server when the end of the static scheduling has been reached. +# Stop the server when the end of the scheduling has been reached. # It is to make it easier to end the demo. # Only the VHT client has to be killed. # Client -> Server diff --git a/cmsisdsp/cg/static/nodes/simu.py b/cmsisdsp/cg/nodes/simu.py similarity index 99% rename from cmsisdsp/cg/static/nodes/simu.py rename to cmsisdsp/cg/nodes/simu.py index 4e1387f3..5afef587 100644 --- a/cmsisdsp/cg/static/nodes/simu.py +++ b/cmsisdsp/cg/nodes/simu.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/cmsisdsp/cg/static/scheduler/__init__.py b/cmsisdsp/cg/scheduler/__init__.py similarity index 100% rename from cmsisdsp/cg/static/scheduler/__init__.py rename to cmsisdsp/cg/scheduler/__init__.py diff --git a/cmsisdsp/cg/static/scheduler/ccode.py b/cmsisdsp/cg/scheduler/ccode.py similarity index 89% rename from cmsisdsp/cg/static/scheduler/ccode.py rename to cmsisdsp/cg/scheduler/ccode.py index b0851530..00cccfce 100644 --- a/cmsisdsp/cg/static/scheduler/ccode.py +++ b/cmsisdsp/cg/scheduler/ccode.py @@ -1,7 +1,7 @@ ########################################### # Project: CMSIS DSP Library # Title: ccode.py -# Description: C++ code generator for static scheduler +# Description: C++ code generator for scheduler # # $Date: 29 July 2021 # $Revision: V1.10.0 @@ -34,13 +34,19 @@ def gencode(sched,directory,config): env = Environment( - loader=PackageLoader("cmsisdsp.cg.static.scheduler"), + loader=PackageLoader("cmsisdsp.cg.scheduler"), autoescape=select_autoescape(), trim_blocks=True ) schedDescription="" + # Asychronous implies code array and switchCase + if config.asynchronous: + config.codeArray = True + config.switchCase = True + config.memoryOptimization = False + if config.codeArray: if config.switchCase: ctemplate = env.get_template("codeSwitch.cpp") diff --git a/cmsisdsp/cg/static/scheduler/config.py b/cmsisdsp/cg/scheduler/config.py similarity index 85% rename from cmsisdsp/cg/static/scheduler/config.py rename to cmsisdsp/cg/scheduler/config.py index 197e808c..04558a02 100644 --- a/cmsisdsp/cg/static/scheduler/config.py +++ b/cmsisdsp/cg/scheduler/config.py @@ -123,6 +123,26 @@ class Configuration: # By default arm_math.h is included self.CMSISDSP = True + # Asynchronous scheduling using + # synchronous as first start + # It implies switchCase and codeArray + # It disables memory optimizations + # Also FIFO cannot be used any more as just + # buffers. + self.asynchronous = False + + # Increase in percent of the FIFOs. + # Used in asynchronous mode + # where the FIFOs may need to be bigger + # than what is computed assuming a + # synchronous scheduling. + self.FIFOIncrease = 0 + + # Default asynchronous more for pure functions + # like CMSIS-DSP + self.asyncDefaultSkip = True + + @property def debug(self): return (self.debugLimit > 0) diff --git a/cmsisdsp/cg/static/scheduler/description.py b/cmsisdsp/cg/scheduler/description.py similarity index 95% rename from cmsisdsp/cg/static/scheduler/description.py rename to cmsisdsp/cg/scheduler/description.py index 600f773e..2f81515e 100644 --- a/cmsisdsp/cg/static/scheduler/description.py +++ b/cmsisdsp/cg/scheduler/description.py @@ -9,7 +9,7 @@ # Target Processor: Cortex-M and Cortex-A cores # -------------------------------------------------------------------- */ # -# Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. +# Copyright (C) 2010-2023 ARM Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # @@ -29,12 +29,14 @@ import networkx as nx import numpy as np +import math + from sympy import Matrix from sympy.core.numbers import ilcm,igcd -import cmsisdsp.cg.static.scheduler.graphviz -import cmsisdsp.cg.static.scheduler.ccode -import cmsisdsp.cg.static.scheduler.pythoncode +import cmsisdsp.cg.scheduler.graphviz +import cmsisdsp.cg.scheduler.ccode +import cmsisdsp.cg.scheduler.pythoncode from .node import * from .config import * @@ -72,7 +74,7 @@ class FifoBuffer: class FIFODesc: """A FIFO connecting two nodes""" - def __init__(self,fifoid): + def __init__(self,fifoid,fifoClass): # The FIFO is in fact just an array self.isArray=False # FIFO length @@ -90,7 +92,7 @@ class FIFODesc: self.dst = None # FIFO delay self.delay=0 - self.fifoClass = "FIFO" + self.fifoClass = fifoClass # Used for liveliness analysis # To share buffers between FIFO in memory optimization @@ -177,6 +179,13 @@ class Graph(): # (prioritizing the scheduling of nodes # close to the graph output) self._topologicalSort=[] + self.defaultFIFOClass = "FIFO" + + # Prefix used to generate the class names + # of the duplicate nodes like Duplicate2, + # Duplicate3 ... + self.duplicateNodeClassName = "Duplicate" + def computeTopologicalSortOfNodes(self): remaining = self._sortedNodes.copy() @@ -213,7 +222,7 @@ class Graph(): #print(self._topologicalSort) - def connectDup(self,destination,outputIO,theId,fifoClass="FIFO"): + def connectDup(self,destination,outputIO,theId): if (destination[theId][1]!=0): self.connectWithDelay(outputIO,destination[theId][0],destination[theId][1],dupAllowed=False,fifoClass=destination[theId][2]) else: @@ -261,10 +270,10 @@ class Graph(): # We create a duplicate node if len(fifo)==2: - dup = Duplicate2("dup%d" % dupNb,theType,inputSize) + dup = Duplicate2("dup%d" % dupNb,theType,inputSize,className=self.duplicateNodeClassName) if len(fifo)==3: - dup = Duplicate3("dup%d" % dupNb,theType,inputSize) + dup = Duplicate3("dup%d" % dupNb,theType,inputSize,className=self.duplicateNodeClassName) #print(dup) @@ -289,7 +298,7 @@ class Graph(): nodea = f[0] nodeb = f[1] - fifoClass = "FIFO" + fifoClass = self.defaultFIFOClass if (nodea,nodeb) in self._FIFOClasses: fifoClass = self._FIFOClasses[(nodea,nodeb)] @@ -342,7 +351,9 @@ class Graph(): - def connect(self,nodea,nodeb,dupAllowed=True,fifoClass="FIFO"): + def connect(self,nodea,nodeb,dupAllowed=True,fifoClass=None): + if fifoClass is None: + fifoClass = self.defaultFIFOClass # When connecting to a constant node we do nothing # since there is no FIFO in this case # and it does not participate to the scheduling. @@ -370,7 +381,9 @@ class Graph(): else: raise IncompatibleIO - def connectWithDelay(self,nodea,nodeb,delay,dupAllowed=True,fifoClass="FIFO"): + def connectWithDelay(self,nodea,nodeb,delay,dupAllowed=True,fifoClass=None): + if fifoClass is None: + fifoClass = self.defaultFIFOClass # We cannot connect with delay to a constant node if (isinstance(nodea,Constant)): raise CannotDelayConstantError @@ -392,7 +405,10 @@ class Graph(): """Initialize FIFOs datastructure""" for fifo in allFIFOs: edge = self._sortedEdges[fifo.fifoID] - fifo.length = fifoLengths[fifo.fifoID] + if config.asynchronous: + fifo.length = int(math.ceil(fifoLengths[fifo.fifoID] * (1.0 + 1.0*config.FIFOIncrease/100))) + else: + fifo.length = fifoLengths[fifo.fifoID] src,dst = edge if edge in self._FIFOClasses: fifoClass = self._FIFOClasses[edge] @@ -404,7 +420,8 @@ class Graph(): # potentially be shared with other FIFOs workign as arrays if src.nbSamples == dst.nbSamples: if fifo.delay==0: - fifo.isArray = True + if not config.asynchronous: + fifo.isArray = True fifo.theType = src.theType #fifo.dump() @@ -415,7 +432,11 @@ class Graph(): # Compute a graph describing when FIFOs are used at the same time # Then use graph coloring to allocate buffer to those FIFOs. # Then size the buffer based on the longest FIFO using it - if config.memoryOptimization: + # Memory optimizations are disabled by the asynchronous mode + # because the execution is no more periodic. + # So anything deduced from a synchronous period + # can't be used. + if config.memoryOptimization and not config.asynchronous: G = nx.Graph() for fifo in allFIFOs: @@ -512,7 +533,7 @@ class Graph(): bufferID = bufferID + 1 for fifo in allFIFOs: # Use shared buffer if memory optimization - if fifo.isArray and config.memoryOptimization: + if fifo.isArray and (config.memoryOptimization and not config.asynchronous): fifo.buffer=allBuffers[fifo.sharedNB] fifo.bufferID=fifo.sharedNB # Create a new buffer for a real FIFO @@ -631,7 +652,7 @@ class Graph(): def evolutionVectorForNode(self,nodeID,test=True): """Return the evolution vector corresponding to a selected node""" - # For a simple static scheduling, the toplogy matrix T + # For a simple static scheduling, the topology matrix T # is enough. If we know which node is executed, we have # a node vector V where there is a 1 at the position of the # execute node. @@ -845,7 +866,7 @@ class Graph(): nbFIFOS = t.shape[0] allFIFOs = [] for i in range(nbFIFOS): - allFIFOs.append(FIFODesc(i)) + allFIFOs.append(FIFODesc(i,self.defaultFIFOClass)) # Normalization vector # For static scheduling it is @@ -1103,15 +1124,15 @@ class Schedule: def ccode(self,directory,config=Configuration()): """Write graphviz into file f""" - cmsisdsp.cg.static.scheduler.ccode.gencode(self,directory,config) + cmsisdsp.cg.scheduler.ccode.gencode(self,directory,config) def pythoncode(self,directory,config=Configuration()): """Write graphviz into file f""" - cmsisdsp.cg.static.scheduler.pythoncode.gencode(self,directory,config) + cmsisdsp.cg.scheduler.pythoncode.gencode(self,directory,config) def graphviz(self,f,config=Configuration()): """Write graphviz into file f""" - cmsisdsp.cg.static.scheduler.graphviz.gengraph(self,f,config) + cmsisdsp.cg.scheduler.graphviz.gengraph(self,f,config) diff --git a/cmsisdsp/cg/static/scheduler/graphviz.py b/cmsisdsp/cg/scheduler/graphviz.py similarity index 95% rename from cmsisdsp/cg/static/scheduler/graphviz.py rename to cmsisdsp/cg/scheduler/graphviz.py index 0924bac3..242b47f3 100644 --- a/cmsisdsp/cg/static/scheduler/graphviz.py +++ b/cmsisdsp/cg/scheduler/graphviz.py @@ -32,7 +32,7 @@ import os.path def gengraph(sched,f,config): env = Environment( - loader=PackageLoader("cmsisdsp.cg.static.scheduler"), + loader=PackageLoader("cmsisdsp.cg.scheduler"), autoescape=select_autoescape(), trim_blocks=True ) diff --git a/cmsisdsp/cg/static/scheduler/node.py b/cmsisdsp/cg/scheduler/node.py similarity index 92% rename from cmsisdsp/cg/static/scheduler/node.py rename to cmsisdsp/cg/scheduler/node.py index 3f20d969..97909947 100644 --- a/cmsisdsp/cg/static/scheduler/node.py +++ b/cmsisdsp/cg/scheduler/node.py @@ -37,6 +37,8 @@ from sympy.core.numbers import ilcm class NoFunctionArrayInPython(Exception): pass +class NoAsynchronousModeInPython(Exception): + pass def camelCase(st): output = ''.join(x for x in st.title() if x.isalnum()) @@ -579,6 +581,15 @@ class GenericNode(BaseNode): def __init__(self,name): BaseNode.__init__(self,name) + # Pure node is for instance a + # CMSIS-DSP function. + # It is not packaged into an object + # and the code is generated directly + self._isPureNode = False + + @property + def isPureNode(self): + return self._isPureNode @property def typeName(self): @@ -668,13 +679,14 @@ class GenericFunction(GenericNode): ENV = Environment( - loader=PackageLoader("cmsisdsp.cg.static.scheduler"), + loader=PackageLoader("cmsisdsp.cg.scheduler"), autoescape=select_autoescape(), lstrip_blocks=True, trim_blocks=True ) CTEMPLATE = ENV.get_template("cmsis.cpp") + CCHECKTEMPLATE = ENV.get_template("cmsisCheck.cpp") CNODETEMPLATE = ENV.get_template("cmsisNode.cpp") PYTEMPLATE = ENV.get_template("cmsis.py") @@ -690,6 +702,7 @@ class GenericFunction(GenericNode): self._hasState = False self._length = length self._nodeName = funcname + self._isPureNode = True GenericFunction.NODEID[funcname]=GenericFunction.NODEID[funcname]+1 @@ -836,14 +849,18 @@ class GenericFunction(GenericNode): def typeName(self): return "Function" - # To clean - def cRun(self,ctemplate=True,codeArray=False): + # Prepare for code generation. + # All those values are used in the + # code generation template + # They are both used for the run part + # and the prepareForRunning part + def _prepareForCodeGen(self,ctemplate=True): if ctemplate: theType=self._inputs[self.inputNames[0]].ctype else: theType=self._inputs[self.inputNames[0]].nptype # For cyclo static scheduling, nbSamples may be a list - # and in this case we are uding the mac value + # and in this case we are using the max value nbSamples = self._inputs[self.inputNames[0]].nbSamples if isinstance(nbSamples,int): theLen = self._inputs[self.inputNames[0]].nbSamples @@ -901,37 +918,65 @@ class GenericFunction(GenericNode): inArgsStr="".join(joinit(inargs,",")) outArgsStr="".join(joinit(outargs,",")) + return ({"theType" : theType, + "nb" : theLen, + "ptrs" : ptrs, + "args" : argsStr, + "inArgsStr" : inArgsStr, + "outArgsStr" :outArgsStr, + "inputs":inputs, + "outputs":outputs}) + + def cCheck(self,asyncDefaultSkip=True): + params = self._prepareForCodeGen(True) + result=Dsp.CCHECKTEMPLATE.render(func=self._nodeName, + theType = params["theType"], + nb = params["nb"], + ptrs = params["ptrs"], + args = params["args"], + inputs=params["inputs"], + outputs=params["outputs"], + node=self, + asyncDefaultSkip = asyncDefaultSkip + ) + return(result) + + + # To clean + def cRun(self,ctemplate=True,codeArray=False): + params = self._prepareForCodeGen(ctemplate) + if ctemplate: if codeArray: result=Dsp.CNODETEMPLATE.render(func=self._nodeName, - theType = theType, - nb = theLen, - ptrs = ptrs, - args = argsStr, - inputs=inputs, - outputs=outputs, + theType = params["theType"], + nb = params["nb"], + ptrs = params["ptrs"], + args = params["args"], + inputs=params["inputs"], + outputs=params["outputs"], node=self ) else: result=Dsp.CTEMPLATE.render(func=self._nodeName, - theType = theType, - nb = theLen, - ptrs = ptrs, - args = argsStr, - inputs=inputs, - outputs=outputs, + theType = params["theType"], + nb = params["nb"], + ptrs = params["ptrs"], + args = params["args"], + inputs=params["inputs"], + outputs=params["outputs"], node=self ) else: result=Dsp.PYTEMPLATE.render(func=self._nodeName, - theType = theType, - nb = theLen, - ptrs = ptrs, - args = argsStr, - inArgs= inArgsStr, - outArgs= outArgsStr, - inputs=inputs, - outputs=outputs, + theType = params["theType"], + nb = params["nb"], + ptrs = params["ptrs"], + args = params["args"], + inArgs= params["inArgsStr"], + outArgs= params["outArgsStr"], + inputs=params["inputs"], + outputs=params["outputs"], node=self ) return(result) diff --git a/cmsisdsp/cg/static/scheduler/pythoncode.py b/cmsisdsp/cg/scheduler/pythoncode.py similarity index 95% rename from cmsisdsp/cg/static/scheduler/pythoncode.py rename to cmsisdsp/cg/scheduler/pythoncode.py index 3430d234..9d0dddb0 100644 --- a/cmsisdsp/cg/static/scheduler/pythoncode.py +++ b/cmsisdsp/cg/scheduler/pythoncode.py @@ -33,7 +33,7 @@ from .config import * def gencode(sched,directory,config): env = Environment( - loader=PackageLoader("cmsisdsp.cg.static.scheduler"), + loader=PackageLoader("cmsisdsp.cg.scheduler"), autoescape=select_autoescape(), trim_blocks=True ) diff --git a/cmsisdsp/cg/static/scheduler/standard.py b/cmsisdsp/cg/scheduler/standard.py similarity index 95% rename from cmsisdsp/cg/static/scheduler/standard.py rename to cmsisdsp/cg/scheduler/standard.py index e5894256..844800c6 100644 --- a/cmsisdsp/cg/static/scheduler/standard.py +++ b/cmsisdsp/cg/scheduler/standard.py @@ -137,9 +137,11 @@ class MFCC(GenericNode): return "MFCC" class Duplicate2(GenericNode): - def __init__(self,name,theType,inLength): + def __init__(self,name,theType,inLength,className="Duplicate"): GenericNode.__init__(self,name) + self._className = className + self.addInput("i",theType,inLength) self.addOutput("oa",theType,inLength) self.addOutput("ob",theType,inLength) @@ -150,12 +152,14 @@ class Duplicate2(GenericNode): @property def typeName(self): - return "Duplicate2" + return ("%s2" % self._className) class Duplicate3(GenericNode): - def __init__(self,name,theType,inLength): + def __init__(self,name,theType,inLength,className="Duplicate"): GenericNode.__init__(self,name) + self._className = className + self.addInput("i",theType,inLength) self.addOutput("oa",theType,inLength) self.addOutput("ob",theType,inLength) @@ -167,7 +171,7 @@ class Duplicate3(GenericNode): @property def typeName(self): - return "Duplicate3" + return ("%s3" % self._className) ############################# # diff --git a/cmsisdsp/cg/scheduler/templates/cmsis.cpp b/cmsisdsp/cg/scheduler/templates/cmsis.cpp new file mode 100644 index 00000000..e378fc9e --- /dev/null +++ b/cmsisdsp/cg/scheduler/templates/cmsis.cpp @@ -0,0 +1,15 @@ + + { + +{% for ptr in ptrs %} + {{theType}}* {{ptr}}; +{% endfor %} +{% for ptr in inputs %} + {{ptr[0]}}={{ptr[1]}}.getReadBuffer({{nb}}); +{% endfor %} +{% for ptr in outputs %} + {{ptr[0]}}={{ptr[1]}}.getWriteBuffer({{nb}}); +{% endfor %} + {{func}}({{args}},{{nb}}); + cgStaticError = 0; + } \ No newline at end of file diff --git a/cmsisdsp/cg/static/scheduler/templates/cmsis.py b/cmsisdsp/cg/scheduler/templates/cmsis.py similarity index 100% rename from cmsisdsp/cg/static/scheduler/templates/cmsis.py rename to cmsisdsp/cg/scheduler/templates/cmsis.py diff --git a/cmsisdsp/cg/scheduler/templates/cmsisCheck.cpp b/cmsisdsp/cg/scheduler/templates/cmsisCheck.cpp new file mode 100644 index 00000000..052ebdd5 --- /dev/null +++ b/cmsisdsp/cg/scheduler/templates/cmsisCheck.cpp @@ -0,0 +1,21 @@ + + bool canRun=true; +{% for ptr in inputs %} + canRun &= !{{ptr[1]}}.willUnderflowWith({{nb}}); +{% endfor %} +{% for ptr in outputs %} + canRun &= !{{ptr[1]}}.willOverflowWith({{nb}}); +{% endfor %} + + if (!canRun) + { +{% if asyncDefaultSkip %} + cgStaticError = CG_SKIP_EXECUTION_ID_CODE; +{% else %} + cgStaticError = CG_BUFFER_ERROR_ID_CODE; +{%- endif %} + } + else + { + cgStaticError = 0; + } \ No newline at end of file diff --git a/cmsisdsp/cg/static/scheduler/templates/cmsisNode.cpp b/cmsisdsp/cg/scheduler/templates/cmsisNode.cpp similarity index 100% rename from cmsisdsp/cg/static/scheduler/templates/cmsisNode.cpp rename to cmsisdsp/cg/scheduler/templates/cmsisNode.cpp diff --git a/cmsisdsp/cg/static/scheduler/templates/code.cpp b/cmsisdsp/cg/scheduler/templates/code.cpp similarity index 100% rename from cmsisdsp/cg/static/scheduler/templates/code.cpp rename to cmsisdsp/cg/scheduler/templates/code.cpp diff --git a/cmsisdsp/cg/static/scheduler/templates/code.h b/cmsisdsp/cg/scheduler/templates/code.h similarity index 100% rename from cmsisdsp/cg/static/scheduler/templates/code.h rename to cmsisdsp/cg/scheduler/templates/code.h diff --git a/cmsisdsp/cg/static/scheduler/templates/code.py b/cmsisdsp/cg/scheduler/templates/code.py similarity index 96% rename from cmsisdsp/cg/static/scheduler/templates/code.py rename to cmsisdsp/cg/scheduler/templates/code.py index 1afcf57b..3ebf391a 100644 --- a/cmsisdsp/cg/static/scheduler/templates/code.py +++ b/cmsisdsp/cg/scheduler/templates/code.py @@ -10,7 +10,7 @@ import sys import numpy as np import cmsisdsp as dsp -from cmsisdsp.cg.static.nodes.simu import * +from cmsisdsp.cg.nodes.simu import * from {{config.appNodesPythonName}} import * from {{config.customPythonName}} import * diff --git a/cmsisdsp/cg/static/scheduler/templates/codeArray.cpp b/cmsisdsp/cg/scheduler/templates/codeArray.cpp similarity index 100% rename from cmsisdsp/cg/static/scheduler/templates/codeArray.cpp rename to cmsisdsp/cg/scheduler/templates/codeArray.cpp diff --git a/cmsisdsp/cg/static/scheduler/templates/codeSwitch.cpp b/cmsisdsp/cg/scheduler/templates/codeSwitch.cpp similarity index 52% rename from cmsisdsp/cg/static/scheduler/templates/codeSwitch.cpp rename to cmsisdsp/cg/scheduler/templates/codeSwitch.cpp index 551a83f9..494e3e78 100644 --- a/cmsisdsp/cg/static/scheduler/templates/codeSwitch.cpp +++ b/cmsisdsp/cg/scheduler/templates/codeSwitch.cpp @@ -31,12 +31,58 @@ static unsigned int schedule[{{schedLen}}]= EventRecord2 (Evt_Node, schedule[id], 0); {% endif -%} CG_BEFORE_NODE_EXECUTION; + + {% if config.asynchronous -%} + cgStaticError = 0; + switch(schedule[id]) + { + {% for nodeID in range(nbNodes) -%} + case {{nodeID}}: + { + {% if not nodes[nodeID].isPureNode -%} + cgStaticError = {{nodes[nodeID].nodeName}}.prepareForRunning(); + {%- else -%} + {{nodes[nodeID].cCheck(config.asyncDefaultSkip)}} + {%- endif %} + + } + break; + + {% endfor -%} + + default: + break; + } + + if (cgStaticError == CG_SKIP_EXECUTION_ID_CODE) + continue; + + {% if config.eventRecorder -%} + if (cgStaticError<0) + { + EventRecord2 (Evt_Error, cgStaticError, 0); + } + {% endif -%} + + CHECKERROR; + + {% endif -%} + switch(schedule[id]) { {% for nodeID in range(nbNodes) -%} case {{nodeID}}: { {{nodes[nodeID].cRun()}} + + {%- if config.dumpFIFO %} + {%- for fifoID in sched.outputFIFOs(nodes[nodeID]) %} + + std::cout << "{{nodes[nodeID].nodeName}}:{{fifoID[1]}}" << std::endl; + fifo{{fifoID[0]}}.dump(); + {%- endfor %} + {%- endif %} + } break; diff --git a/cmsisdsp/cg/static/scheduler/templates/commonc.cpp b/cmsisdsp/cg/scheduler/templates/commonc.cpp similarity index 85% rename from cmsisdsp/cg/static/scheduler/templates/commonc.cpp rename to cmsisdsp/cg/scheduler/templates/commonc.cpp index 1480f3eb..bef27552 100644 --- a/cmsisdsp/cg/static/scheduler/templates/commonc.cpp +++ b/cmsisdsp/cg/scheduler/templates/commonc.cpp @@ -31,6 +31,10 @@ CG_AFTER_INCLUDES {% if config.cOptionalArgs %},{{config.cOptionalArgs}}{% endif %} {% endmacro -%} +{% macro async() -%} +{% if config.asynchronous %}1{% else %}0{% endif %} +{% endmacro %} + {% block schedArray %} {% endblock %} @@ -66,9 +70,9 @@ uint32_t {{config.schedName}}(int *error{{optionalargs()}}) */ {% for id in range(nbFifos) %} {% if fifos[id].hasDelay %} - {{fifos[id].fifoClass}}<{{fifos[id].theType.ctype}},FIFOSIZE{{id}},{{fifos[id].isArrayAsInt}}> fifo{{id}}({{config.prefix}}buf{{fifos[id].buffer._bufferID}},{{fifos[id].delay}}); + {{fifos[id].fifoClass}}<{{fifos[id].theType.ctype}},FIFOSIZE{{id}},{{fifos[id].isArrayAsInt}},{{async()}}> fifo{{id}}({{config.prefix}}buf{{fifos[id].buffer._bufferID}},{{fifos[id].delay}}); {% else %} - {{fifos[id].fifoClass}}<{{fifos[id].theType.ctype}},FIFOSIZE{{id}},{{fifos[id].isArrayAsInt}}> fifo{{id}}({{config.prefix}}buf{{fifos[id].buffer._bufferID}}); + {{fifos[id].fifoClass}}<{{fifos[id].theType.ctype}},FIFOSIZE{{id}},{{fifos[id].isArrayAsInt}},{{async()}}> fifo{{id}}({{config.prefix}}buf{{fifos[id].buffer._bufferID}}); {% endif %} {% endfor %} diff --git a/cmsisdsp/cg/static/scheduler/templates/defineConfig.h b/cmsisdsp/cg/scheduler/templates/defineConfig.h similarity index 100% rename from cmsisdsp/cg/static/scheduler/templates/defineConfig.h rename to cmsisdsp/cg/scheduler/templates/defineConfig.h diff --git a/cmsisdsp/cg/static/scheduler/templates/dot_template.dot b/cmsisdsp/cg/scheduler/templates/dot_template.dot similarity index 100% rename from cmsisdsp/cg/static/scheduler/templates/dot_template.dot rename to cmsisdsp/cg/scheduler/templates/dot_template.dot diff --git a/cmsisdsp/cg/static/scheduler/templates/cmsis.cpp b/cmsisdsp/cg/static/scheduler/templates/cmsis.cpp deleted file mode 100644 index 99136fb0..00000000 --- a/cmsisdsp/cg/static/scheduler/templates/cmsis.cpp +++ /dev/null @@ -1,13 +0,0 @@ -{ -{% for ptr in ptrs %} - {{theType}}* {{ptr}}; -{% endfor %} -{% for ptr in inputs %} - {{ptr[0]}}={{ptr[1]}}.getReadBuffer({{nb}}); -{% endfor %} -{% for ptr in outputs %} - {{ptr[0]}}={{ptr[1]}}.getWriteBuffer({{nb}}); -{% endfor %} - {{func}}({{args}},{{nb}}); - cgStaticError = 0; -} \ No newline at end of file diff --git a/cmsisdsp/cg/static/types.py b/cmsisdsp/cg/types.py similarity index 100% rename from cmsisdsp/cg/static/types.py rename to cmsisdsp/cg/types.py diff --git a/setup.py b/setup.py index 7306d1b7..07bb6dcf 100644 --- a/setup.py +++ b/setup.py @@ -255,11 +255,11 @@ def build(): setup (name = 'cmsisdsp', version = main_ns['__version__'], packages=["cmsisdsp", - "cmsisdsp.cg.static", - "cmsisdsp.cg.static.nodes", - "cmsisdsp.cg.static.nodes.host", - "cmsisdsp.cg.static.scheduler", - "cmsisdsp.cg.static.scheduler.templates"], + "cmsisdsp.cg", + "cmsisdsp.cg.nodes", + "cmsisdsp.cg.nodes.host", + "cmsisdsp.cg.scheduler", + "cmsisdsp.cg.scheduler.templates"], description = 'CMSIS-DSP Python API', long_description=open("PythonWrapper_README.md").read(), long_description_content_type='text/markdown',