From 5a99a5c5ad35363df844fc6b3bca65579ff8f337 Mon Sep 17 00:00:00 2001 From: Christophe Favergeon Date: Mon, 3 Oct 2022 15:39:17 +0200 Subject: [PATCH] Improvement to compute graph More customization options New (experimental) scheduling algorithm to improve latencies. --- ComputeGraph/README.md | 141 +++++++++++-- ComputeGraph/cg/static/src/GenericNodes.h | 22 -- ComputeGraph/examples/CMakeLists.txt | 1 + .../examples/example1/generated/scheduler.cpp | 89 +++++++- .../examples/example1/generated/scheduler.h | 1 + ComputeGraph/examples/example1/graph.py | 4 +- .../examples/example2/generated/scheduler.cpp | 148 ++++++++++---- .../examples/example2/generated/scheduler.h | 1 + ComputeGraph/examples/example2/graph.py | 3 +- .../examples/example3/generated/scheduler.cpp | 119 ++++++++--- .../examples/example3/generated/scheduler.h | 1 + ComputeGraph/examples/example3/graph.py | 2 +- ComputeGraph/examples/example4/graph.py | 6 +- ComputeGraph/examples/example4/sched.py | 12 +- ComputeGraph/examples/example5/graph.py | 7 +- ComputeGraph/examples/example5/sched.py | 120 +++++------ .../examples/example6/generated/scheduler.cpp | 99 +++++++-- .../examples/example6/generated/scheduler.h | 1 + ComputeGraph/examples/example6/graph.py | 7 +- ComputeGraph/examples/example7/graph.py | 7 +- .../examples/example8/generated/scheduler.cpp | 109 ++++++++-- .../examples/example8/generated/scheduler.h | 1 + ComputeGraph/examples/example8/graph.py | 5 +- ComputeGraph/examples/example9/AppNodes.h | 109 ++++++++++ ComputeGraph/examples/example9/CMakeLists.txt | 13 ++ ComputeGraph/examples/example9/custom.h | 3 + .../examples/example9/generated/scheduler.cpp | 188 +++++++++++++++++ .../examples/example9/generated/scheduler.h | 26 +++ ComputeGraph/examples/example9/graph.py | 73 +++++++ ComputeGraph/examples/example9/main.cpp | 11 + ComputeGraph/examples/example9/test.dot | 80 ++++++++ ComputeGraph/examples/example9/test.pdf | Bin 0 -> 22537 bytes PythonWrapper_README.md | 12 +- cmsisdsp/__init__.py | 2 +- cmsisdsp/cg/static/scheduler/config.py | 50 +++-- cmsisdsp/cg/static/scheduler/description.py | 190 +++++++++++++++++- cmsisdsp/cg/static/scheduler/node.py | 13 ++ cmsisdsp/cg/static/scheduler/pythoncode.py | 2 +- .../cg/static/scheduler/templates/code.cpp | 2 + .../static/scheduler/templates/codeArray.cpp | 14 +- .../static/scheduler/templates/codeSwitch.cpp | 2 + .../cg/static/scheduler/templates/commonc.cpp | 10 +- .../static/scheduler/templates/defineConfig.h | 55 +++++ cmsisdsp/version.py | 2 +- 44 files changed, 1497 insertions(+), 266 deletions(-) create mode 100644 ComputeGraph/examples/example9/AppNodes.h create mode 100644 ComputeGraph/examples/example9/CMakeLists.txt create mode 100644 ComputeGraph/examples/example9/custom.h create mode 100644 ComputeGraph/examples/example9/generated/scheduler.cpp create mode 100644 ComputeGraph/examples/example9/generated/scheduler.h create mode 100644 ComputeGraph/examples/example9/graph.py create mode 100644 ComputeGraph/examples/example9/main.cpp create mode 100644 ComputeGraph/examples/example9/test.dot create mode 100644 ComputeGraph/examples/example9/test.pdf create mode 100644 cmsisdsp/cg/static/scheduler/templates/defineConfig.h diff --git a/ComputeGraph/README.md b/ComputeGraph/README.md index 011c01f1..c3d66744 100644 --- a/ComputeGraph/README.md +++ b/ComputeGraph/README.md @@ -113,13 +113,19 @@ The generated files need to include the `ComputeGraph/cg/static/src/GenericNodes If you have declared new nodes in `graph.py` then you'll need to provide an implementation. -More details and explanations can be found in the documentation for the examples: +More details and explanations can be found in the documentation for the examples. The first example is giving all the details about the Python and C++ sides of the tool: * [Example 1 : how to describe a simple graph](documentation/example1.md) * [Example 2 : More complex example with delay and CMSIS-DSP](documentation/example2.md) * [Example 3 : Working example with CMSIS-DSP and FFT](documentation/example3.md) * [Example 4 : Same as example 3 but with the CMSIS-DSP Python wrapper](documentation/example4.md) +Examples 5 and 6 are showing how to use the CMSIS-DSP MFCC with a synchronous data flow. + +Example 7 is communicating with OpenModelica. The Modelica model (PythonTest) in the example is implementing a Larsen effect. + +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. + ## Cyclo static scheduling Beginning with the version 1.7.0 of the Python wrapper and version >= 1.12 of CMSIS-DSP, cyclo static scheduling has been added. @@ -167,9 +173,127 @@ 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. +## 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 scheduling + +#### memoryOptimization (default = False) + +When the amount of data written to a FIFO and read from the FIFO is the same, the FIFO is just an array. In this case, depending on the scheduling, the memory used by different arrays may be reused if those arrays are not needed at the same time. + +This option is enabling an analysis to optimize the memory usage by merging some buffers when it is possible. + +#### sinkPriority (default = True) + +Try to prioritize the scheduling of the sinks to minimize the latency between sources and sinks. + +When this option is enable, the tool may not be able to find a schedule in all cases. If it can't find a schedule, it will raise a `DeadLock` exception. + +#### displayFIFOSizes (default = False) + +During computation of the schedule, the evolution of the FIFO sizes is generated on `stdout`. + +#### dumpSchedule (default = False) + +During computation of the schedule, the human readable schedule is generated on `stdout`. + +### Options for the code generator + +#### debugLimit (default = 0) + +When `debugLimit` is > 0, the number of iterations of the scheduling is limited to `debugLimit`. Otherwise, the scheduling is running forever or until an error has occured. + +#### dumpFIFO (default = False) + +When true, generate some code to dump the FIFO content at runtime. Only useful for debug. + +#### schedName (default = "scheduler") + +Name of the scheduler function used in the generated code. + +#### prefix (default = "") + +Prefix to add before the FIFO buffer definition. Those buffers are not static and are global. If you want to use several schedulers in your code, the buffer names used by each should be different. + +Another possibility would be to make the buffer static by redefining the macro `CG_BEFORE_BUFFER` + +#### Options for C Code Generation only + +##### cOptionalArgs (default = "") + +Optional arguments to pass to the C version of the scheduler function + +##### codeArray (default = True) + +When true, the scheduling is defined as an array. Otherwise, the list of function calls is generated. + +The list of function call may be easier to read but if the schedule is long, it is not good for code size. In that case, it is better to encode the schedule as an array rather than a list of functions. + +When `codeArray` is True, the option `switchCase` is taken into account. + +##### switchCase (default = True) + +`codeArray` must be true or this option is ignored. +When the schedule is encoded as an array, it can either be an array of function pointers (`switchCase` false) or an array of indexes for a state machine (`switchCase` true) -### How to build the examples +##### eventRecorder (default = False) + +Enable the generation of `CMSIS EventRecorder` intrumentation in the code. + +##### customCName (default = "custom.h") + +Name of custom header in generated C code. If you use several scheduler, you may want to use different headers for each one. + +##### genericNodeCName (default = "GenericNodes.h") + +Name of GenericNodes header in generated C code. If you use several scheduler, you may want to use different headers for each one. + +##### appNodesCName (default = "AppNodes.h") + +Name of AppNodes header in generated C code. If you use several scheduler, you may want to use different headers for each one. + +##### schedulerCFileName (default = "scheduler") + +Name of scheduler cpp and header in generated C code. If you use several scheduler, you may want to use different headers for each one. + +If the option is set to `xxx`, the names generated will be `xxx.cpp` and `xxx.h` + +#### Options for Python code generation only + +##### pyOptionalArgs (default = "") + +Optional arguments to pass to the Python version of the scheduler function + +##### customPythonName (default = "custom") + +Name of custom header in generated Python code. If you use several scheduler, you may want to use different headers for each one. + +##### appNodesPythonName (default = "appnodes") + +Name of AppNodes header in generated Python code. If you use several scheduler, you may want to use different headers for each one. + +##### schedulerPythonFileName (default = "sched") + +Name of scheduler file in generated Python code. If you use several scheduler, you may want to use different headers for each one. + +If the option is set to `xxx`, the name generated will be `xxx.py` + +### Options for the graphviz generator + +#### horizontal (default = True) + +Horizontal or vertical layout for the graph. + +#### displayFIFOBuf (default = False) + +By default, the graph is displaying the FIFO sizes. If you want to know with FIFO variable is used in the code, you can set this option to true and the graph will display the FIFO variable names. + + + +## How to build the examples In folder `ComputeGraph/example/build`, type the `cmake` command: @@ -257,16 +381,3 @@ Here is a list of the nodes supported by default. More can be easily added: - WavSink and WavSource to use wav files for testing - VHTSDF : To communicate with OpenModelica using VHTModelica blocks - -## Detailed examples - -- [Example 1 : how to describe a simple graph](documentation/example1.md) -- [Example 2 : More complex example with delay and CMSIS-DSP](documentation/example2.md) -- [Example 3 : Working example with CMSIS-DSP and FFT](documentation/example3.md) -- [Example 4 : Same as example 3 but with the CMSIS-DSP Python wrapper](documentation/example4.md) - -Examples 5 and 6 are showing how to use the CMSIS-DSP MFCC with a synchronous data flow. - -Example 7 is communicating with OpenModelica. The Modelica model (PythonTest) in the example is implementing a Larsen effect. - -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. diff --git a/ComputeGraph/cg/static/src/GenericNodes.h b/ComputeGraph/cg/static/src/GenericNodes.h index 30c6cd5d..be7fd153 100644 --- a/ComputeGraph/cg/static/src/GenericNodes.h +++ b/ComputeGraph/cg/static/src/GenericNodes.h @@ -382,29 +382,7 @@ public: }; -#if !defined(CHECKERROR) -#define CHECKERROR if (cgStaticError < 0) \ - {\ - goto errorHandling;\ - } -#endif - -#if !defined(CG_BEFORE_ITERATION) -#define CG_BEFORE_ITERATION -#endif - -#if !defined(CG_AFTER_ITERATION) -#define CG_AFTER_ITERATION -#endif - -#if !defined(CG_BEFORE_SCHEDULE) -#define CG_BEFORE_SCHEDULE -#endif - -#if !defined(CG_BEFORE_NODE_INIT) -#define CG_BEFORE_NODE_INIT -#endif #endif diff --git a/ComputeGraph/examples/CMakeLists.txt b/ComputeGraph/examples/CMakeLists.txt index 7a526c69..7b7f529d 100644 --- a/ComputeGraph/examples/CMakeLists.txt +++ b/ComputeGraph/examples/CMakeLists.txt @@ -51,3 +51,4 @@ add_subdirectory(example2 bin_example2) add_subdirectory(example3 bin_example3) add_subdirectory(example6 bin_example6) add_subdirectory(example8 bin_example8) +add_subdirectory(example9 bin_example9) diff --git a/ComputeGraph/examples/example1/generated/scheduler.cpp b/ComputeGraph/examples/example1/generated/scheduler.cpp index 2f8034f4..e3825ee0 100644 --- a/ComputeGraph/examples/example1/generated/scheduler.cpp +++ b/ComputeGraph/examples/example1/generated/scheduler.cpp @@ -14,10 +14,67 @@ The support classes and code is covered by CMSIS-DSP license. #include "AppNodes.h" #include "scheduler.h" +#if !defined(CHECKERROR) +#define CHECKERROR if (cgStaticError < 0) \ + {\ + goto errorHandling;\ + } + +#endif + +#if !defined(CG_BEFORE_ITERATION) +#define CG_BEFORE_ITERATION +#endif + +#if !defined(CG_AFTER_ITERATION) +#define CG_AFTER_ITERATION +#endif + +#if !defined(CG_BEFORE_SCHEDULE) +#define CG_BEFORE_SCHEDULE +#endif + +#if !defined(CG_AFTER_SCHEDULE) +#define CG_AFTER_SCHEDULE +#endif + +#if !defined(CG_BEFORE_BUFFER) +#define CG_BEFORE_BUFFER +#endif + +#if !defined(CG_BEFORE_FIFO_BUFFERS) +#define CG_BEFORE_FIFO_BUFFERS +#endif + +#if !defined(CG_BEFORE_FIFO_INIT) +#define CG_BEFORE_FIFO_INIT +#endif + +#if !defined(CG_BEFORE_NODE_INIT) +#define CG_BEFORE_NODE_INIT +#endif + +#if !defined(CG_AFTER_INCLUDES) +#define CG_AFTER_INCLUDES +#endif + +#if !defined(CG_BEFORE_SCHEDULER_FUNCTION) +#define CG_BEFORE_SCHEDULER_FUNCTION +#endif + +#if !defined(CG_BEFORE_NODE_EXECUTION) +#define CG_BEFORE_NODE_EXECUTION +#endif + +#if !defined(CG_AFTER_NODE_EXECUTION) +#define CG_AFTER_NODE_EXECUTION +#endif + +CG_AFTER_INCLUDES + /* -Description of the scheduling. It is a list of nodes to call. -The values are indexes in the previous array. +Description of the scheduling. */ static unsigned int schedule[17]= @@ -25,6 +82,7 @@ static unsigned int schedule[17]= 2,2,0,1,2,0,1,2,2,0,1,2,0,1,2,0,1, }; +CG_BEFORE_FIFO_BUFFERS /*********** FIFO buffers @@ -33,25 +91,30 @@ FIFO buffers #define FIFOSIZE0 11 #define FIFOSIZE1 5 -#define BUFFERSIZE0 11 -float32_t buf0[BUFFERSIZE0]={0}; - -#define BUFFERSIZE1 5 +#define BUFFERSIZE1 11 +CG_BEFORE_BUFFER float32_t buf1[BUFFERSIZE1]={0}; +#define BUFFERSIZE2 5 +CG_BEFORE_BUFFER +float32_t buf2[BUFFERSIZE2]={0}; + +CG_BEFORE_SCHEDULER_FUNCTION uint32_t scheduler(int *error,int someVariable) { int cgStaticError=0; uint32_t nbSchedule=0; int32_t debugCounter=1; + CG_BEFORE_FIFO_INIT; /* Create FIFOs objects */ - FIFO fifo0(buf0); - FIFO fifo1(buf1); + FIFO fifo0(buf1); + FIFO fifo1(buf2); + CG_BEFORE_NODE_INIT; /* Create node objects */ @@ -60,43 +123,47 @@ uint32_t scheduler(int *error,int someVariable) Source source(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 < 17; id++) { + CG_BEFORE_NODE_EXECUTION; switch(schedule[id]) { case 0: { cgStaticError = filter.run(); - CHECKERROR; } break; case 1: { cgStaticError = sink.run(); - CHECKERROR; } break; case 2: { cgStaticError = source.run(); - CHECKERROR; } 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/example1/generated/scheduler.h b/ComputeGraph/examples/example1/generated/scheduler.h index 3a0f0ccc..a907ece2 100644 --- a/ComputeGraph/examples/example1/generated/scheduler.h +++ b/ComputeGraph/examples/example1/generated/scheduler.h @@ -15,6 +15,7 @@ extern "C" { #endif + extern uint32_t scheduler(int *error,int someVariable); #ifdef __cplusplus diff --git a/ComputeGraph/examples/example1/graph.py b/ComputeGraph/examples/example1/graph.py index f0ba1901..7c2981f4 100644 --- a/ComputeGraph/examples/example1/graph.py +++ b/ComputeGraph/examples/example1/graph.py @@ -57,8 +57,8 @@ conf.cOptionalArgs="int someVariable" # Prefix for global FIFO buffers #conf.prefix="sched1" -#print(g.nullVector()) -sched = g.computeSchedule() +#conf.dumpSchedule = True +sched = g.computeSchedule(config=conf) #print(sched.schedule) print("Schedule length = %d" % sched.scheduleLength) print("Memory usage %d bytes" % sched.memory) diff --git a/ComputeGraph/examples/example2/generated/scheduler.cpp b/ComputeGraph/examples/example2/generated/scheduler.cpp index 577d2791..d2c9a19f 100644 --- a/ComputeGraph/examples/example2/generated/scheduler.cpp +++ b/ComputeGraph/examples/example2/generated/scheduler.cpp @@ -14,24 +14,82 @@ The support classes and code is covered by CMSIS-DSP license. #include "AppNodes.h" #include "scheduler.h" +#if !defined(CHECKERROR) +#define CHECKERROR if (cgStaticError < 0) \ + {\ + goto errorHandling;\ + } + +#endif + +#if !defined(CG_BEFORE_ITERATION) +#define CG_BEFORE_ITERATION +#endif + +#if !defined(CG_AFTER_ITERATION) +#define CG_AFTER_ITERATION +#endif + +#if !defined(CG_BEFORE_SCHEDULE) +#define CG_BEFORE_SCHEDULE +#endif + +#if !defined(CG_AFTER_SCHEDULE) +#define CG_AFTER_SCHEDULE +#endif + +#if !defined(CG_BEFORE_BUFFER) +#define CG_BEFORE_BUFFER +#endif + +#if !defined(CG_BEFORE_FIFO_BUFFERS) +#define CG_BEFORE_FIFO_BUFFERS +#endif + +#if !defined(CG_BEFORE_FIFO_INIT) +#define CG_BEFORE_FIFO_INIT +#endif + +#if !defined(CG_BEFORE_NODE_INIT) +#define CG_BEFORE_NODE_INIT +#endif + +#if !defined(CG_AFTER_INCLUDES) +#define CG_AFTER_INCLUDES +#endif + +#if !defined(CG_BEFORE_SCHEDULER_FUNCTION) +#define CG_BEFORE_SCHEDULER_FUNCTION +#endif + +#if !defined(CG_BEFORE_NODE_EXECUTION) +#define CG_BEFORE_NODE_EXECUTION +#endif + +#if !defined(CG_AFTER_NODE_EXECUTION) +#define CG_AFTER_NODE_EXECUTION +#endif + +CG_AFTER_INCLUDES + /* -Description of the scheduling. It is a list of nodes to call. -The values are indexes in the previous array. +Description of the scheduling. */ static unsigned int schedule[302]= { -7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2, -1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3, -2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5, -7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2, -1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3, -2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5, -7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,7,8,3,2, -1,7,8,3,2,1,4,5,7,8,3,2,1,7,8,3,2,1,4,5,6,0, +7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3, +1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2, +3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5, +7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3, +1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2, +3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5, +7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,7,8,2,3, +1,7,8,2,3,1,4,5,7,8,2,3,1,7,8,2,3,1,4,5,6,0, }; +CG_BEFORE_FIFO_BUFFERS /*********** FIFO buffers @@ -47,53 +105,65 @@ FIFO buffers #define FIFOSIZE7 250 #define FIFOSIZE8 500 -#define BUFFERSIZE0 330 -float32_t buf0[BUFFERSIZE0]={0}; - -#define BUFFERSIZE1 160 +#define BUFFERSIZE1 330 +CG_BEFORE_BUFFER float32_t buf1[BUFFERSIZE1]={0}; #define BUFFERSIZE2 160 +CG_BEFORE_BUFFER float32_t buf2[BUFFERSIZE2]={0}; #define BUFFERSIZE3 160 +CG_BEFORE_BUFFER float32_t buf3[BUFFERSIZE3]={0}; #define BUFFERSIZE4 160 +CG_BEFORE_BUFFER float32_t buf4[BUFFERSIZE4]={0}; -#define BUFFERSIZE5 320 +#define BUFFERSIZE5 160 +CG_BEFORE_BUFFER float32_t buf5[BUFFERSIZE5]={0}; -#define BUFFERSIZE6 640 +#define BUFFERSIZE6 320 +CG_BEFORE_BUFFER float32_t buf6[BUFFERSIZE6]={0}; -#define BUFFERSIZE7 250 +#define BUFFERSIZE7 640 +CG_BEFORE_BUFFER float32_t buf7[BUFFERSIZE7]={0}; -#define BUFFERSIZE8 500 +#define BUFFERSIZE8 250 +CG_BEFORE_BUFFER float32_t buf8[BUFFERSIZE8]={0}; +#define BUFFERSIZE9 500 +CG_BEFORE_BUFFER +float32_t buf9[BUFFERSIZE9]={0}; + +CG_BEFORE_SCHEDULER_FUNCTION uint32_t scheduler(int *error,int opt1,int opt2) { int cgStaticError=0; uint32_t nbSchedule=0; int32_t debugCounter=1; + CG_BEFORE_FIFO_INIT; /* Create FIFOs objects */ - FIFO fifo0(buf0,10); - FIFO fifo1(buf1); - FIFO fifo2(buf2); - FIFO fifo3(buf3); - FIFO fifo4(buf4); - FIFO fifo5(buf5); - FIFO fifo6(buf6); - FIFO fifo7(buf7); - FIFO fifo8(buf8); - + 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; /* Create node objects */ @@ -105,17 +175,19 @@ uint32_t scheduler(int *error,int opt1,int opt2) Unzip toMono(fifo0,fifo1,fifo2); /* 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 < 302; id++) { + CG_BEFORE_NODE_EXECUTION; switch(schedule[id]) { case 0: { cgStaticError = TFLite.run(); - CHECKERROR; } break; @@ -130,8 +202,7 @@ uint32_t scheduler(int *error,int opt1,int opt2) o2=fifo5.getWriteBuffer(160); arm_add_f32(i0,i1,o2,160); cgStaticError = 0; - } - CHECKERROR; +} } break; @@ -144,8 +215,7 @@ uint32_t scheduler(int *error,int opt1,int opt2) o2=fifo3.getWriteBuffer(160); arm_scale_f32(i0,HALF,o2,160); cgStaticError = 0; - } - CHECKERROR; +} } break; @@ -158,55 +228,53 @@ uint32_t scheduler(int *error,int opt1,int opt2) o2=fifo4.getWriteBuffer(160); arm_scale_f32(i0,HALF,o2,160); cgStaticError = 0; - } - CHECKERROR; +} } break; case 4: { cgStaticError = audioWin.run(); - CHECKERROR; } break; case 5: { cgStaticError = mfcc.run(); - CHECKERROR; } break; case 6: { cgStaticError = mfccWind.run(); - CHECKERROR; } break; case 7: { cgStaticError = src.run(); - CHECKERROR; } break; case 8: { cgStaticError = toMono.run(); - CHECKERROR; } 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/example2/generated/scheduler.h b/ComputeGraph/examples/example2/generated/scheduler.h index 68010aa0..b44ccba3 100644 --- a/ComputeGraph/examples/example2/generated/scheduler.h +++ b/ComputeGraph/examples/example2/generated/scheduler.h @@ -15,6 +15,7 @@ extern "C" { #endif + extern uint32_t scheduler(int *error,int opt1,int opt2); #ifdef __cplusplus diff --git a/ComputeGraph/examples/example2/graph.py b/ComputeGraph/examples/example2/graph.py index 815dce4d..9868d544 100644 --- a/ComputeGraph/examples/example2/graph.py +++ b/ComputeGraph/examples/example2/graph.py @@ -93,8 +93,9 @@ print("Generate graphviz and code") conf=Configuration() conf.debugLimit=1 conf.cOptionalArgs="int opt1,int opt2" +conf.sinkPriority = True #conf.memoryOptimization=True -#print(g.nullVector()) +#conf.dumpSchedule = True sched = g.computeSchedule(conf) #print(sched.schedule) print("Schedule length = %d" % sched.scheduleLength) diff --git a/ComputeGraph/examples/example3/generated/scheduler.cpp b/ComputeGraph/examples/example3/generated/scheduler.cpp index d5f8f959..26710576 100644 --- a/ComputeGraph/examples/example3/generated/scheduler.cpp +++ b/ComputeGraph/examples/example3/generated/scheduler.cpp @@ -14,17 +14,75 @@ The support classes and code is covered by CMSIS-DSP license. #include "AppNodes.h" #include "scheduler.h" +#if !defined(CHECKERROR) +#define CHECKERROR if (cgStaticError < 0) \ + {\ + goto errorHandling;\ + } + +#endif + +#if !defined(CG_BEFORE_ITERATION) +#define CG_BEFORE_ITERATION +#endif + +#if !defined(CG_AFTER_ITERATION) +#define CG_AFTER_ITERATION +#endif + +#if !defined(CG_BEFORE_SCHEDULE) +#define CG_BEFORE_SCHEDULE +#endif + +#if !defined(CG_AFTER_SCHEDULE) +#define CG_AFTER_SCHEDULE +#endif + +#if !defined(CG_BEFORE_BUFFER) +#define CG_BEFORE_BUFFER +#endif + +#if !defined(CG_BEFORE_FIFO_BUFFERS) +#define CG_BEFORE_FIFO_BUFFERS +#endif + +#if !defined(CG_BEFORE_FIFO_INIT) +#define CG_BEFORE_FIFO_INIT +#endif + +#if !defined(CG_BEFORE_NODE_INIT) +#define CG_BEFORE_NODE_INIT +#endif + +#if !defined(CG_AFTER_INCLUDES) +#define CG_AFTER_INCLUDES +#endif + +#if !defined(CG_BEFORE_SCHEDULER_FUNCTION) +#define CG_BEFORE_SCHEDULER_FUNCTION +#endif + +#if !defined(CG_BEFORE_NODE_EXECUTION) +#define CG_BEFORE_NODE_EXECUTION +#endif + +#if !defined(CG_AFTER_NODE_EXECUTION) +#define CG_AFTER_NODE_EXECUTION +#endif + +CG_AFTER_INCLUDES + /* -Description of the scheduling. It is a list of nodes to call. -The values are indexes in the previous array. +Description of the scheduling. */ static unsigned int schedule[25]= { -6,2,0,7,3,4,8,1,6,2,0,7,3,4,8,2,0,7,3,4,1,5,8,1,5, +6,2,0,7,3,4,8,1,6,2,0,7,3,4,8,1,5,2,0,7,3,4,8,1,5, }; +CG_BEFORE_FIFO_BUFFERS /*********** FIFO buffers @@ -39,49 +97,60 @@ FIFO buffers #define FIFOSIZE6 256 #define FIFOSIZE7 256 -#define BUFFERSIZE0 256 -float32_t buf0[BUFFERSIZE0]={0}; - #define BUFFERSIZE1 256 +CG_BEFORE_BUFFER float32_t buf1[BUFFERSIZE1]={0}; #define BUFFERSIZE2 256 +CG_BEFORE_BUFFER float32_t buf2[BUFFERSIZE2]={0}; -#define BUFFERSIZE3 512 +#define BUFFERSIZE3 256 +CG_BEFORE_BUFFER float32_t buf3[BUFFERSIZE3]={0}; #define BUFFERSIZE4 512 +CG_BEFORE_BUFFER float32_t buf4[BUFFERSIZE4]={0}; #define BUFFERSIZE5 512 +CG_BEFORE_BUFFER float32_t buf5[BUFFERSIZE5]={0}; -#define BUFFERSIZE6 256 +#define BUFFERSIZE6 512 +CG_BEFORE_BUFFER float32_t buf6[BUFFERSIZE6]={0}; #define BUFFERSIZE7 256 +CG_BEFORE_BUFFER float32_t buf7[BUFFERSIZE7]={0}; +#define BUFFERSIZE8 256 +CG_BEFORE_BUFFER +float32_t buf8[BUFFERSIZE8]={0}; + +CG_BEFORE_SCHEDULER_FUNCTION uint32_t scheduler(int *error) { int cgStaticError=0; uint32_t nbSchedule=0; int32_t debugCounter=40; + CG_BEFORE_FIFO_INIT; /* Create FIFOs objects */ - FIFO fifo0(buf0); - FIFO fifo1(buf1); - FIFO fifo2(buf2); - FIFO fifo3(buf3); - FIFO fifo4(buf4); - FIFO fifo5(buf5); - FIFO fifo6(buf6); - FIFO fifo7(buf7); - + 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; /* Create node objects */ @@ -95,11 +164,14 @@ uint32_t scheduler(int *error) ToReal toReal(fifo5,fifo6); /* 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 < 25; id++) { + CG_BEFORE_NODE_EXECUTION; switch(schedule[id]) { case 0: @@ -112,75 +184,70 @@ uint32_t scheduler(int *error) arm_mult_f32(i0,HANN,o2,256); cgStaticError = 0; } - CHECKERROR; } break; case 1: { cgStaticError = audioOverlap.run(); - CHECKERROR; } break; case 2: { cgStaticError = audioWin.run(); - CHECKERROR; } break; case 3: { cgStaticError = cfft.run(); - CHECKERROR; } break; case 4: { cgStaticError = icfft.run(); - CHECKERROR; } break; case 5: { cgStaticError = sink.run(); - CHECKERROR; } break; case 6: { cgStaticError = src.run(); - CHECKERROR; } break; case 7: { cgStaticError = toCmplx.run(); - CHECKERROR; } break; case 8: { cgStaticError = toReal.run(); - CHECKERROR; } 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/example3/generated/scheduler.h b/ComputeGraph/examples/example3/generated/scheduler.h index ebe9978c..104de2b1 100644 --- a/ComputeGraph/examples/example3/generated/scheduler.h +++ b/ComputeGraph/examples/example3/generated/scheduler.h @@ -15,6 +15,7 @@ extern "C" { #endif + extern uint32_t scheduler(int *error); #ifdef __cplusplus diff --git a/ComputeGraph/examples/example3/graph.py b/ComputeGraph/examples/example3/graph.py index 4ea21492..17600c54 100644 --- a/ComputeGraph/examples/example3/graph.py +++ b/ComputeGraph/examples/example3/graph.py @@ -52,7 +52,7 @@ conf=Configuration() conf.debugLimit=40 #conf.memoryOptimization=True -#print(g.nullVector()) +#conf.dumpSchedule = True sched = g.computeSchedule(conf) #print(sched.schedule) print("Schedule length = %d" % sched.scheduleLength) diff --git a/ComputeGraph/examples/example4/graph.py b/ComputeGraph/examples/example4/graph.py index 52cd9b43..17c70f8d 100644 --- a/ComputeGraph/examples/example4/graph.py +++ b/ComputeGraph/examples/example4/graph.py @@ -52,14 +52,14 @@ g.connect(overlap.o,sink.i) print("Generate graphviz and code") +conf=Configuration() -#print(g.nullVector()) -sched = g.computeSchedule() +#conf.dumpSchedule = True +sched = g.computeSchedule(conf) #print(sched.schedule) print("Schedule length = %d" % sched.scheduleLength) print("Memory usage %d bytes" % sched.memory) # -conf=Configuration() conf.debugLimit=42 conf.pyOptionalArgs="dispbuf" #conf.dumpFIFO=True diff --git a/ComputeGraph/examples/example4/sched.py b/ComputeGraph/examples/example4/sched.py index 3e2857e5..4fbeff85 100644 --- a/ComputeGraph/examples/example4/sched.py +++ b/ComputeGraph/examples/example4/sched.py @@ -155,6 +155,12 @@ def scheduler(dispbuf): if cgStaticError < 0: break cgStaticError = toReal.run() + if cgStaticError < 0: + break + cgStaticError = audioOverlap.run() + if cgStaticError < 0: + break + cgStaticError = sink.run() if cgStaticError < 0: break cgStaticError = audioWin.run() @@ -175,12 +181,6 @@ def scheduler(dispbuf): if cgStaticError < 0: break cgStaticError = icfft.run() - if cgStaticError < 0: - break - cgStaticError = audioOverlap.run() - if cgStaticError < 0: - break - cgStaticError = sink.run() if cgStaticError < 0: break cgStaticError = toReal.run() diff --git a/ComputeGraph/examples/example5/graph.py b/ComputeGraph/examples/example5/graph.py index 97d7fda2..6ad75244 100644 --- a/ComputeGraph/examples/example5/graph.py +++ b/ComputeGraph/examples/example5/graph.py @@ -42,13 +42,12 @@ g.connect(slidingMFCC.o,sink.i) print("Generate graphviz and code") - - -sched = g.computeSchedule() +conf=Configuration() +#conf.dumpSchedule = True +sched = g.computeSchedule(conf) print("Schedule length = %d" % sched.scheduleLength) print("Memory usage %d bytes" % sched.memory) # -conf=Configuration() conf.debugLimit=12 conf.pyOptionalArgs="mfccConfig,dispbuf" diff --git a/ComputeGraph/examples/example5/sched.py b/ComputeGraph/examples/example5/sched.py index b1b66f3d..06f3b8cb 100644 --- a/ComputeGraph/examples/example5/sched.py +++ b/ComputeGraph/examples/example5/sched.py @@ -101,15 +101,9 @@ def scheduler(mfccConfig,dispbuf): if cgStaticError < 0: break cgStaticError = toMono.run() - if cgStaticError < 0: - break - cgStaticError = src.run() if cgStaticError < 0: break cgStaticError = audioWin.run() - if cgStaticError < 0: - break - cgStaticError = toMono.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -136,10 +130,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -166,10 +160,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -196,10 +190,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -226,10 +220,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -256,10 +250,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -286,10 +280,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -316,10 +310,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -346,10 +340,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -376,10 +370,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -406,10 +400,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -436,10 +430,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -466,10 +460,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -496,10 +490,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -526,10 +520,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -556,10 +550,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -586,10 +580,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -616,10 +610,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -646,10 +640,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -676,10 +670,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -706,10 +700,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -736,10 +730,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -766,10 +760,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -796,10 +790,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -826,10 +820,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -856,10 +850,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -886,10 +880,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -916,10 +910,10 @@ def scheduler(mfccConfig,dispbuf): cgStaticError = src.run() if cgStaticError < 0: break - cgStaticError = audioWin.run() + cgStaticError = toMono.run() if cgStaticError < 0: break - cgStaticError = toMono.run() + cgStaticError = audioWin.run() if cgStaticError < 0: break cgStaticError = mfcc.run() @@ -941,6 +935,12 @@ def scheduler(mfccConfig,dispbuf): if cgStaticError < 0: break cgStaticError = toMono.run() + if cgStaticError < 0: + break + cgStaticError = src.run() + if cgStaticError < 0: + break + cgStaticError = toMono.run() if cgStaticError < 0: break cgStaticError = audioWin.run() diff --git a/ComputeGraph/examples/example6/generated/scheduler.cpp b/ComputeGraph/examples/example6/generated/scheduler.cpp index 8556b118..008b55fe 100644 --- a/ComputeGraph/examples/example6/generated/scheduler.cpp +++ b/ComputeGraph/examples/example6/generated/scheduler.cpp @@ -14,10 +14,67 @@ The support classes and code is covered by CMSIS-DSP license. #include "AppNodes.h" #include "scheduler.h" +#if !defined(CHECKERROR) +#define CHECKERROR if (cgStaticError < 0) \ + {\ + goto errorHandling;\ + } + +#endif + +#if !defined(CG_BEFORE_ITERATION) +#define CG_BEFORE_ITERATION +#endif + +#if !defined(CG_AFTER_ITERATION) +#define CG_AFTER_ITERATION +#endif + +#if !defined(CG_BEFORE_SCHEDULE) +#define CG_BEFORE_SCHEDULE +#endif + +#if !defined(CG_AFTER_SCHEDULE) +#define CG_AFTER_SCHEDULE +#endif + +#if !defined(CG_BEFORE_BUFFER) +#define CG_BEFORE_BUFFER +#endif + +#if !defined(CG_BEFORE_FIFO_BUFFERS) +#define CG_BEFORE_FIFO_BUFFERS +#endif + +#if !defined(CG_BEFORE_FIFO_INIT) +#define CG_BEFORE_FIFO_INIT +#endif + +#if !defined(CG_BEFORE_NODE_INIT) +#define CG_BEFORE_NODE_INIT +#endif + +#if !defined(CG_AFTER_INCLUDES) +#define CG_AFTER_INCLUDES +#endif + +#if !defined(CG_BEFORE_SCHEDULER_FUNCTION) +#define CG_BEFORE_SCHEDULER_FUNCTION +#endif + +#if !defined(CG_BEFORE_NODE_EXECUTION) +#define CG_BEFORE_NODE_EXECUTION +#endif + +#if !defined(CG_AFTER_NODE_EXECUTION) +#define CG_AFTER_NODE_EXECUTION +#endif + +CG_AFTER_INCLUDES + /* -Description of the scheduling. It is a list of nodes to call. -The values are indexes in the previous array. +Description of the scheduling. */ static unsigned int schedule[17]= @@ -25,6 +82,7 @@ static unsigned int schedule[17]= 4,0,1,2,3,3,4,0,1,2,3,3,0,1,2,3,3, }; +CG_BEFORE_FIFO_BUFFERS /*********** FIFO buffers @@ -35,33 +93,40 @@ FIFO buffers #define FIFOSIZE2 13 #define FIFOSIZE3 26 -#define BUFFERSIZE0 256 -float32_t buf0[BUFFERSIZE0]={0}; - #define BUFFERSIZE1 256 +CG_BEFORE_BUFFER float32_t buf1[BUFFERSIZE1]={0}; -#define BUFFERSIZE2 13 +#define BUFFERSIZE2 256 +CG_BEFORE_BUFFER float32_t buf2[BUFFERSIZE2]={0}; -#define BUFFERSIZE3 26 +#define BUFFERSIZE3 13 +CG_BEFORE_BUFFER float32_t buf3[BUFFERSIZE3]={0}; +#define BUFFERSIZE4 26 +CG_BEFORE_BUFFER +float32_t buf4[BUFFERSIZE4]={0}; + +CG_BEFORE_SCHEDULER_FUNCTION uint32_t scheduler(int *error,arm_mfcc_instance_f32 *mfccConfig) { int cgStaticError=0; uint32_t nbSchedule=0; int32_t debugCounter=1; + CG_BEFORE_FIFO_INIT; /* Create FIFOs objects */ - FIFO fifo0(buf0); - FIFO fifo1(buf1); - FIFO fifo2(buf2); - FIFO fifo3(buf3); + FIFO fifo0(buf1); + FIFO fifo1(buf2); + FIFO fifo2(buf3); + FIFO fifo3(buf4); + CG_BEFORE_NODE_INIT; /* Create node objects */ @@ -72,57 +137,59 @@ uint32_t scheduler(int *error,arm_mfcc_instance_f32 *mfccConfig) FileSource src(fifo0,"input_example6.txt"); /* 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 < 17; id++) { + CG_BEFORE_NODE_EXECUTION; switch(schedule[id]) { case 0: { cgStaticError = audioWin.run(); - CHECKERROR; } break; case 1: { cgStaticError = mfcc.run(); - CHECKERROR; } break; case 2: { cgStaticError = mfccWin.run(); - CHECKERROR; } break; case 3: { cgStaticError = sink.run(); - CHECKERROR; } break; case 4: { cgStaticError = src.run(); - CHECKERROR; } 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/example6/generated/scheduler.h b/ComputeGraph/examples/example6/generated/scheduler.h index df2238b7..7ab9fe75 100644 --- a/ComputeGraph/examples/example6/generated/scheduler.h +++ b/ComputeGraph/examples/example6/generated/scheduler.h @@ -15,6 +15,7 @@ extern "C" { #endif + extern uint32_t scheduler(int *error,arm_mfcc_instance_f32 *mfccConfig); #ifdef __cplusplus diff --git a/ComputeGraph/examples/example6/graph.py b/ComputeGraph/examples/example6/graph.py index fd3755e3..4ff9fb28 100644 --- a/ComputeGraph/examples/example6/graph.py +++ b/ComputeGraph/examples/example6/graph.py @@ -38,13 +38,12 @@ g.connect(slidingMFCC.o,sink.i) print("Generate graphviz and code") - - -sched = g.computeSchedule() +conf=Configuration() +#conf.dumpSchedule = True +sched = g.computeSchedule(conf) print("Schedule length = %d" % sched.scheduleLength) print("Memory usage %d bytes" % sched.memory) # -conf=Configuration() conf.debugLimit=1 conf.cOptionalArgs="arm_mfcc_instance_f32 *mfccConfig" diff --git a/ComputeGraph/examples/example7/graph.py b/ComputeGraph/examples/example7/graph.py index cc1174ad..47b1a7d9 100644 --- a/ComputeGraph/examples/example7/graph.py +++ b/ComputeGraph/examples/example7/graph.py @@ -32,13 +32,14 @@ print("Generate graphviz and code") -#print(g.nullVector()) -sched = g.computeSchedule() +conf=Configuration() +#conf.dumpSchedule = True +sched = g.computeSchedule(conf) #print(sched.schedule) print("Schedule length = %d" % sched.scheduleLength) print("Memory usage %d bytes" % sched.memory) # -conf=Configuration() + # Pass the source and sink objects used to communicate with the VHT Modelica block #conf.pyOptionalArgs="" conf.pathToSDFModule="C:\\\\benchresults\\\\cmsis_docker\\\\CMSIS\\\\DSP\\\\SDFTools" diff --git a/ComputeGraph/examples/example8/generated/scheduler.cpp b/ComputeGraph/examples/example8/generated/scheduler.cpp index 954c8165..ff2ea63c 100644 --- a/ComputeGraph/examples/example8/generated/scheduler.cpp +++ b/ComputeGraph/examples/example8/generated/scheduler.cpp @@ -14,17 +14,75 @@ The support classes and code is covered by CMSIS-DSP license. #include "AppNodes.h" #include "scheduler.h" +#if !defined(CHECKERROR) +#define CHECKERROR if (cgStaticError < 0) \ + {\ + goto errorHandling;\ + } + +#endif + +#if !defined(CG_BEFORE_ITERATION) +#define CG_BEFORE_ITERATION +#endif + +#if !defined(CG_AFTER_ITERATION) +#define CG_AFTER_ITERATION +#endif + +#if !defined(CG_BEFORE_SCHEDULE) +#define CG_BEFORE_SCHEDULE +#endif + +#if !defined(CG_AFTER_SCHEDULE) +#define CG_AFTER_SCHEDULE +#endif + +#if !defined(CG_BEFORE_BUFFER) +#define CG_BEFORE_BUFFER +#endif + +#if !defined(CG_BEFORE_FIFO_BUFFERS) +#define CG_BEFORE_FIFO_BUFFERS +#endif + +#if !defined(CG_BEFORE_FIFO_INIT) +#define CG_BEFORE_FIFO_INIT +#endif + +#if !defined(CG_BEFORE_NODE_INIT) +#define CG_BEFORE_NODE_INIT +#endif + +#if !defined(CG_AFTER_INCLUDES) +#define CG_AFTER_INCLUDES +#endif + +#if !defined(CG_BEFORE_SCHEDULER_FUNCTION) +#define CG_BEFORE_SCHEDULER_FUNCTION +#endif + +#if !defined(CG_BEFORE_NODE_EXECUTION) +#define CG_BEFORE_NODE_EXECUTION +#endif + +#if !defined(CG_AFTER_NODE_EXECUTION) +#define CG_AFTER_NODE_EXECUTION +#endif + +CG_AFTER_INCLUDES + /* -Description of the scheduling. It is a list of nodes to call. -The values are indexes in the previous array. +Description of the scheduling. */ static unsigned int schedule[37]= { -6,6,1,5,0,4,3,2,6,1,6,5,0,4,3,2,6,1,5,0,4,3,2,6,1,6,5,0,4,3,2,1,5,0,4,3,2, +6,6,1,5,0,2,3,4,6,1,5,0,2,3,4,6,6,1,5,0,2,3,4,6,1,5,0,2,3,4,6,1,5,0,2,3,4, }; +CG_BEFORE_FIFO_BUFFERS /*********** FIFO buffers @@ -37,41 +95,50 @@ FIFO buffers #define FIFOSIZE4 5 #define FIFOSIZE5 5 -#define BUFFERSIZE0 11 -complex buf0[BUFFERSIZE0]={0}; - -#define BUFFERSIZE1 5 +#define BUFFERSIZE1 11 +CG_BEFORE_BUFFER complex buf1[BUFFERSIZE1]={0}; #define BUFFERSIZE2 5 +CG_BEFORE_BUFFER complex buf2[BUFFERSIZE2]={0}; #define BUFFERSIZE3 5 +CG_BEFORE_BUFFER complex buf3[BUFFERSIZE3]={0}; #define BUFFERSIZE4 5 +CG_BEFORE_BUFFER complex buf4[BUFFERSIZE4]={0}; #define BUFFERSIZE5 5 +CG_BEFORE_BUFFER complex buf5[BUFFERSIZE5]={0}; +#define BUFFERSIZE6 5 +CG_BEFORE_BUFFER +complex buf6[BUFFERSIZE6]={0}; + +CG_BEFORE_SCHEDULER_FUNCTION uint32_t scheduler(int *error,int someVariable) { int cgStaticError=0; uint32_t nbSchedule=0; int32_t debugCounter=1; + CG_BEFORE_FIFO_INIT; /* Create FIFOs objects */ - FIFO fifo0(buf0); - FIFO fifo1(buf1); - FIFO fifo2(buf2); - FIFO fifo3(buf3); - FIFO fifo4(buf4); - FIFO fifo5(buf5); - + FIFO fifo0(buf1); + FIFO fifo1(buf2); + FIFO fifo2(buf3); + FIFO fifo3(buf4); + FIFO fifo4(buf5); + FIFO fifo5(buf6); + + CG_BEFORE_NODE_INIT; /* Create node objects */ @@ -84,71 +151,71 @@ uint32_t scheduler(int *error,int someVariable) Source source(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 < 37; id++) { + CG_BEFORE_NODE_EXECUTION; switch(schedule[id]) { case 0: { cgStaticError = dup0.run(); - CHECKERROR; } break; case 1: { cgStaticError = filter.run(); - CHECKERROR; } break; case 2: { cgStaticError = sa.run(); - CHECKERROR; } break; case 3: { cgStaticError = sb.run(); - CHECKERROR; } break; case 4: { cgStaticError = sc.run(); - CHECKERROR; } break; case 5: { cgStaticError = sd.run(); - CHECKERROR; } break; case 6: { cgStaticError = source.run(); - CHECKERROR; } 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/example8/generated/scheduler.h b/ComputeGraph/examples/example8/generated/scheduler.h index 3a0f0ccc..a907ece2 100644 --- a/ComputeGraph/examples/example8/generated/scheduler.h +++ b/ComputeGraph/examples/example8/generated/scheduler.h @@ -15,6 +15,7 @@ extern "C" { #endif + extern uint32_t scheduler(int *error,int someVariable); #ifdef __cplusplus diff --git a/ComputeGraph/examples/example8/graph.py b/ComputeGraph/examples/example8/graph.py index 270dff3f..118c6db3 100644 --- a/ComputeGraph/examples/example8/graph.py +++ b/ComputeGraph/examples/example8/graph.py @@ -75,7 +75,9 @@ GEN_PYTHON = False print("Generate graphviz and code") -sched = g.computeSchedule() +conf=Configuration() +#conf.dumpSchedule = True +sched = g.computeSchedule(conf) print("Schedule length = %d" % sched.scheduleLength) print("Memory usage %d bytes" % sched.memory) @@ -83,7 +85,6 @@ print("Memory usage %d bytes" % sched.memory) # (Introduction of duplicate nodes ...) # So we cannot reuse the graph to compute the Python and the C # code generation -conf=Configuration() conf.debugLimit=1 conf.cOptionalArgs="int someVariable" #conf.codeArray=True diff --git a/ComputeGraph/examples/example9/AppNodes.h b/ComputeGraph/examples/example9/AppNodes.h new file mode 100644 index 00000000..53e311ac --- /dev/null +++ b/ComputeGraph/examples/example9/AppNodes.h @@ -0,0 +1,109 @@ +/* ---------------------------------------------------------------------- + * 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 + +template +class Sink: public GenericSink +{ +public: + Sink(FIFOBase &src):GenericSink(src){}; + + int run() + { + IN *b=this->getReadBuffer(); + printf("Sink\n"); + for(int i=0;i +class Source: GenericSource +{ +public: + Source(FIFOBase &dst):GenericSource(dst),mCounter(0){}; + + int run(){ + OUT *b=this->getWriteBuffer(); + + printf("Source\n"); + for(int i=0;i +class ProcessingNode; + +template +class ProcessingNode: + public GenericNode21 +{ +public: + ProcessingNode(FIFOBase &src1, + FIFOBase &src2, + FIFOBase &dst): + GenericNode21(src1,src2,dst){}; + + int run(){ + printf("ProcessingNode\n"); + float32_t *a=this->getReadBuffer1(); + float32_t *b=this->getReadBuffer2(); + float32_t *c=this->getWriteBuffer(); + + for(int i=0;i fifo0(buf1); + FIFO fifo1(buf2); + FIFO fifo2(buf3); + FIFO fifo3(buf4,5); + + CG_BEFORE_NODE_INIT; + /* + Create node objects + */ + Duplicate2 dup0(fifo1,fifo2,fifo3); + ProcessingNode filter(fifo0,fifo3,fifo1); + Sink sink(fifo2); + Source source(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 < 4; id++) + { + CG_BEFORE_NODE_EXECUTION; + switch(schedule[id]) + { + case 0: + { + cgStaticError = dup0.run(); + } + break; + + case 1: + { + cgStaticError = filter.run(); + } + break; + + case 2: + { + cgStaticError = sink.run(); + } + break; + + case 3: + { + cgStaticError = source.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/example9/generated/scheduler.h b/ComputeGraph/examples/example9/generated/scheduler.h new file mode 100644 index 00000000..a907ece2 --- /dev/null +++ b/ComputeGraph/examples/example9/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 _SCHED_H_ +#define _SCHED_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + + +extern uint32_t scheduler(int *error,int someVariable); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/ComputeGraph/examples/example9/graph.py b/ComputeGraph/examples/example9/graph.py new file mode 100644 index 00000000..7aa824b0 --- /dev/null +++ b/ComputeGraph/examples/example9/graph.py @@ -0,0 +1,73 @@ +from cmsisdsp.cg.static.scheduler import * + +### Define new types of Nodes + +class Node(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) + +class Sink(GenericSink): + def __init__(self,name,theType,inLength): + GenericSink.__init__(self,name) + self.addInput("i",theType,inLength) + + @property + def typeName(self): + return "Sink" + +class Source(GenericSource): + def __init__(self,name,theType,inLength): + GenericSource.__init__(self,name) + self.addOutput("o",theType,inLength) + + @property + def typeName(self): + return "Source" + +class ProcessingNode(Node): + @property + def typeName(self): + return "ProcessingNode" + + + +### Define nodes +floatType=CType(F32) +src=Source("source",floatType,5) +b=ProcessingNode("filter",floatType,5,5) +sink=Sink("sink",floatType,5) + +g = Graph() + +g.connect(src.o,b.ia) +g.connect(b.o,sink.i) +# With less than 5, the tool cannot find a possible schedule +# and is generating a DeadLock error +g.connectWithDelay(b.o,b.ib,5) + + +print("Generate graphviz and code") + +conf=Configuration() +conf.debugLimit=2 +conf.cOptionalArgs="int someVariable" +#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.codeArray=True +sched.ccode("generated",conf) + +with open("test.dot","w") as f: + sched.graphviz(f) + diff --git a/ComputeGraph/examples/example9/main.cpp b/ComputeGraph/examples/example9/main.cpp new file mode 100644 index 00000000..b0cb113f --- /dev/null +++ b/ComputeGraph/examples/example9/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,1); + return 0; +} \ No newline at end of file diff --git a/ComputeGraph/examples/example9/test.dot b/ComputeGraph/examples/example9/test.dot new file mode 100644 index 00000000..c9f0cbcf --- /dev/null +++ b/ComputeGraph/examples/example9/test.dot @@ -0,0 +1,80 @@ + + + + +digraph structs { + node [shape=plaintext] + rankdir=LR + edge [arrowsize=0.5] + fontname="times" + + +dup0 [shape=point,label=dup0] + + +filter [label=< + + + + + + + + + + + + +
iafilter
(ProcessingNode)
o
ib
>]; + +sink [label=< + + + + +
sink
(Sink)
>]; + +source [label=< + + + + +
source
(Source)
>]; + + + +source:i -> filter:ia [label="f32(5)" +,headlabel=<
5 +
> +,taillabel=<
5 +
>] + +filter:o -> +dup0 [label="f32(5)" + +,taillabel=<
5 +
>] + + +dup0 -> sink:i [label="f32(5)" +,headlabel=<
5 +
> +] +dup0Delay [label=< + + + + +
5
>]; + + + +dup0 -> dup0Delay:i [label=""] + +dup0Delay:i -> filter:ib [label="f32(5)" +,headlabel=<
5 +
>] + + + +} diff --git a/ComputeGraph/examples/example9/test.pdf b/ComputeGraph/examples/example9/test.pdf new file mode 100644 index 0000000000000000000000000000000000000000..08e298dec3ce57e71b34b4e8ced4c1b2b7059dea GIT binary patch literal 22537 zcmZs?1CS^|(=9l*ZQHhO+qQMbykpz8ZQHhO+cSH=A1~f+Y)3~SE4xl*N8~AVHmQP$ z7%d|mD->z-b1GjwDtGqY0}H+13MD_%@2c)FY!-K4fx9waQE^FK6uInhIM$n zP#!7|zWtqV=Fhw5-W*z6{e~D$VuFR^@TX4NihgM0X0m?&-<8(E;Wr;GOWfPiqQlnk z$v4xFt{;Xf9ytP1Gp+i^^dJ>2{lTwAj`}G4eUvH6DY-W9jEu|9XE;(41R!mo8Y>2# z2lY4x(2I= z8W~C5Caxd>T~;;(D)2%S#e}-h0*{gIwg#d$R)$z84}i@Om8Dy<9Co{*A+1cbP-f znquzP7=P*cxu9C7V3jGHtm zthDHGVyS)&h%sKHqo(`#0J|ABtiQGwL1R*yn`a4g&;=9@$qJhYl#szgz`l`!Tmcog zF>H2HMG?EKlBpJT(~U?XHD!_v@t%I=Mk0IGV2vv!Lijr1aJ6|rr(VEhMp@}&?<$%< zJYT>&mnzRtrgkR(3;z$7&@8$>xc1wpQD#GHL)}lvUexY{wHD}U}5DXVB+K? z(ETrM;eVL_)SU_b!(abRR^m?ft`7g#IR3l;|C0W%AC;Y-|J(K-`9^PH~DN&mic?l>?@}~gOEf}2x4fFt9mWw+cWL%8%ln=E=0WQ zWlgWzLp6D?h{X=z-yv>x7@Mw-3%gmRziMm(g*VaP?6z&k8K*k|csZhF$Lq4GwVJ<< zy*$Bl8Q=u=BV=QBG-|5CAFvXk?7BYJ(Us6rz(unHo<6@PuGR>v;6AmwVCS;xwe5}q z@7l|P61@PHmIh}J0pF~zKClbDf@ALdL~D_81C;^{T7b`|H&QOw#BUxCK1Cux9+o(i zZ#?fmfN+)|Se#+;(#0xWAjcq%95G98RNk0QWJB^9GpxEaIksBfHISx+biQDEuF2;3 z2af~vudl>30&TVU#O7S_aw@8Sz-0xML3pl?=gpDW@6)RUa*4uo{R4=iR?y`V^&Pwv zS^Z2MRf$MbyqXc)d6!tPv+VlfZ<5@=9gdDc_MlZkC@Z-1IW_kC>>>ANbV_%+RUY?2w|K^Qe zjhQ%d#dD;{;dni!y^#%7R3bsn@{wYz$ zGpxq0e%3xC`wdkMyZrS_-uCQvYMB}<+K5pYWmqKdfB(-K9cd{L^yk4n};3MNhP_qD$Z717N=T_j>sbpC> zsVA{te4TRF{q5Mdc(XW4&2JsJCD}dQh404yVJK%cPvjk^SGAM88R8z7vs+`k!h3=7 zYE{3HIgehGV9C-deX>g6r}CmLdT?^9T(^F0Kgc>#d`BHr&hKuZN|8mC>%A0>-NkhV+$4bq_vN#7uX-=7b$lE%>&R?c}>-&GtbE+*S@yws5^pX9yvMePweLh zZ#T$koY(*)zF~&L5RcAcj5QytN!v_@DyyU7L>u&}4_I@EN;FbsMB)x4RS7Egxt;+L zaGKqlI~dPzls#zA>38O()jbR*xH+FG(xwZh2stCU8n>j#oswee@>q;?Hd8}7M$dd8S+HllZxg|YiqNmRnftE3!m+%4J>fkS(3v3GuYG_Z9-7b zTd(io%amtuTR0zvv+eE{*W--xS1M}!PScSCtwf6zN3T*}m)~UDWZ2{Tr7zdrjfTlu z$HVv6txl3f&vgr9v*SHtxWazOrr12PJCNaSiO7kuliBiJLzEEH$Ma{CcFo#?0KPE_ zgMSNxyy5Zk$6$WkJQO8aN`h*j_*S?%qp+_dgzD-Q?f|L=ErfU6%HyJCOWLq5*SHlY zDUX{!4TZ#QoNcHID9824&n$RxL1IgTV(1_j&i34TDi`V35;20rHVQt*tIJ*5))|s4F83DF%c%kDXRI z4}GPz)A?@LTZ01`)f%idwrEtWgRG*!hV1yVntLlaiztLbt|7O^%@JcG?l|a0pjM0)&3v|MWR7FITXzIJEvdcF&74ca0-@c?^*Qq+v(^^!NrLm&&kXMhn z4;ubibm}_kFFIlh0S5?iA+Y)azkAk->GiMWzA$Y}r>R{~oU1hJ5j1TA{`pmun^ze_ zkEz6%s&t16pemLF@8OlnT;Fc$t0_dVEUlBZ7C7Q10~R=vcS=%gCC0bDGRA$^K<{Zw zbVFpH8*z-_Ym&_xK&-|pbr7)@KH@ipwM7)yL#ioQ%dbgUcIjgMxOMIoCTe4HK-5BF z3$X_IlK|02+Hl^)!TEkade8~EhnOydD2^2Vr(ZVd0EJ|@@IdiIasGg3KqsX|*}p`( zP)Be2zy@G>_$S&B71#$=7%Vwqpld?TA|=~&U6p0huC`W~Pi=9O$-8Q9luXL`a4`)y zzL!tjdg$=kmGrM6aW{a60vGI}=PO3GEaDK4lX16>Vr|7O@Owc;Ph(H5Z=P?Hp5nN1 z`fw;=a;5soh5S;Y4)0A+RJi9%X~XFE2#&W z2GJIq0YzapXtwyPts07ZC-P$6w#XH zyXi>#Ha+VyyW9-ZY;{|hwr)n^7P7IU$!qEiyX+22(>~BNj7Ti*J6LTn``m};HJ=;@ z_aD!$t9wwph%}iKHo~Dx|J$jfA4fntvMm^V-KkU;6ik_^(Wy|JG5ED|d{DXBx?r&* z5L*P;nXqCYjxhzgu{ruN1o|{1iqr<>Cu!M2O)D%P<;60HcuGwZO(HhXx3ZD_n50H@ zLB01!e@8_XcnS3p`jrV z@#%sOg3;l;)~d?L_nshp6NTP*NS`LhkVC{C-X)s>j|#L05HnTbaR{q9dngp z$zTf6A`7lXe{7L{GP=>!ut^d=lA0mv=-Ws?WK?KbXdwDZ{gtmwv3-TjVSjoA3sUDO zsp@fy`A%29!d(Ofy5p2bY1HilrbVX61=FYZuYlse^osFMFyx|kkdawrYVg)Bm2j7K zfM`XE9Y$QZU0h=jLJH!F{1A~pe1%};M1&^szb4)+$rCKza%dQ;c}IH~5CBm`92lZ^ zW0Ru9WMqxQL>)kOz0|0$qeiA!1uxctj(#j?8Hm#CtDojmqLxL?VMZEI8l*Bs{)#A- z4E08Hh$JMSG-e#8R-z54{ydE@yf3uYL$vW{6OdWh1CnNmt&Z?eEHnP#K0Yp zxbwxhF^uGH9l)4bkRi$xS#pI*X%od{O5{orGbng+(IpZUwS^Jay_oU`Tj`!>R z`8g%S2Spqn^&C%-(K(>Y_IEt076qc~Wa!yU2z*c+M#{)xuX1F&_R7gb5L6t}I2u_) z2O{&kVv{jYKoD>478VY5-~m)~gT4ff@PSu87~%eGYK?5z6yW<9S@K|g)a+&u$;eHZ zw6HRm#*7N+zzDVEFX>EC^YTG9>=YDunvtU6k^LR#ELJ16AFLB$n=```gw4IXsD(pi zDTsv`G~1kHK(j?4Wpr4T@Eb`|f^`5MP7G8aLIS!l%ISTArh)0CLaInmTzoc*<$;Rc zx1{Rqeoylo4Hb3PFDlYY%6!k@>Q?tER(fM4tXL$i)dPP;hr4rOdpK9C7BziD5+tlv zxRramNYmIX1`KT)IjrtS%8|EW<#6CKn_~8m>}J-yV<|GP>;3?U2K&Ec`d2_zkfS$8O znQS6Q3X1eZmq!87iRVc>^*`HsFnoW>K)ss;K>-5Gn#Rj9*!w$BwY633fNdr?s?vaqCzn3 zW(HrmymY&7^kS9r)iD%?MTq^XU$nlPLoTC%e**dhJoBtNFQ_+cKWsy|qc@S{@8y>x z%zUbQ!Q?8VwVQI>C8$|vBGg&o(nD!B z2V)^(i~gwcCg)K63JzC>hmSBvuSBZC;x(nazZ@L;7ilw&h%4k+r#?T&s(JDDOasR5 z`VEOYU+BbYQ6q(uW%pMsVj^`w0pqJS3SbVLA}FP(JLfx9X7EIs`f1{h1W?4BI7*-` zmZZuY6+udubeANO%_Na;=0NjY%7+OwSOoWJK=melI9J3SC_Q|6a=PLm;YdlAQ}sFW zJp<*0GP&cq6%?@TCQ@8HCMGTBVgo&}jvM5k12y|-Dbin@>(4EK6V4R*WA0P+E8#7Z zEjLrxMXemvf%4=-C1gnH(k@&igr{jJMn;FYiw@ybh*mT;q*z@g{YB|6kdPY&V;OpS zb=`pdh(725IlJY>F!3xI2%@T445XV<$7F8aqp$*&)4sT@)UB}+s`&P`b_G6FR;PX( z(1&|ccV1D@dwX>6RsGgS(Ov|-u#J$ZZ1q)AvM(QyM%Ce`q-v|T;PA|knB+xfeC6>v zFX=>XBwh(Xflw+Oj9$SolMIcAh9GpGosz4_W3YU=^QRO-Ce_$1AjH zvW0qL`uOAmt?lOEW`Pp#*s;UO+ChbrDyn>BsdIR{M5kCwB?9QY`b-v~cEX{rwxbpX z{|?$ijuc$zhw)e1_kcZ^7%uA!rCvi7kv%hsN|4yIjy#rry|)ob{bZc{u#G5ww4jPU z*0^z=WXGK4pe}%F$;Zc)#>R#wWZi}lM!iv?#Vg3Q5G)F&Ab*v?AdMl5fdaPX2vKev zlC%ilD69n|?u+JdPZptT*SVvNf}J%R+iD_*+0=7k5y_XttXZ76#bQYBMwHPyP3lcl z1{cT<7Vht-PmF`!8-_;tu6-acOSs2=q42k3qEfyRv#XY>QmJ+Syr`KwIE1=3(oukU z$b~gvFGe7$>oo~xczF&ft_UO52USI)0PcS*fJCd!t_?KZwqYKkZ5ul1x2QLAjrk z`Wq}Ktp1?PA{#481qeSkO7bqgv$5WEK5kN<;;b|AIP5NoZ>|;x(Y2n7i5D3S4if$$ zxStzV8eI$A{J`5BYPI#dn}|_`zpCMLk-ljuJsI(qOeU&ay1BWvu)j-Y|71+<7mbVUG!4SRtB3DGQt;|v{ zuVK}-{^@wZ4$&x>i+{u&<(^Sq;D7VuuwlD8)P;(jHvPltEhl(A$JN}! zwd%d$1V`fssAI#Gke%wm;?4;|qR>kT=ED zntolTB_aEW)F~^A#E*>X01Bu7)yNyqJ40Zi6rl-YPaMQBFFXQF00Au0<)uB_>MP-+ zEh8_d|7OUN3Ljv4=xHb@tQh%|wx+ftuWNIzNmj*%(<%=37MEy6ooQz-yAT)t6!Wk&T-p;JtKC8yYck#3FeAwli zacr;l^2-pv*M zFcHd7$}MYNSzV!Pe19NI5;gcH>V{N()XP?)@VgraICu zZV_K}7$Q8}{>FIlS$+dnj6IMGb-*t z{2}}XUG8n+N0* zeJEN!Z@yGRLyVbISKN_}>g;w`w0-9_C=rMoL1}1YoHpCL%fNJpuG<8V;u~Z>#+bay zriqJ)sH{Gt>Vje+7M7J3!&9ILo88Qqviabui^&wRlO?P)w>P5tgz}k0--C)uMj4yO zW=9iMUAZ)2voB0?V?W54d6g-KGue?x1zIRKz>ttto;av_G@A-1GMjhewT$$*fHR8)%s4)iKH@@{$)~jEQZ{Py za5r$DrE=GID9l0|yEZx{>Lu2xnp0@4k{UOCsGMB#|X}ArLd5Li z!zHiZ^Te&Sc2d%$s+o4-@XF52*4b!Zmdw(J^EUuyuCYqUgpl#_Z)lwG)L^%YXSv^4 z0)gQ4K~$++J-@gx*ylGkr)r)(jHZYpdWsH#i2xxQjGu>~7oQOsa{v(+(V~3du@nSq z2#CNRTYGRXym~ESUJkSwlMpIppm8YhpM_{s?h~54N}-p8XE7vIzm9`1R3L-x{IeP80RP*^v8aR>Ts`vhH>%zrm>eWOkZax z44_!1ijDw)Pn-yA(94XE8#livY8=Lx^K#U*NFh2Dg{|O%TwS%C)|-!nHQi&S%Is3{ zpse7Nfdp#N62zp&8wj#MupndAj4_{1BQgMj1=PbU3rPnO$-zR`bQp_Er|jP5F>+_&&>TJe@wK<-0!m=V*5-1B%_}+yHS>ik6Jac*Q)z#H|+cW5uQ$Ge* zya@vSuFJjelJd*zm6~KfR=v3Q_1#b!yUUT9@&AmsOv;5s=@{!sW($4h!E`{8cP(Fb zIEhMU%fM$A#8&7bh@AShavu@xZhbqf_3Lu7WJ4l7w;;*J?tDMc)N33uL~(5?34mRW ze%m)0-9m+pyy@CF>iC{WxoBsvfw13IP)o6qSzc1D!H5PwA-T!$ypGXS6Wm?8o7?(y zlux8yPt}~0KK~IF5QxfCU%zWa*FwY%mXA;qsgw&3PFNX`Gh^gV2R*O4Q#Zr^JBANG zN3-3OuOL1ho)jf;)E(3mEq70J#!BS?)Zp({*sQV6S+xv?`IOgu(xs%#f`?P=32X4( z6hLfd*dkDw{DEc>5~r|W9+3ev+k~~<6dwVK>PnGpG*=~~FORX*s`0k~ae7kd*Wmyg}5Z;(a3fzlwGn|UaZV4?-u zID%V87@TKh6{Mhq9dR)bY}R@X+@h`H`dHeqp0tVJ&@m~yR@C;oc!CPcXJ&MGGf3h-N19$EyKGg(j~TgLx%YbV?-=NL+ai+=lO#+ro=E zwt45)g$JLpM8V;98wPGlbM#}~xyw+uaZxu@ghAeZ6 zg%;T%S}bE)Wpl8Mn%kaor3n}1vT{JnbXxf4j%Q^H56317H0-RbtE#u{32Sl;$8S)1 z?(Dy9)OO?HvGwZmOAT>m;0raH2t9bZsp39MYtm%E$LS%Q4nV*$6?b0i3`E^u7#aqK z`9Xt9AWJ=1FiC+x2~ly1l4{E%f@<=+B${~)5#KKy zQ1c$G%3>(PzzM>2NiYyz-eG8{TQahRQL?k^CZenUgg9glk|#)&zsJF!Z}#_xJmUJKeM@BqsKo~6&0t>)@>rr%IYQ>r@&;E|EF%ba z=mAa}z6l&71dXQ_^6&}(TxuBSjGh>WO#jXlbbQKYKhR1YB=rp3cA|7&%z#`}GE!Q& zRP#i4nS;m#nQM*LrKy!%@rtFlP1~n-pt-iJTKzejl@uynpf-h$-JD{bGVMgVCSPqI zq8{^(WglYh)ekfuZnK8B$e+r=4BR%C`4N0gc!4$Pwkc?|;FdCI zyHHivZC%RHoJY2-H?6RNQTA=wW@kMBmIlHdtwZQ{a1YA&n)|Zyt-Z^^7jY4H6xJts zF$;0+TkM-}o19-7)h_Fx)XT+Gi=>*BU6qEzsWd^E6^~U)Riu^|!LVanl-mvx8rmla zu(@DycsUYawlIOsMkTE@q=h4Ui`_q|0Rxu~oP(wqV6^f%AQzBSk^>``>0}uD6)BsEUg~YtV!=Zx2m8s#30&&k|OPU`~#^mKdZ9Ygc z|7#?0+Z*K-K`CEfm9&7OoM9w2t<)fxT_0W(CCy~|gw%;1jI1B$+ELD{h0sKXJptXz zzh>WPDVx)EHy_}en0NeG(dXW)_SvqajATbMaWG#Sqy(g-Z(f3BNrFL(hxQ9qtKVz8 zteXtD8H?}nRoIK;`Lyi3d8Z6D-)uLsz&%#QUeoQi9ldhhJN%X%TX*-d7_GYx_Rcnu z`a}@R1KuIS`Li6Xk{@`^FyTLlVyq-Fh1o|&mYk-sF;s-4Y_P$Clg5Syel`tzKru!Z60>%)wt|69jh@9N2LJ~K1U0wljQo482iD!G z(h}jd_^otg1E`Wpx(^?{0k*wj~O)rkx!5$QD=zB&Zo$}_7Hz`V_wQ>NIPJqn500F_ip`{9u1X6Jp3t@H~ijw|U_#>9^ zq7fp1``MGhy^cy2PR11@XM{NJSzx4yaL|>~cH8#3Q7UFuhLf(I>AD}PIvoQyQ5gMH zk{VBM9?xH>;oM!Lw_3D8(bX*$XF*+#rUkPWRW;2gXES-yljL?5D4#LhFw45=&CkT# zCK6+2t?K*95;VF9kAeLGI zN3GfB9qmbQC3w%^%&ghq*reZ$N9)_#0V#DEnPhS1co`ct4^=DGtB5rLD~@6xRKr&G z8{5~Pg=g#7gL0>@!#nO6{ovj3ZrvY6jq_yT9;al&Jw?z-hdyEu;Sv8)G#J9%q^i4Q zb75RUBq+$12&c|%F_plHsI7v|3EEEZEvQ$a61~}(l|qyh<#rTI;}HwGGKL*AgV05b)T{!+j;qi*e~F#RmLk;6-u;xqu%jQnYm9ib^+_+?Gy04G-+3K zwiKY;o)l06{ZF=N_8d2NT*SxoZ9!P|U3LHl9ewrZ{mmPcptW2>VE zZ;2fEabV_Zb{V5VErVNMkpe`>?sgOr1oa+9)75aldS6F`EhB5|ME*LRnHTo{C_XDV zf0Ope!qc}oYub{xn_hJ63os#BLg-9BFKT?gP0MF7Os!h?l%C`_1ivUZ{OaZR)<^J-Z!p;}H_9E z`W^5A^xY?|?9eMRrwRi{r;uS`VR317>Gt2!fjx*%u6$ScnsXLJ5?Q@j_m=VTg#iT`gG-wC8xs2cph0u5=3hBjhXQLJRR|o1&>W@o_-<1p?#|=RVj|Q8r zc#ZN-Rr|U_&#_WMb6Daf9?i#1{0nGL3q}BDJ_;pc`$f5z$o}fe4Z0PDM%SD zql6oGgZq~Fb;;7-!#p+Y%669o`MGdQVchw&UpHXZ2~!PM_)2+O*m0?QYv! z+FV%voDO!1$ib zc%PzWEXJ~sz5{_opv(v81me>%v^~=~IuSpg2xoq6hOi~s*NaJPivI&eu()%W-8(C4 zvMYti{y@HdIf33JlW)0Exl{Vw@%aN3HtH&M$h^ay|0${gTIt*^>P5OcpWeRC%{A;Z z=V#X{cW4*!i5G2QiQ*~p8@GwQ1^*A=PyU_oYG}pc4!@Lny|U!S6!kPW5O>l3TzZ^A z!W}e$pwu2vu=3$p=ozABGRBZk|2~1BAmcy33Ubja?C0&*aUgOha;I`9D}%SY%Vse; zgug~?23YjM77@l8gFS(fQNs2n&I2B{A@6=9W;*YNhs{$&I3b^-EGoeS;q{6BgkwK< zIb>vaJ7olT#`J90P(g#8fHIZVoHTrBF0eGPK&R_an8xokn6H>xyQ)R)05>YDEr{CT(w)w~V z8#oHy<}>x;h^AK(8){n5Z$81^PT%W(ip%@F-*0##(cXA)W_aFZ$<3#4x8)^s_q~bK z75N+Jtf1Uus}>N=JU_TBlUuzBHT^mpUi{DWo}&mV_qqE#XLa!${wj_=jy><{o937BWpcG#Eg!Sy zQQCE@Vb9B`KC9uxiWW>w_y%X@KwG>cE5=NbLc!Wat0k+*;R-ca@j6Z+`I>2fZN!Qr z4OdO92`vH{{IJSef zOD{`eZ51$%z+L!(Lsg&*>=a zT`=^8&V{eYgb#rj9Kg5tm02k&MUOM1!j;!n?T+Nss5 z`2y`l1gq9taZQrtDawF9WZ<%mi{e2IF1zxeZxYrZ&^c!$E@HjC8vIwcNlB!%!Y26k}*V$WZ zZERpyy?o%v)kKI{mz$Sem$Mo;HxOk-sS2oxCcD#2Yu72XEuZnCk-SN#YJsVNq(`^l@b*7)7`F*m!${-EjO^!9$<6gCKzyS1@G9R~*N3$Iv=SS5huD3?70r^3{9 z+M7 z&XF3u@nc+D<1-pR%0-25DfT8BG{){>lsBG49QS2#UP82b${i z6~cv70mgD;FlEZ-{idc*3b4~sPafKjkE}IfP+*hcwN6x>NaS>pT~0~Ml1Ww}nZ_fZ zt8<_L4qHZ*#0w|gMs}_ryE1CikJh7%dL5J=kl58P(Ao4GsAA?&44TU5P#C8cPevR8 zXQZ@Gta?|vR|wummwS;$2bc+b`FKiAtAHw?k+mjYmHKmIl7q?!+7zghg$iU*sb}h* zaLg1$wF5V&d(Bflnz`x8b-sCc&wX+I$lLrd_p6Yei3LIGbgfE3$zQ)_Ha=lp z`gZbJm+1cuh@kXhX5rF<;-TR8|Yuy>EguGPYSp z`KYCWI&8z^>C-)mb}6~vRHYtxxifF|D!^4$K0Z%2{H%&JdpDy*^C5Exp$&+TD3Zha zaN*{EZA33s-r89y0u>8BL6dug8t%^PR7=Q*eu}fm$etffmLq#8k2t_b28};zY}9SG zCasW8qqt32OQlDugl+drP4A-?{+&W43U#~2Qu?S)My&B0@k#vFMtSB9VZ#3 zX?are#?m>*F*(>CQjGV>b-D@^j+366gOb#WDU|I7=A-%i@E$^+6JpomUbMWrVgL~v zn|UNP>y2L{r%Xta8`i{S`xn|)%#=;#bIIjHdaV&?$$9(ogfl6(0B>JT3(m5^(l_(< z?rIqU?u=C4(eqUORULuo%hAow8;^GMl`2wc6sVvgT(+w2VZS(d%V|8FV2UT;$o z<3rEhtGAwDwXQWVG@N)E;`uKGdLF;qn+ZgjjZ+HWE;&XQW7bI=xr+)dyy775@@_8~ z8WB>RA_0kt^5V%?2{@U*oJ+2@0<~ADzQA7%cN`j@DV!-?osut=VjXrotihrhchVR|7SVmb57c0R#I(szw%>t(LI_xDm3S8V)T?ySCi0i(%_s#)1wX`;mSyk=#& z{bw2bRQKtex_0k9viCC@bP4bIymL3}mNghmf6#swcg)4+V?NeT^0p{$pi~7Me66*R z;pfPCZTD1Np$?7mjH-(3$fWOPY5o>b3T=G01U5dG5lM17prNO%g&Q}F+zJ}hanXai zh`eA0tTR&Ih!NxWD-qsvyuqR|1MHT%;9Gy=AGc- zU;It!qA0OG&l10yr~Y{&M67or#_+Bpo)R9)55^vXRx$_FV29v?r90?DI#@*ktkI^<$~;lfPG)JF;h3;O?n``b`2yH9|5+@84~#QuOvL zt!#DBasd3KI_b-MqTGc+|G4z$a@E|x1A^u~bbhuid*Qs|3LNl{@Vu9!;PHFkXBGfC zzfYAU{DS&fetV|$5_lhqyM4_+ydTTCKGvhm*|D_%eUEAwb;S)Y7gHDZ-o`#YKAi7I zcly4bFa7%?VmEuMm_ymr{I8iA8~EKxUh~GFno$5**@t)nhzqWO!Of#|A0uMK0GFY8sDGUL@jIjr2oQq?97R6deH zYI&v4;FO?hmI!kio||YKa1gEdsXb}3MWvQEphIrO3M|GBRMBb+T(J9Lz@D^j<-=5F zxT`4Dhf{iV?U>cB%paF4b{hp6&QACUlr{MYzNB6j+Hj^0Hm(%Yu+$e)nb*fOhQ%yoKey40I~arX`?Fe;c2=z zb?aM}n9Xs9cTQ+A8B)aw^(vOndsT>9s}9N$vC7JR1o208qTU>xYYdWGZG zE?)5#4`F=tM)=S5)VaJ#Q8A&}aCzjj&*E89(JKHE?RH(e3V)3}M_;?TdnA}jWlI63 zg?#Ev6jH^yh>y5f4u1(mL zP%M|SO3HyvL4|tS{TA@LBW;79Xib`+RmkDg|MB}VbRV_9D}1|jXXo*fnytM1g7`i2 zE2|Y4@S9vDV13*8edhgLjMW2StWSIJD=!v?GXbPC5}^VKglyg<%F)U&FrBIjfoNnP zIQ>0+{rx=y_r_4fhT@=~lY$ot-VNl2y$LFB88DBiRIs?17@(X1O72y6f)Je(wtxwq z$JB||6GJY81iWgwM>+Rbv6*PN(#S@a694bilB(RDk|KcL8}lET(|H2PFwdzB|bdhFp7p7^olE zjQarl;Dtkf$p=O7!p|cEXL;LaXnKgFdd~)ILz!u|)A(M!(R=fiAjwpzn8@7N= z+6mn0N7I=e57sc0Geke)3;&TqI#QMMcO;n;T8iw6=z*-fN8^hA32AP_^8@Awo|V7P zMX?`T+n9GfGh=}v_5<4Bi-bG0(vig(0<1h1aBRYva4Q(y|NfkZ+=}VE2l)YfZ7Z;% z(yl|Z3FZfYQ_rKr*vI$)>^h{W3FNv1tBLANd3{`^vYOcjW7di7iIL089pKmra{a~r z;I8pMZ9SSau{Jc_FCVxyq@kJ<#hOEEg0dA`EySK{HBi?V6Q9?=D;0rdjeV@8uv5#T z+yb}87Iikb!d391eCh^vv&1>x_kcFD96*{X0Mv(ZF(=*$#~<+cg7Xcfkc@*#;CJG@ zalK*xf(b5|gWz2cm;eEgQY(BmYf5pjIv|;mi1FVgO1qr|Z*2uR2+*gYp}3!w|;N53lY21tZ2M1?K=JcOtCG zu|7$0+}_&W0^W*k%e@o+V*aW{fkw4PnT4<}>0xhLw|ZxPPyR$`dY6=lNg}N^#?uhL1v(3S1mY3t-RIHnx}|1^k+Z!;c!P^~&!exf z)Ym9bFMMD0;F^!Kl-XRt&X>uf?pX7=<)v&f!Q1S4kzbB4^POK;;D8*(A4sJLCDs8e z)(&lO=jfgTCc*PpPt^Y3?xp?*6}bu^?9Ty$fH0C9ZRF`;@Ey&F^qwZ%tW+0N4y!xu z4#=z;Pn7-WpFQq-_4}YJ50H}=jvqQckXh$92%g?P^83%X*tc2`|1<}HX+$Jq%Y=aR zmbmw#ARVAtu?EZ>KqirzP+ZT1EAr5B?X2KPhvY3RySUBaZ|GO1SF%_5oI6RrY4&^S zx9Bh2FUOzKug$O02>$}rddYh2`-X+WDq!6Qsbk`a?g{vRUp1xjl{KihsJbXoR1ovj z`2Tv~e*uk4Q^y`!qp1CeIU(H*jKfm2>>t+|Ni{@Wp57e$maO^DIw1X^;_cV$)lhk7 zW{UsJ*p5(-Nw-BoQ((2jp5g6v&c^K~{QDiD3znW3|G??T`i-+8-I_4UoYHq@^Nkw? zA3*zq)QUv=4#Xq4GpMuhj>t;oYJ?oMb6h@tD3=J$1*y&x+XDmZ`>&3mTOM7H+&yIn z(8_mb1h!$fd19v{=qp(-_ID0+yx*w#ov;9F%sv~R3>`7%7mUJ|^I9y~Jpoo9JP{^; z?C!9~-+Se)M0dpbe!Jr^yjc*y0}S*bdnc?V+0n4Vp%CtI%!Vv4L65-7QH-*o*}3Vo zK~(_m#a_xM(msE12^b44YK6qyL#(}Wc;Ie+?q{IlwTMvCGl;&u?Xn1-Ks#EP!)Jk4bYf3!n%G(q%PLT-bN_lRv^<0;Ezt=oaZudC4KQ%N_Z;Y7_K_tzW|n2>;muh zbEbXHKd?KuVzLZ9s%M$rNMf{rrg(qR^mDcRP276|^+rK#0~lw)cy5o-0gipj97w0` zp!O6a{mFYmYAAT3v=mk3W|eE{eSfIF~phY*hJYi^Cj+F1&s9v z#RKrav2Fo6x&gkS&J;no=kfR7(J_8)h952d{*unk*zD&F&0lW_;36sIR%Plyh1vYmvfy{37r=L&(CQpo`QrqbU<|pSlUf*C}*oJkl zGZG2)QUN|MeLauW=2aM2IGdk^KR$*Vw{^qE$7FYU6zFncJz+T(&eVVT6C)_ zWb=H{Xa!nf$2j=`K5LBFjS!AaY&XOb#i4@7TlwwPoaz>IxPmc}O*NXimpTi}T&u`1 zZt7&tZKk>wws65GM^TV!pv`rwM@h7t0o`I9fiqy&aWACgVmITF7Pk6At$a69mSM&+m?2~*dzJ`U%915n%TCD}WtRveYb3?kW&O_R-Fy9A*YES1Yvz94&pGE_ z&biO)kGbZ0#8k?b#iOvcw&@SYD{_}w20@z*4cT#Ak=;w2sZ?T(RLSLjh2$%|o*tfG z2<8Np$lj3NLg|Js)e?BYil$%zLl3p{*}9jEl5gb`R8#P*T%HYIT0;#wCxl`|mS`w& zCc4jjdTy;K2%oK+%Rdk`!TP<(h1Kc8*-|_*n~kHsZm=Un6)oRd7t6*}Sz`Uoq1@L| z=L1!*nesPl)T=-WD8HG14nzz~C`s&i*r1Zk828-Nu1yR|@k**RYK$+iGD9!K?9(?N zhF?@XwC+vQkA?9b(hmrPCaecie)+I^i}yj%vn&8_Wm9a+8T$%%Kjhl3+`CZ9E7WAh z3PaS64fzdF`sj0XgFhx7qzss9%!Q@6vwb*ay2PQ5l^u8t_N%nDu5YW(NT$!B~bm5LFF`OsW%upiiQEp)o-{8hRbuKD`f(g#7qjf+3}*kivF z=H@3p&?kPn$0?k6-V_FV>3?p9x2qoe(*B&?gValx>v7zX%X%1RURF8}a|W@|kqGx@ zq~9ebFsiqasnO@10>|WQccT$2=JSioNb_0p#N1@m+HsaB@LfZ!60D3F~^>Omxn7wT=0OdO1sx2L+Dn@dZNj z^z4o)DGo(hS91B(nOy1HXrkZvJ@Z<`<>LkV66_O2g4nDqwN-nWpIxSXdQjnZ+Gk5S zGo}2y9M5Mdtt~D)d2{{eGISE?&xD_HXJkt5D6111XWCK%6hgif)o=A7`W4r+-*W9f z-8e<*cK#w{P31-4z@SG{lcbWy^;$7iCWOuip8C+QEZTlB^9cr@vO3weV9a^j!p=1Vu` zb{kk0ii+RSmn9(p5uAb-q+Y&VFI+m=_9TaBX;8^P_oHuie*sgACxj~7%55;>P2?$+liD4l+lOUq>RUJ&$95>Uoik_{X* z$=X zn^4ES|EhAfX;O)|avm!b=9H!1`9+O^x)oWZ>a$)M7CfOBdgYU8OUsEXC3&CgnT6&y zHu!GyX@lpP1;xqYCr)a-Qy(e$dXhZ(3i?S~_1zdM{c<2J`Z=ep&<#U%lp3nIhEY3mmI@G>tv+FXO0K0@w8hWD~ z1=W+21duNdRK|wHUt6`?#kcAuzA(s=XZH162o(rG_YV5Mb6^y4TAWl;9)kVc$p0Ad zRYLMW=Zik{#je@WyiaPF%f!z+lLaGB8q{BDSCJ<;Jd3+t_?RifK}}d~W3lRJC)LYD z@Mt|#E4A68H+(pe&S8dmFi8AC!2s;=ES%1Y<`&i5C5jaCn&dqpQ=o?!nSJOEufT}v z1Wdp&bVCj(roUfK@BuEYJtGKOR-y+d!9YQtz$Bi)k*`draD?X+K$8`6#J4M*s~71O z;3_<>jq*T>fDk?4T22?!^RM9}x}b-$(R!N@6_0*}lbGQWU{52n#+u;nkxuz&U8izC z$e(6OExz*_;=zpSAvjkl*CZs_E#Ts$ssiiOVVjQ1Yk-tY=?29bzGJU0N~wDE?PG)= zJ{)y_PhOTfR-Jb)f~hN*GtlcqMh>L;;7)*}V@yUk=^{72irsU#dY=H%b7pb<=P-!)e%cC0j8BjQ7D`HMDT5dLw6q%Qs=Go^F>Z^#4+3k<`&TJ)m2mh(`kZ_p(EXY+iphCvtoF0vN$$gVABI$`yWzDVc?ltV z^!aCpeCfZRLa-SUyu!Q<2zF=-x-X2w9Rkfkgcyf|JQ(%X^!nt2~y z+cR}1c-9J;8Ma^atWUn(Jo0_k4|)8_PReP9W&hLuR4P*UCWCzy?@iu=(|2etc7Srf z#3iqJDvvQtgYyVho!d$~OciUCwNzS*OLa@e4USsVJj-cr0ZL9=s&(Y0G+b0Ks6bl) zk%^NVU^D^hNj1{SkM)Z%O#vzFN~X=n|APP9|Iv>VKha zLe_j?*!r>N@{MOW;h{RVZ+My_{dV({D-B^YfE`|GYA&iJfBNFfJ$*rnCzi>(-mvUn zclAL%-EQSLn$%DjX~;CM!Zs?p?9NgJ$*?) zSaM*ra^VRqJ*3NEDl;4E>Ks3)s>vuQKP0QH5W*Zar8Onn>?X3pD*Z{3P|)>U^NZiq z!{U9qB60JJAFAiXWjN9!X1QK3-&dbC#=Mst=|oPY_RKfj5glvzny1#mzWO>^Qf#a~ zBK@kESuFo^2V>*Y+0)H#DGhe{FT78W(t?*L0_gBpUAbriGbvLjYB;!bw}Q_Bz9kbo zBPUAPGssMR<7Py5g7i6h&e{Iz!QG5ql zo$_U&NwIzHvgYP?`95IhgR}H-*kG&?pCUk%wv-(YkfxCBEg`RONZ4ZZ2jBs;_easbBb;IlJn|FXQd+tr8lswOpbo3rBw`_i#qPn zihX1F?oJuieO!#jf!v{R8m;{FYQ7iX0{HY)i?fEtx^K|nK{f~2@8#Q{V zQNu?UzuEqHOHfHZiqUT>Q8olQ8^VN8v1b1Gh-KR*RG~q*uNlyGFsK z^7y+)&?-}UE@P-My9KLt$W|7Rmi|*GWf>JmXxQ`Be&_z^HKjTCDaEuW1uW_Tt#4N+ zf&zR_%-4`5ylJKF$fVxifS8xAHm%z7s54ObpSr6+(3Fn5d7tWHf;SuMtB2hzzL0Xp z(=t>Z)C#Ph((z|@EFzK=c!KpmeS4ArzR4!rD9D^yBaRBA)uLIw#1jP*GK|V=@T8Y=I=AA(#YN>jT=Y>un z7(yoQjNUG96&*cN^hVRv(QWl_AFb^v9=&<8lUcGoCGUpm$(b(J!b=F>lEings25GE zwbJt!of`sczS)>E==7ts!B@%V3fyqcb5n4R(@Pt#PeB~K8ZQ}&5hU}+#e#|djui04K+BK+0a+=zzCnX0g$Cp zN35a5MftoHUh1{4f?z+cy|i$qsiG(v6y6)w=i7r@ct9D+d^ufa(J8H;W8TRw4Psvg z798}*dQ}MKGaU|MTI}lHpP;SC??{2h2RSYv3@79?SIn3Cxp63EDt8vK+Q=ZMPwhCWLdmL%{!=T$4UIV|( z>ug`bQx(m*g(>=ktxWzA6L#=&PrtY?&L(NEKziwE1?=tF`hrL1a7Jiy^ov{fB}5oZ zL_9y{V5)d@PSH;4wQocu=4Y9~tumY+>v)uvD`35^HKu>oW!gDPpcBg&E+()6(oSpA z^1+q))jkwSugXil^AzjL+g_}(F`#+ghdK?7_>QlPW1QUfQHTOTl(xuHjkdDZdq3h` zDHayH``uftKWu%1T>uS7yGT@qeeRT`jEo%~xIEemx;`Lum(Psj0*$G%m@RF{m1|v# zbxxRgJ-FJnvH=f(bM3rfzpd(vM>PoEY1et68_D5zT_nl~Iy$|#FWD#T z8!JX&Yi#Cs`?%JY$9L;a1@A4>dNKKUl|`kyqI{~;N?xM-y!+=b%q9&PWed88>P$Bh zl$hKaP^E$%c{-80S2@tvg*1%>3$^vgoYzly#RY@RwRM2iygH*aeWOv*Xko8CIWXk? zW`gDz!B>0Q#6Rd2Wj{bG@GR)6Kj0_J)Xk>MK=Plgb2kHB0N~E}LbRpoHTroSZ4+PvYk3%0p$C1JM;u8(7T17ayXtg zzy9?|7d_PJ(x|W5gczRjgmCNS4c}If|9%=J&CfQ;V4pg6mTy#T+tjw~0-zVB=iZ#o zI;;r{x^x$O1kl+?fAC)v8VS$!2MP@;1Ctj2%_o!jFUbo^f{*=gI7IUV9hZ9`rm&TL zn%MDHM0**=T08AWyQ-r8__-{IlaA0ScgDlLUP!w0U`X%DS1SPV3k%D2xDOW-x68ls zNTT`e$%?xqQ1Ref7x`g)J-IdG0*9M&UJtVBCP(P!dq>saG1>~QZWAq{jJkFl)5;ox zi8Z%D+RMC*mJ3e`c8j(ZAtB?oLSr}1mOolX63SZXV3G4S_O5@8 zL1_uE`MlYDj5cl*m&N0T=W^e%Rq%?YY7N>K_eDrlAWCT!%#_!Q%QP%;Ptw*N3TB?? zi$TWl&HEL3m8I1oM0YIIUa>EfS^M{zT<=ZWZc(CmWfY}(RsPMkhNWuLR$Dmpo(gv= z7k)C2^#ks!1fFKMq<)kLE>z%XjgX^>@hNq zBwe#|CNk%Y^|drjRD=!D&Pd!fq^G`%vlT|qK*#V;GlZ2h+Q}OvaBKl0nt+aN1b-Pg zS=kf0bFe?HRenM0E=fpAgD#0fBtb9;Tml4zi2sJwU3Ir|Lfa@~?46Jx$g!I;&V~fL zla_%VcS$QH^)JDolE;Fom8%94ZI2@I@QA}4O9t*p=NlmDU;d86-BSHB78<$_hd04w21NaPy=sQx(j#U*a9P$|F_y36z8jEvRMOk5ghe6xwk3JMC zbv)2N;}1H{(61s=!w@l|XpB8b>?RtcjKQIQ3&(yp(YEd=+#kq4Qsw=AiHnPaNbleM z->n3M$QmTRe}Deg|F>2OMq>F%NRYUMf3^Q_UpQR+c&5L%q!s;|9VSHzKI#6iyI9O zocK2)A@nb*BN1pwq_q-@03={zg~qyoprTN?s5D3blQ?&>&CbtlQ^G}NTuNu3Gu_NaBPfR7w-3se|99 0) diff --git a/cmsisdsp/cg/static/scheduler/description.py b/cmsisdsp/cg/static/scheduler/description.py index 9ae1edb8..21f6012d 100644 --- a/cmsisdsp/cg/static/scheduler/description.py +++ b/cmsisdsp/cg/static/scheduler/description.py @@ -169,6 +169,47 @@ class Graph(): self._totalMemory=0 self._allFIFOs = None self._allBuffers = None + # Topological sorting of nodes + # computed during topology matrix + # and used for some scheduling + # (prioritizing the scheduling of nodes + # close to the graph output) + self._topologicalSort=[] + + def computeTopologicalSortOfNodes(self): + remaining = self._sortedNodes.copy() + + while len(remaining)>0: + toremove = [] + for n in remaining: + if n.nbOutputsForTopologicalSort == 0: + toremove.append(n) + + if (len(toremove)==0): + # There are some loops so topological sort is + # not possible + self._topologicalSort = [] + return + + + self._topologicalSort.append(toremove) + for x in toremove: + remaining.remove(x) + + # Update output count for predecessors of the + # removed nodes + for x in toremove: + for i in x._inputs: + theFifo = x._inputs[i].fifo + # Fifo empty means connection to a + # constant node so should be ignoredf + # for topological sort + if len(theFifo)>0: + theInputNodeOutput = theFifo[0] + theInputNode = theInputNodeOutput.owner + theInputNode.nbOutputsForTopologicalSort = theInputNode.nbOutputsForTopologicalSort - 1 + #print(self._topologicalSort) + def connectDup(self,destination,outputIO,theId): if (destination[theId][1]!=0): @@ -525,6 +566,13 @@ class Graph(): #for x in self._sorted: # print(x.nodeID) + # Compute sorted node ID + nID = 0 + for node in self._sortedNodes: + node.sortedNodeID = nID + node.nbOutputsForTopologicalSort = node.nbOutputs + nID = nID + 1 + for edge in self._sortedEdges: na,nb = edge currentRow=[0] * len(self._sortedNodes) @@ -629,7 +677,110 @@ class Graph(): #print(v) return(v) + def computeTopologicalOrderSchedule(self,normV,allFIFOs,initB,bMax,initN,config): + b = np.array(initB) + n = np.array(initN) + + + schedule=[] + + zeroVec = np.zeros(len(self._sortedNodes)) + evolutionTime = 0 + #print(self._sortedNodes) + # While there are remaining node periods to schedule + while (n != zeroVec).any(): + #print("") + #print(n) + # Look for the best node to schedule + # which is the one giving the minimum FIFO increase + + # None selected + selected = -1 + + # Min FIFO size found + + for layer in self._topologicalSort: + minVal = 10000000 + selected = -1 + for node in layer: + # If the node can be scheduled + if n[node.sortedNodeID] > 0: + # Evolution vector for static scheduling + # v = self.evolutionVectorForNode(nodeID) + # for cyclo static we need the new fifo state + newB = self.evolutionVectorForNode(node.sortedNodeID) + b + # New fifos size after this evolution + # For static scheduling, fifo update would have been + # newB = np.dot(t,v) + b + #print(newB) + + # Check that there is no FIFO underflow or overfflow: + if np.all(newB >= 0) and np.all(newB <= bMax): + # Total FIFO size for this possible execution + # We normalize to get the occupancy number as explained above + theMin = (1.0*np.array(newB) / normV).max() + # If this possible evolution is giving smaller FIFO size + # (measured in occupancy number) then it is selected + if theMin < minVal: + minVal = theMin + selected = node.sortedNodeID + + if selected != -1: + # We put the selected node at the end of the layer + # so that we do not always try the same node + # each time we analyze this layer + # If several nodes can be selected it should allow + # to select them with same probability + layer.remove(node) + layer.append(node) + break + # For breaking from the outer loop too + else: + continue + break + # No node could be scheduled because of not enough data + # in the FIFOs. It should not occur if there is a null + # space of dimension 1. So, it is probably a bug if + # this exception is raised + if selected < 0: + raise DeadlockError + + # Now we have evaluated all schedulable nodes for this run + # and selected the one giving the smallest FIFO increase + # Implementation for static scheduling + # Real evolution vector for selected node + # evol = self.evolutionVectorForNode(selected) + # Keep track that this node has been schedule + # n = n - evol + # Compute new fifo state + # fifoChange = np.dot(t,evol) + # b = fifoChange + b + + # Implementation for cyclo static scheduling + #print("selected") + fifoChange = self.evolutionVectorForNode(selected,test=False) + b = fifoChange + b + # For cyclo static, we create an evolution vector + # which contains a 1 + # at a node position only if this node has executed + # its period. + # Otherwise the null vector is not decreased + v = np.zeros(len(self._sortedNodes)) + v[selected] = self._sortedNodes[selected].executeNode() + n = n - v + + + if config.displayFIFOSizes: + print(b) + + schedule.append(selected) + + # Analyze FIFOs to know if a FIFOs write is + # followed immediately by a FIFO read of same size + analyzeStep(fifoChange,allFIFOs,evolutionTime) + evolutionTime = evolutionTime + 1 + return(schedule) def computeSchedule(self,config=Configuration()): # First we must rewrite the graph and insert duplication @@ -639,11 +790,21 @@ class Graph(): self.insertDuplicates() networkMatrix = self.topologyMatrix() + #print(networkMatrix) + + if config.sinkPriority: + self.computeTopologicalSortOfNodes() + + mustDoSinkPrioritization = config.sinkPriority and len(self._topologicalSort)>0 + if config.sinkPriority and not mustDoSinkPrioritization: + print("Sink prioritization has been disabled. The graph has some loops") # Init values initB = self.initEvolutionVector initN = self.nullVector(networkMatrix) + #print(initB) #print(initN) + #print(np.dot(networkMatrix,initN)) # nullVector is giving the number of repetitions # for a node cycle. @@ -705,7 +866,7 @@ class Graph(): # to minimize the occupancy number of all FIFOs by # selecting the scheduling which is giving the # minimum maximum occupancy number after the run. - bMax = 1.0*np.array(initB) / normV + bMax = 1.0*np.array(initB) schedule=[] @@ -725,19 +886,20 @@ class Graph(): # Min FIFO size found minVal = 10000000 - nodeID = 0 + + for node in self._sortedNodes: # If the node can be scheduled - if n[nodeID] > 0: + if n[node.sortedNodeID] > 0: # Evolution vector for static scheduling # v = self.evolutionVectorForNode(nodeID) # for cyclo static we need the new fifo state - newB = self.evolutionVectorForNode(nodeID) + b + newB = self.evolutionVectorForNode(node.sortedNodeID) + b # New fifos size after this evolution # For static scheduling, fifo update would have been # newB = np.dot(t,v) + b #print(newB) - + # Check that there is no FIFO underflow: if np.all(newB >= 0): # Total FIFO size for this possible execution @@ -745,11 +907,11 @@ class Graph(): theMin = (1.0*np.array(newB) / normV).max() # If this possible evolution is giving smaller FIFO size # (measured in occupancy number) then it is selected + if theMin <= minVal: minVal = theMin - selected = nodeID + selected = node.sortedNodeID - nodeID = nodeID + 1 # No node could be scheduled because of not enough data # in the FIFOs. It should not occur if there is a null @@ -783,7 +945,7 @@ class Graph(): n = n - v - if config.displayFIFOSizes: + if config.displayFIFOSizes and not mustDoSinkPrioritization: print(b) bMax = np.maximum(b,bMax) @@ -791,14 +953,24 @@ class Graph(): # Analyze FIFOs to know if a FIFOs write is # followed immediately by a FIFO read of same size - analyzeStep(fifoChange,allFIFOs,evolutionTime) + if not mustDoSinkPrioritization: + analyzeStep(fifoChange,allFIFOs,evolutionTime) evolutionTime = evolutionTime + 1 fifoMax=np.floor(bMax).astype(np.int32) + + if mustDoSinkPrioritization: + schedule = self.computeTopologicalOrderSchedule(normV,allFIFOs,initB,bMax,initN,config) allBuffers=self.initializeFIFODescriptions(config,allFIFOs,fifoMax,evolutionTime) self._allFIFOs = allFIFOs self._allBuffers = allBuffers + + if config.dumpSchedule: + print("Schedule") + for nodeID in schedule: + print(self._sortedNodes[nodeID].nodeName) + print("") return(Schedule(self,self._sortedNodes,self._sortedEdges,schedule)) diff --git a/cmsisdsp/cg/static/scheduler/node.py b/cmsisdsp/cg/static/scheduler/node.py index 0c8d42e3..49d05988 100644 --- a/cmsisdsp/cg/static/scheduler/node.py +++ b/cmsisdsp/cg/static/scheduler/node.py @@ -252,6 +252,11 @@ class BaseNode: self._positionInCycle = 0 + self.sortedNodeID = 0 + # Updated during toplogical sorts to find the current + # sinks of the truncated graph + self.nbOutputsForTopologicalSort = 0 + def __getattr__(self,name): """Present inputs / outputs as attributes""" if name in self._inputs: @@ -518,6 +523,14 @@ class BaseNode: def hasManyIOs(self): return (self.hasManyInputs or self.hasManyOutputs) + @property + def nbInputs(self): + return(len(self._inputs.keys())) + + @property + def nbOutputs(self): + return(len(self._outputs.keys())) + @property def nbEmptyInputs(self): return (self.maxNbIOs - len(self._inputs.keys())) diff --git a/cmsisdsp/cg/static/scheduler/pythoncode.py b/cmsisdsp/cg/static/scheduler/pythoncode.py index 94cadc4d..3430d234 100644 --- a/cmsisdsp/cg/static/scheduler/pythoncode.py +++ b/cmsisdsp/cg/static/scheduler/pythoncode.py @@ -40,7 +40,7 @@ def gencode(sched,directory,config): template = env.get_template("code.py") - cfile=os.path.join(directory,"sched.py" % config.schedulerPythonFileName) + cfile=os.path.join(directory,"%s.py" % config.schedulerPythonFileName) with open(cfile,"w") as f: diff --git a/cmsisdsp/cg/static/scheduler/templates/code.cpp b/cmsisdsp/cg/static/scheduler/templates/code.cpp index 491b027b..a825ed3e 100644 --- a/cmsisdsp/cg/static/scheduler/templates/code.cpp +++ b/cmsisdsp/cg/static/scheduler/templates/code.cpp @@ -20,7 +20,9 @@ {% if config.eventRecorder -%} EventRecord2 (Evt_Node, {{nodes[s].codeID}}, 0); {% endif -%} + CG_BEFORE_NODE_EXECUTION; {{nodes[s].cRun()}} + CG_AFTER_NODE_EXECUTION; {% if config.eventRecorder -%} if (cgStaticError<0) { diff --git a/cmsisdsp/cg/static/scheduler/templates/codeArray.cpp b/cmsisdsp/cg/static/scheduler/templates/codeArray.cpp index fc17d2b9..2874431b 100644 --- a/cmsisdsp/cg/static/scheduler/templates/codeArray.cpp +++ b/cmsisdsp/cg/static/scheduler/templates/codeArray.cpp @@ -13,10 +13,15 @@ The support classes and code is covered by CMSIS-DSP license. #include "arm_math.h" #include "{{config.customCName}}" -#include "GenericNodes.h" +#include "{{config.genericNodeCName}}" #include "{{config.appNodesCName}}" #include "{{config.schedulerCFileName}}.h" +{% include "defineConfig.h" %} + + +CG_AFTER_INCLUDES + {% macro optionalargs() -%} {% if config.cOptionalArgs %},{{config.cOptionalArgs}}{% endif %} {% endmacro -%} @@ -35,6 +40,7 @@ static unsigned int schedule[{{schedLen}}]= {{schedDescription}} }; +CG_BEFORE_FIFO_BUFFERS /*********** FIFO buffers @@ -46,6 +52,7 @@ FIFO buffers {% for buf in sched._graph._allBuffers %} #define BUFFERSIZE{{buf._bufferID}} {{buf._length}} +CG_BEFORE_BUFFER {{buf._theType.ctype}} {{config.prefix}}buf{{buf._bufferID}}[BUFFERSIZE{{buf._bufferID}}]={0}; {% endfor %} @@ -74,6 +81,7 @@ public: }; {% endfor %} +CG_BEFORE_SCHEDULER_FUNCTION uint32_t {{config.schedName}}(int *error{{optionalargs()}}) { int cgStaticError=0; @@ -82,6 +90,7 @@ uint32_t {{config.schedName}}(int *error{{optionalargs()}}) int32_t debugCounter={{config.debugLimit}}; {% endif %} + CG_BEFORE_FIFO_INIT; /* Create FIFOs objects */ @@ -129,7 +138,9 @@ uint32_t {{config.schedName}}(int *error{{optionalargs()}}) {% if config.eventRecorder -%} EventRecord2 (Evt_Node, nodeId, 0); {% endif -%} + CG_BEFORE_NODE_EXECUTION; cgStaticError = nodeArray[nodeId]->run(); + CG_AFTER_NODE_EXECUTION; {% if config.eventRecorder -%} if (cgStaticError<0) { @@ -146,6 +157,7 @@ uint32_t {{config.schedName}}(int *error{{optionalargs()}}) } errorHandling: + CG_AFTER_SCHEDULE; *error=cgStaticError; return(nbSchedule); } \ No newline at end of file diff --git a/cmsisdsp/cg/static/scheduler/templates/codeSwitch.cpp b/cmsisdsp/cg/static/scheduler/templates/codeSwitch.cpp index 8e1b4cd9..551a83f9 100644 --- a/cmsisdsp/cg/static/scheduler/templates/codeSwitch.cpp +++ b/cmsisdsp/cg/static/scheduler/templates/codeSwitch.cpp @@ -30,6 +30,7 @@ static unsigned int schedule[{{schedLen}}]= {% if config.eventRecorder -%} EventRecord2 (Evt_Node, schedule[id], 0); {% endif -%} + CG_BEFORE_NODE_EXECUTION; switch(schedule[id]) { {% for nodeID in range(nbNodes) -%} @@ -43,6 +44,7 @@ static unsigned int schedule[{{schedLen}}]= default: break; } + CG_AFTER_NODE_EXECUTION; {% if config.eventRecorder -%} if (cgStaticError<0) { diff --git a/cmsisdsp/cg/static/scheduler/templates/commonc.cpp b/cmsisdsp/cg/static/scheduler/templates/commonc.cpp index 6b629e0a..41f9abb9 100644 --- a/cmsisdsp/cg/static/scheduler/templates/commonc.cpp +++ b/cmsisdsp/cg/static/scheduler/templates/commonc.cpp @@ -13,12 +13,15 @@ The support classes and code is covered by CMSIS-DSP license. #include "arm_math.h" #include "{{config.customCName}}" -#include "GenericNodes.h" +#include "{{config.genericNodeCName}}" #include "{{config.appNodesCName}}" #include "{{config.schedulerCFileName}}.h" +{% include "defineConfig.h" %} +CG_AFTER_INCLUDES + {% macro optionalargs() -%} {% if config.cOptionalArgs %},{{config.cOptionalArgs}}{% endif %} {% endmacro -%} @@ -26,6 +29,7 @@ The support classes and code is covered by CMSIS-DSP license. {% block schedArray %} {% endblock %} +CG_BEFORE_FIFO_BUFFERS /*********** FIFO buffers @@ -37,10 +41,12 @@ FIFO buffers {% for buf in sched._graph._allBuffers %} #define BUFFERSIZE{{buf._bufferID}} {{buf._length}} +CG_BEFORE_BUFFER {{buf._theType.ctype}} {{config.prefix}}buf{{buf._bufferID}}[BUFFERSIZE{{buf._bufferID}}]={0}; {% endfor %} +CG_BEFORE_SCHEDULER_FUNCTION uint32_t {{config.schedName}}(int *error{{optionalargs()}}) { int cgStaticError=0; @@ -49,6 +55,7 @@ uint32_t {{config.schedName}}(int *error{{optionalargs()}}) int32_t debugCounter={{config.debugLimit}}; {% endif %} + CG_BEFORE_FIFO_INIT; /* Create FIFOs objects */ @@ -74,6 +81,7 @@ uint32_t {{config.schedName}}(int *error{{optionalargs()}}) {% block scheduleLoop %} {% endblock %} errorHandling: + CG_AFTER_SCHEDULE; *error=cgStaticError; return(nbSchedule); } \ No newline at end of file diff --git a/cmsisdsp/cg/static/scheduler/templates/defineConfig.h b/cmsisdsp/cg/static/scheduler/templates/defineConfig.h new file mode 100644 index 00000000..62cb6e4d --- /dev/null +++ b/cmsisdsp/cg/static/scheduler/templates/defineConfig.h @@ -0,0 +1,55 @@ +#if !defined(CHECKERROR) +#define CHECKERROR if (cgStaticError < 0) \ + {\ + goto errorHandling;\ + } + +#endif + +#if !defined(CG_BEFORE_ITERATION) +#define CG_BEFORE_ITERATION +#endif + +#if !defined(CG_AFTER_ITERATION) +#define CG_AFTER_ITERATION +#endif + +#if !defined(CG_BEFORE_SCHEDULE) +#define CG_BEFORE_SCHEDULE +#endif + +#if !defined(CG_AFTER_SCHEDULE) +#define CG_AFTER_SCHEDULE +#endif + +#if !defined(CG_BEFORE_BUFFER) +#define CG_BEFORE_BUFFER +#endif + +#if !defined(CG_BEFORE_FIFO_BUFFERS) +#define CG_BEFORE_FIFO_BUFFERS +#endif + +#if !defined(CG_BEFORE_FIFO_INIT) +#define CG_BEFORE_FIFO_INIT +#endif + +#if !defined(CG_BEFORE_NODE_INIT) +#define CG_BEFORE_NODE_INIT +#endif + +#if !defined(CG_AFTER_INCLUDES) +#define CG_AFTER_INCLUDES +#endif + +#if !defined(CG_BEFORE_SCHEDULER_FUNCTION) +#define CG_BEFORE_SCHEDULER_FUNCTION +#endif + +#if !defined(CG_BEFORE_NODE_EXECUTION) +#define CG_BEFORE_NODE_EXECUTION +#endif + +#if !defined(CG_AFTER_NODE_EXECUTION) +#define CG_AFTER_NODE_EXECUTION +#endif \ No newline at end of file diff --git a/cmsisdsp/version.py b/cmsisdsp/version.py index f2709910..3c94aa56 100755 --- a/cmsisdsp/version.py +++ b/cmsisdsp/version.py @@ -1,2 +1,2 @@ # Python wrapper version -__version__ = "1.8.1" +__version__ = "1.9.0"