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 00000000..08e298de Binary files /dev/null and b/ComputeGraph/examples/example9/test.pdf differ diff --git a/PythonWrapper_README.md b/PythonWrapper_README.md index a098f6d7..a3b122b0 100644 --- a/PythonWrapper_README.md +++ b/PythonWrapper_README.md @@ -11,11 +11,11 @@ The signal processing chain can thus be tested and developed in a Python environ A tutorial is also available but with less details than this README: https://developer.arm.com/documentation/102463/latest/ -This wrapper is also containing the scripts for the new CMSIS-DSP compute graph framework (CG). +This wrapper is also containing the scripts for the new [CMSIS-DSP compute graph framework](https://github.com/ARM-software/CMSIS-DSP/tree/main/ComputeGraph) (CG). CG is also including some nodes to communicate with Modelica using the VHT Modelica blocks developed as part of our [VHT-SystemModeling](https://github.com/ARM-software/VHT-SystemModeling) demos. - +An history of the changes to this wrapper is available at the end of the README. # How to build and install @@ -235,3 +235,11 @@ MEL filters are represented as 3 arrays to encode a sparse array. The wrapper is now containing the compute graph Python scripts and you should refer the the documentation in `DSP/ComputeGraph` folder to know how to use those tools. + + +# Change history + +## Version 1.9.0: + +* New scheduling mode, in the compute graph generator, giving priority to sinks in the scheduling. The idea is to try to decrease the latency between sinks and sources. +* More customization options (Macros to be defined) in the C++ code generated by the compute graph generator diff --git a/cmsisdsp/__init__.py b/cmsisdsp/__init__.py index d12403ea..ae3b3767 100755 --- a/cmsisdsp/__init__.py +++ b/cmsisdsp/__init__.py @@ -23,7 +23,7 @@ cmsis_dsp_version="1.14.1" # CMSIS-DSP Commit hash used to build the wrapper -commit_hash="b8177102d9a4aaf83fd3f067364ecfa3100966c2" +commit_hash="8ed2ef7a7e2c6fce29c59d18ba7ee0f9f66027c2" # True if development version of CMSIS-DSP used # (So several CMSIS-DSP versions may have same version number hence the commit hash) diff --git a/cmsisdsp/cg/static/scheduler/config.py b/cmsisdsp/cg/static/scheduler/config.py index 10627da8..fea3e427 100644 --- a/cmsisdsp/cg/static/scheduler/config.py +++ b/cmsisdsp/cg/static/scheduler/config.py @@ -29,6 +29,40 @@ class Configuration: def __init__(self): + + ######################### + # + # SCHEDULING OPTIONS + # + + # If FIFOs size must be dumped during computation of the schedule + self.displayFIFOSizes=False + + # Experimental so disabled by default + self.memoryOptimization = False + + # Give priority to sink with topological sort + self.sinkPriority = True + + # Print schedule in a human understandble form + self.dumpSchedule = False + + ############################# + # + # GRAPHVIZ GENERATION OPTIONS + # + + # True for an horizontal graphviz layout + self.horizontal = True + + # Display FIFO buffers in graph instead of datatype + self.displayFIFOBuf = False + + ######################### + # + # CODE GENERATION OPTIONS + # + # Number of iterations of the schedule. By default it is infinite # represented by the value 0 self.debugLimit = 0 @@ -36,9 +70,6 @@ class Configuration: # If FIFO content must be dumped during execution of the schedule. self.dumpFIFO = False - # If FIFOs size must be dumped during computation of the schedule - self.displayFIFOSizes=False - # Name of scheduling function in the generated code # (Must be valid C and Python name) self.schedName = "scheduler" @@ -54,9 +85,6 @@ class Configuration: # Prefix to add before the global FIFO buffer names self.prefix = "" - # Experimental so disabled by default - self.memoryOptimization = False - # Path to CG module for Python simu self.pathToCGModule="../../.." @@ -68,12 +96,6 @@ class Configuration: # we can generate a switch/case instead self.switchCase = True - # True for an horizontal graphviz layout - self.horizontal = True - - # Display FIFO buffers in graph instead of datatype - self.displayFIFOBuf = False - # Enable support for CMSIS Event Recorder self.eventRecorder = False @@ -85,11 +107,13 @@ class Configuration: self.customCName = "custom.h" self.customPythonName = "custom" + # Name of generic nodes headers + self.genericNodeCName = "GenericNodes.h" + # Name of scheduler source and header files self.schedulerCFileName = "scheduler" self.schedulerPythonFileName = "sched" - @property def debug(self): return (self.debugLimit > 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"