Improvement to compute graph

More customization options
New (experimental) scheduling algorithm to improve latencies.
pull/53/head
Christophe Favergeon 3 years ago
parent 8ed2ef7a7e
commit 5a99a5c5ad

@ -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.

@ -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

@ -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)

@ -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<float32_t,FIFOSIZE0,0> fifo0(buf0);
FIFO<float32_t,FIFOSIZE1,1> fifo1(buf1);
FIFO<float32_t,FIFOSIZE0,0> fifo0(buf1);
FIFO<float32_t,FIFOSIZE1,1> fifo1(buf2);
CG_BEFORE_NODE_INIT;
/*
Create node objects
*/
@ -60,43 +123,47 @@ uint32_t scheduler(int *error,int someVariable)
Source<float32_t,5> 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);
}

@ -15,6 +15,7 @@ extern "C"
{
#endif
extern uint32_t scheduler(int *error,int someVariable);
#ifdef __cplusplus

@ -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)

@ -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<float32_t,FIFOSIZE0,0> fifo0(buf0,10);
FIFO<float32_t,FIFOSIZE1,1> fifo1(buf1);
FIFO<float32_t,FIFOSIZE2,1> fifo2(buf2);
FIFO<float32_t,FIFOSIZE3,1> fifo3(buf3);
FIFO<float32_t,FIFOSIZE4,1> fifo4(buf4);
FIFO<float32_t,FIFOSIZE5,0> fifo5(buf5);
FIFO<float32_t,FIFOSIZE6,1> fifo6(buf6);
FIFO<float32_t,FIFOSIZE7,0> fifo7(buf7);
FIFO<float32_t,FIFOSIZE8,1> fifo8(buf8);
FIFO<float32_t,FIFOSIZE0,0> fifo0(buf1,10);
FIFO<float32_t,FIFOSIZE1,1> fifo1(buf2);
FIFO<float32_t,FIFOSIZE2,1> fifo2(buf3);
FIFO<float32_t,FIFOSIZE3,1> fifo3(buf4);
FIFO<float32_t,FIFOSIZE4,1> fifo4(buf5);
FIFO<float32_t,FIFOSIZE5,0> fifo5(buf6);
FIFO<float32_t,FIFOSIZE6,1> fifo6(buf7);
FIFO<float32_t,FIFOSIZE7,0> fifo7(buf8);
FIFO<float32_t,FIFOSIZE8,1> fifo8(buf9);
CG_BEFORE_NODE_INIT;
/*
Create node objects
*/
@ -105,17 +175,19 @@ uint32_t scheduler(int *error,int opt1,int opt2)
Unzip<float32_t,320,float32_t,160,float32_t,160> 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);
}

@ -15,6 +15,7 @@ extern "C"
{
#endif
extern uint32_t scheduler(int *error,int opt1,int opt2);
#ifdef __cplusplus

@ -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)

@ -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<float32_t,FIFOSIZE0,0> fifo0(buf0);
FIFO<float32_t,FIFOSIZE1,1> fifo1(buf1);
FIFO<float32_t,FIFOSIZE2,1> fifo2(buf2);
FIFO<float32_t,FIFOSIZE3,1> fifo3(buf3);
FIFO<float32_t,FIFOSIZE4,1> fifo4(buf4);
FIFO<float32_t,FIFOSIZE5,1> fifo5(buf5);
FIFO<float32_t,FIFOSIZE6,1> fifo6(buf6);
FIFO<float32_t,FIFOSIZE7,0> fifo7(buf7);
FIFO<float32_t,FIFOSIZE0,0> fifo0(buf1);
FIFO<float32_t,FIFOSIZE1,1> fifo1(buf2);
FIFO<float32_t,FIFOSIZE2,1> fifo2(buf3);
FIFO<float32_t,FIFOSIZE3,1> fifo3(buf4);
FIFO<float32_t,FIFOSIZE4,1> fifo4(buf5);
FIFO<float32_t,FIFOSIZE5,1> fifo5(buf6);
FIFO<float32_t,FIFOSIZE6,1> fifo6(buf7);
FIFO<float32_t,FIFOSIZE7,0> fifo7(buf8);
CG_BEFORE_NODE_INIT;
/*
Create node objects
*/
@ -95,11 +164,14 @@ uint32_t scheduler(int *error)
ToReal<float32_t,512,float32_t,256> 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);
}

@ -15,6 +15,7 @@ extern "C"
{
#endif
extern uint32_t scheduler(int *error);
#ifdef __cplusplus

@ -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)

@ -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

@ -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()

@ -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"

@ -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()

@ -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<float32_t,FIFOSIZE0,0> fifo0(buf0);
FIFO<float32_t,FIFOSIZE1,1> fifo1(buf1);
FIFO<float32_t,FIFOSIZE2,1> fifo2(buf2);
FIFO<float32_t,FIFOSIZE3,0> fifo3(buf3);
FIFO<float32_t,FIFOSIZE0,0> fifo0(buf1);
FIFO<float32_t,FIFOSIZE1,1> fifo1(buf2);
FIFO<float32_t,FIFOSIZE2,1> fifo2(buf3);
FIFO<float32_t,FIFOSIZE3,0> fifo3(buf4);
CG_BEFORE_NODE_INIT;
/*
Create node objects
*/
@ -72,57 +137,59 @@ uint32_t scheduler(int *error,arm_mfcc_instance_f32 *mfccConfig)
FileSource<float32_t,192> 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);
}

@ -15,6 +15,7 @@ extern "C"
{
#endif
extern uint32_t scheduler(int *error,arm_mfcc_instance_f32 *mfccConfig);
#ifdef __cplusplus

@ -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"

@ -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"

@ -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<complex,FIFOSIZE0,0> fifo0(buf0);
FIFO<complex,FIFOSIZE1,1> fifo1(buf1);
FIFO<complex,FIFOSIZE2,1> fifo2(buf2);
FIFO<complex,FIFOSIZE3,1> fifo3(buf3);
FIFO<complex,FIFOSIZE4,1> fifo4(buf4);
FIFO<complex,FIFOSIZE5,1> fifo5(buf5);
FIFO<complex,FIFOSIZE0,0> fifo0(buf1);
FIFO<complex,FIFOSIZE1,1> fifo1(buf2);
FIFO<complex,FIFOSIZE2,1> fifo2(buf3);
FIFO<complex,FIFOSIZE3,1> fifo3(buf4);
FIFO<complex,FIFOSIZE4,1> fifo4(buf5);
FIFO<complex,FIFOSIZE5,1> fifo5(buf6);
CG_BEFORE_NODE_INIT;
/*
Create node objects
*/
@ -84,71 +151,71 @@ uint32_t scheduler(int *error,int someVariable)
Source<complex,5> 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);
}

@ -15,6 +15,7 @@ extern "C"
{
#endif
extern uint32_t scheduler(int *error,int someVariable);
#ifdef __cplusplus

@ -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

@ -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 <iostream>
template<typename IN, int inputSize>
class Sink: public GenericSink<IN, inputSize>
{
public:
Sink(FIFOBase<IN> &src):GenericSink<IN,inputSize>(src){};
int run()
{
IN *b=this->getReadBuffer();
printf("Sink\n");
for(int i=0;i<inputSize;i++)
{
std::cout << (int)b[i] << std::endl;
}
return(0);
};
};
template<typename OUT,int outputSize>
class Source: GenericSource<OUT,outputSize>
{
public:
Source(FIFOBase<OUT> &dst):GenericSource<OUT,outputSize>(dst),mCounter(0){};
int run(){
OUT *b=this->getWriteBuffer();
printf("Source\n");
for(int i=0;i<outputSize;i++)
{
b[i] = (OUT)mCounter++;
}
return(0);
};
int mCounter;
};
template<typename IN1, int inputSize1,
typename IN2, int inputSize2,
typename OUT,int outputSize>
class ProcessingNode;
template<int inputSize>
class ProcessingNode<float32_t,inputSize,
float32_t,inputSize,
float32_t,inputSize>:
public GenericNode21<float32_t,inputSize,
float32_t,inputSize,
float32_t,inputSize>
{
public:
ProcessingNode(FIFOBase<float32_t> &src1,
FIFOBase<float32_t> &src2,
FIFOBase<float32_t> &dst):
GenericNode21<float32_t,inputSize,
float32_t,inputSize,
float32_t,inputSize>(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<inputSize;i++)
{
c[i] = a[i] + 0.5f*b[i];
}
return(0);
};
};
#endif

@ -0,0 +1,13 @@
cmake_minimum_required (VERSION 3.14)
include(CMakePrintHelpers)
project(Example9)
add_executable(example9 main.cpp)
sdf(example9)
add_sdf_dir(example9)
target_include_directories(example9 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_include_directories(example9 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/generated)

@ -0,0 +1,3 @@
#ifndef _CUSTOM_H_
#endif

@ -0,0 +1,188 @@
/*
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.
*/
#include "arm_math.h"
#include "custom.h"
#include "GenericNodes.h"
#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.
*/
static unsigned int schedule[4]=
{
3,1,0,2,
};
CG_BEFORE_FIFO_BUFFERS
/***********
FIFO buffers
************/
#define FIFOSIZE0 5
#define FIFOSIZE1 5
#define FIFOSIZE2 5
#define FIFOSIZE3 5
#define BUFFERSIZE1 5
CG_BEFORE_BUFFER
float32_t buf1[BUFFERSIZE1]={0};
#define BUFFERSIZE2 5
CG_BEFORE_BUFFER
float32_t buf2[BUFFERSIZE2]={0};
#define BUFFERSIZE3 5
CG_BEFORE_BUFFER
float32_t buf3[BUFFERSIZE3]={0};
#define BUFFERSIZE4 5
CG_BEFORE_BUFFER
float32_t buf4[BUFFERSIZE4]={0};
CG_BEFORE_SCHEDULER_FUNCTION
uint32_t scheduler(int *error,int someVariable)
{
int cgStaticError=0;
uint32_t nbSchedule=0;
int32_t debugCounter=2;
CG_BEFORE_FIFO_INIT;
/*
Create FIFOs objects
*/
FIFO<float32_t,FIFOSIZE0,1> fifo0(buf1);
FIFO<float32_t,FIFOSIZE1,1> fifo1(buf2);
FIFO<float32_t,FIFOSIZE2,1> fifo2(buf3);
FIFO<float32_t,FIFOSIZE3,0> fifo3(buf4,5);
CG_BEFORE_NODE_INIT;
/*
Create node objects
*/
Duplicate2<float32_t,5,float32_t,5,float32_t,5> dup0(fifo1,fifo2,fifo3);
ProcessingNode<float32_t,5,float32_t,5,float32_t,5> filter(fifo0,fifo3,fifo1);
Sink<float32_t,5> sink(fifo2);
Source<float32_t,5> 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);
}

@ -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

@ -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)

@ -0,0 +1,11 @@
#include <cstdio>
#include <cstdint>
#include "scheduler.h"
int main(int argc, char const *argv[])
{
int error;
printf("Start\n");
uint32_t nbSched=scheduler(&error,1);
return 0;
}

@ -0,0 +1,80 @@
digraph structs {
node [shape=plaintext]
rankdir=LR
edge [arrowsize=0.5]
fontname="times"
dup0 [shape=point,label=dup0]
filter [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
<TR>
<TD PORT="ia"><FONT POINT-SIZE="9.0">ia</FONT></TD>
<TD ALIGN="CENTER" ROWSPAN="2">filter<BR/>(ProcessingNode)</TD>
<TD PORT="o"><FONT POINT-SIZE="9.0">o</FONT></TD>
</TR>
<TR>
<TD PORT="ib"><FONT POINT-SIZE="9.0">ib</FONT></TD>
<TD></TD></TR>
</TABLE>>];
sink [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
<TR>
<TD ALIGN="CENTER" PORT="i">sink<BR/>(Sink)</TD>
</TR>
</TABLE>>];
source [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
<TR>
<TD ALIGN="CENTER" PORT="i">source<BR/>(Source)</TD>
</TR>
</TABLE>>];
source:i -> filter:ia [label="f32(5)"
,headlabel=<<TABLE BORDER="0" CELLPADDING="2"><TR><TD><FONT COLOR="blue" POINT-SIZE="12.0" >5</FONT>
</TD></TR></TABLE>>
,taillabel=<<TABLE BORDER="0" CELLPADDING="2"><TR><TD><FONT COLOR="blue" POINT-SIZE="12.0" >5</FONT>
</TD></TR></TABLE>>]
filter:o ->
dup0 [label="f32(5)"
,taillabel=<<TABLE BORDER="0" CELLPADDING="2"><TR><TD><FONT COLOR="blue" POINT-SIZE="12.0" >5</FONT>
</TD></TR></TABLE>>]
dup0 -> sink:i [label="f32(5)"
,headlabel=<<TABLE BORDER="0" CELLPADDING="2"><TR><TD><FONT COLOR="blue" POINT-SIZE="12.0" >5</FONT>
</TD></TR></TABLE>>
]
dup0Delay [label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
<TR>
<TD ALIGN="CENTER" PORT="i">5</TD>
</TR>
</TABLE>>];
dup0 -> dup0Delay:i [label=""]
dup0Delay:i -> filter:ib [label="f32(5)"
,headlabel=<<TABLE BORDER="0" CELLPADDING="2"><TR><TD><FONT COLOR="blue" POINT-SIZE="12.0" >5</FONT>
</TD></TR></TABLE>>]
}

@ -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

@ -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)

@ -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)

@ -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,8 +677,111 @@ 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
# nodes when an ouput is connected to several inputs.
@ -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,14 +886,15 @@ 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
@ -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))

@ -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()))

@ -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:

@ -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)
{

@ -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);
}

@ -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)
{

@ -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);
}

@ -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

@ -1,2 +1,2 @@
# Python wrapper version
__version__ = "1.8.1"
__version__ = "1.9.0"

Loading…
Cancel
Save