CMSIS-DSP: Added code size optimization for the SDF C++ generator.

pull/19/head
Christophe Favergeon 4 years ago
parent 60475b816d
commit 72483e7c3f

@ -150,13 +150,14 @@ This configuration object can be used as argument of the scheduling function (na
There are other fields for the configuration:
- dumpFIFO : Will dump the output FIFOs content after each execution of the node (the code generator is inserting calls to the FIFO dump function)
- displayFIFOSizes : During the computation of the schedule, the Python script is displaying the evolution of the FIFO lengths.
- schedName : The name of the scheduler function (`scheduler` by default)
- cOptionalArgs and pyOptionalArgs for passing additional arguments to the scheduling function
- prefix to prefix the same of the global buffers
- memoryOptimization : Experimental. It is attempting to reuse buffer memory and share it between several FIFOs
- pathToSDFModule : Path to the Python SDF module so that the generated Python code can find it
- `dumpFIFO` : Will dump the output FIFOs content after each execution of the node (the code generator is inserting calls to the FIFO dump function)
- `displayFIFOSizes` : During the computation of the schedule, the Python script is displaying the evolution of the FIFO lengths.
- `schedName` : The name of the scheduler function (`scheduler` by default)
- `cOptionalArgs` and pyOptionalArgs for passing additional arguments to the scheduling function
- `prefix` to prefix the same of the global buffers
- `memoryOptimization` : Experimental. It is attempting to reuse buffer memory and share it between several FIFOs
- `pathToSDFModule` : Path to the Python SDF module so that the generated Python code can find it
- `codeArray` : Experimental. When a schedule is very long, representing it as a sequence of function calls is not good for the code size of the generated solution. When this option is enabled, the schedule is described with an array. It implies that the pure function calls cannot be inlined any more and are replaced by new nodes which are automatically generated.
In the example 1, we are passing a variable to initialize the node of type ProcessingNode. So, it would be great if this variable was an argument of the scheduler function. So we define:

@ -48,9 +48,10 @@ uint32_t scheduler(int *error,int someVariable)
Sink<float32_t,5> sink(fifo1);
Source<float32_t,5> source(fifo0);
/* Run several schedule iterations */
while((sdfError==0) && (debugCounter > 0))
{
/* Run a schedule iteration */
sdfError = source.run();
CHECKERROR;
sdfError = source.run();

@ -68,7 +68,7 @@ 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:

@ -86,9 +86,10 @@ uint32_t scheduler(int *error,int opt1,int opt2)
StereoSource<float32_t,320> src(fifo0);
Unzip<float32_t,320,float32_t,160,float32_t,160> toMono(fifo0,fifo1,fifo2);
/* Run several schedule iterations */
while((sdfError==0) && (debugCounter > 0))
{
/* Run a schedule iteration */
sdfError = src.run();
CHECKERROR;
sdfError = toMono.run();

@ -104,6 +104,7 @@ 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:

@ -83,9 +83,10 @@ uint32_t scheduler(int *error)
ToComplex<float32_t,256,float32_t,512> toCmplx(fifo2,fifo3);
ToReal<float32_t,512,float32_t,256> toReal(fifo5,fifo6);
/* Run several schedule iterations */
while((sdfError==0) && (debugCounter > 0))
{
/* Run a schedule iteration */
sdfError = src.run();
CHECKERROR;
sdfError = audioWin.run();

@ -64,6 +64,7 @@ print("Memory usage %d bytes" % sched.memory)
#conf.dumpFIFO=True
#conf.codeArray=True
sched.ccode("generated",config=conf)
with open("test.dot","w") as f:

@ -51,7 +51,7 @@ buf7=np.zeros(FIFOSIZE7,dtype=np.float32)
def scheduler(dispbuf):
sdfError=0
nbSchedule=0
debugCounter=42;
debugCounter=42
#
# Create FIFOs objects
@ -68,14 +68,14 @@ def scheduler(dispbuf):
#
# Create node objects
#
audioOverlap = OverlapAdd(256,128,fifo6,fifo7);
audioWin = SlidingBuffer(256,128,fifo0,fifo1);
cfft = CFFT(512,512,fifo3,fifo4);
icfft = ICFFT(512,512,fifo4,fifo5);
sink = FileSink(192,fifo7,"output_example3.txt",dispbuf);
src = FileSource(192,fifo0,"input_example3.txt");
toCmplx = ToComplex(256,512,fifo2,fifo3);
toReal = ToReal(512,256,fifo5,fifo6);
audioOverlap = OverlapAdd(256,128,fifo6,fifo7)
audioWin = SlidingBuffer(256,128,fifo0,fifo1)
cfft = CFFT(512,512,fifo3,fifo4)
icfft = ICFFT(512,512,fifo4,fifo5)
sink = FileSink(192,fifo7,"output_example3.txt",dispbuf)
src = FileSource(192,fifo0,"input_example3.txt")
toCmplx = ToComplex(256,512,fifo2,fifo3)
toReal = ToReal(512,256,fifo5,fifo6)
while((sdfError==0) and (debugCounter > 0)):
nbSchedule = nbSchedule + 1

@ -60,9 +60,10 @@ uint32_t scheduler(int *error,arm_mfcc_instance_f32 *mfccConfig)
FileSink<float32_t,13> sink(fifo3,"output_example6.txt");
FileSource<float32_t,192> src(fifo0,"input_example6.txt");
/* Run several schedule iterations */
while((sdfError==0) && (debugCounter > 0))
{
/* Run a schedule iteration */
sdfError = src.run();
CHECKERROR;
sdfError = audioWin.run();

@ -50,6 +50,7 @@ conf=Configuration()
conf.debugLimit=1
conf.cOptionalArgs="arm_mfcc_instance_f32 *mfccConfig"
#conf.codeArray=True
sched.ccode("generated",config=conf)
with open("test.dot","w") as f:

@ -22,4 +22,4 @@ nbMFCCOutputs = int(np.floor(sample_rate / (FFTSize >> 1)))
if nbMFCCOutputs%2 == 1:
nbMFCCOutputs = nbMFCCOutputs + 1
print(nbMFCCOutputs)
print("nbMFCCOutputs = %d " % nbMFCCOutputs)

@ -32,7 +32,7 @@
template<typename OUT,int outputSize>
class AudioSource: GenericSource<OUT,outputSize>
class AudioSource: public GenericSource<OUT,outputSize>
{
public:
AudioSource(FIFOBase<OUT> &dst,ring_config_t *config):

@ -37,7 +37,7 @@ template<int inputSize,int outputSize>
class StereoToMono<q15_t,inputSize,q15_t,outputSize>: public GenericNode<q15_t,inputSize,q15_t,outputSize>
{
public:
StereoToMonoQ15(FIFOBase<q15_t> &src,FIFOBase<q15_t> &dst):
StereoToMono(FIFOBase<q15_t> &src,FIFOBase<q15_t> &dst):
GenericNode<q15_t,inputSize,q15_t,outputSize>(src,dst){};

@ -37,7 +37,7 @@ file is reached.
*/
template<int outputSize>
class FileSource<float32_t,outputSize>: GenericSource<float32_t,outputSize>
class FileSource<float32_t,outputSize>: public GenericSource<float32_t,outputSize>
{
public:
FileSource(FIFOBase<float32_t> &dst,std::string name):GenericSource<float32_t,outputSize>(dst),

@ -36,7 +36,19 @@ def gencode(sched,directory,config):
trim_blocks=True
)
ctemplate = env.get_template("code.cpp")
schedDescription=""
if config.codeArray:
ctemplate = env.get_template("codeArray.cpp")
nb = 0
for s in sched.schedule:
schedDescription = schedDescription + ("%d," % sched.nodes[s].codeID)
nb = nb + 1
if nb == 40:
nb=0
schedDescription = schedDescription + "\n"
else:
ctemplate = env.get_template("code.cpp")
htemplate = env.get_template("code.h")
@ -48,10 +60,14 @@ def gencode(sched,directory,config):
with open(cfile,"w") as f:
print(ctemplate.render(fifos=sched._graph._allFIFOs,
nbFifos=nbFifos,
nbNodes=len(sched.nodes),
nodes=sched.nodes,
pureNodes=sched.pureNodes,
schedule=sched.schedule,
schedLen=len(sched.schedule),
config=config,
sched=sched
sched=sched,
schedDescription=schedDescription
),file=f)
with open(hfile,"w") as f:

@ -60,6 +60,11 @@ class Configuration:
# Path to SDF module for Python simu
self.pathToSDFModule="../.."
# When codeArray is true, instead of using
# function calls we parse un array giving
# the index of functions to call in another array
self.codeArray = False
@property
def debug(self):
return (self.debugLimit > 0)

@ -541,29 +541,67 @@ class Schedule:
self._sortedEdges=sortedEdges
self._schedule = schedule
self._graph = g
# Nodes containing pure functions (no state) like some
# CMSIS-DSP functions.
# When scheduling is using the option codeArray, the
# schedule is encoded as an array.
# Function calls cannot be inlined anymore and we need
# to create new nodes for those function calls.
# The pureNode structure is done for this.
# It is a record because we want to reuse nodes for same
# types.
self._pureNodes={}
nodeCodeID = 0
pureClassID = 1
for n in self.nodes:
n.codeID = nodeCodeID
nodeCodeID = nodeCodeID + 1
# Constant nodes are ignored since they have
# no arcs, and are connected to no FIFOs
theArgs=[]
theArgTypes=[]
i,o=n.allIOs()
for io in i:
# An io connected to a constant node has no fifo
if not io.fifo is None:
theArgs.append(self.fifoID(io.fifo))
theArgTypes.append(io.ctype)
else:
# Instead the arg is the name of a constant node
# instead of being a fifo ID
theArgs.append(io.constantNode.name)
theArgTypes.append(io.constantNode.name)
for io in o:
theArgs.append(self.fifoID(io.fifo))
theArgTypes.append(io.ctype)
n.args=theArgs
# Analyze the nature of arguments for pure functions
# The information created during this analysis
# is useful when generating a class containing the
# pure function
if not n.hasState:
theType=(n.nodeName,tuple(theArgTypes))
if not theType in self._pureNodes:
self._pureNodes[theType]=n
n.pureClassID = pureClassID
pureClassID = pureClassID + 1
else:
n.pureClassID = self._pureNodes[theType].pureClassID
n.pureNodeType=theType
n.analyzeArgs()
def hasDelay(self,edge):
return(self._graph.hasDelay(edge))
def getDelay(self,edge):
return(self._graph.getDelay(edge))
@property
def pureNodes(self):
return self._pureNodes
@property
def constantEdges(self):
return self._graph.constantEdges

@ -28,6 +28,10 @@
"""Description of the basic types used to build a dataflow graph"""
from jinja2 import Environment, PackageLoader, select_autoescape
class NoFunctionArrayInPython(Exception):
pass
def camelCase(st):
output = ''.join(x for x in st.title() if x.isalnum())
return output[0].lower() + output[1:]
@ -254,6 +258,10 @@ class BaseNode:
return(ins,outs)
def ioTemplate(self):
"""Template arguments for C
input type, input size ...
@ -303,6 +311,16 @@ class BaseNode:
return ("sdfError = %s.run();" % self.nodeName)
else:
return ("sdfError = %s.run()" % self.nodeName)
def cFunc(self,ctemplate=True):
"""Function call for code array scheduler
Some nodes may customize it
"""
if ctemplate:
return ("(runNode)&%s<%s>::run" % (self.typeName,self.ioTemplate()))
else:
raise NoFunctionArrayInPython
@property
@ -310,11 +328,13 @@ class BaseNode:
"""List of fifos args for object initialization"""
return self._args
@property
def args(self):
"""String of fifo args for object initialization
with literal argument and variable arguments"""
allArgs=self.listOfargs
# Add specific argrs after FIFOs
if self.schedArgs:
for lit in self.schedArgs:
allArgs.append(lit.arg)
@ -323,15 +343,23 @@ class BaseNode:
@args.setter
def args(self,fifoIDs):
res=[]
# Template args is used only for code array
# scheduler when we create on the fly a new class
# for a function.
# In this case, the arguments of the template must only be
# fifos and not constant.
templateargs=[]
for x in fifoIDs:
# If args is a FIFO we generate a name using fifo ids
if isinstance(x,int):
res.append("fifo%d" % x)
templateargs.append("fifo%d" % x)
# If args is a constant node, we just use the constant node name
# (Defined in C code)
else:
res.append(x)
self._args=res
self._templateargs=templateargs
# For graphviz generation
@ -491,9 +519,10 @@ class GenericFunction(GenericNode):
# Used to generate unique ID and names when
# unique names are required
# like for creating the graph where each call to
# the same functiion must be identified as a
# the same function must be identified as a
# separate node
NODEID={}
PUREID=1
ENV = Environment(
loader=PackageLoader("sdf"),
@ -503,19 +532,147 @@ class GenericFunction(GenericNode):
)
CTEMPLATE = ENV.get_template("cmsis.cpp")
CNODETEMPLATE = ENV.get_template("cmsisNode.cpp")
PYTEMPLATE = ENV.get_template("cmsis.py")
def __init__(self,funcname,theType,length):
if not (funcname in Dsp.NODEID):
Dsp.NODEID[funcname]=1
if not (funcname in GenericFunction.NODEID):
GenericFunction.NODEID[funcname]=1
GenericNode.__init__(self,"%s%d" % (funcname,Dsp.NODEID[funcname]))
self._pureNodeID = GenericFunction.PUREID
GenericFunction.PUREID = GenericFunction.PUREID + 1
GenericNode.__init__(self,"%s%d" % (funcname,GenericFunction.NODEID[funcname]))
self._hasState = False
self._length = length
self._nodeName = funcname
Dsp.NODEID[funcname]=Dsp.NODEID[funcname]+1
GenericFunction.NODEID[funcname]=GenericFunction.NODEID[funcname]+1
# For class generated on the fly to contain a function call
# Analyze which args are constant instead of being FIFOs
def analyzeArgs(self):
inputid=0
outputid=0
# Arguments to use when calling the constructor
ios=[]
# Template params
temptypes=[]
specializedtemptypes=[]
# template args
tempargs=[]
# Template params for the generic node
tempgen=[]
# Datatypes for the constructor
constructortypes=[]
# Args for the generic constructor
genericconstructorargs=[]
typenameID = 1
# Use ordered io names
for io in self.inputNames:
x = self._inputs[io]
if not x.constantNode:
inputid = inputid + 1
temptypes.append("typename T%d, int input%dSize" % (typenameID,inputid))
specializedtemptypes.append("int input%dSize" % (inputid))
tempargs.append("%s,input%dSize" % (x.ctype,inputid))
tempgen.append("%s,input%dSize" % (x.ctype,inputid))
constructortypes.append("FIFOBase<%s> &src%d" % (x.ctype,inputid))
genericconstructorargs.append("src%d" % inputid)
ios.append("%s,%d" % (x.ctype,x.nbSamples))
typenameID = typenameID + 1
for io in self.outputNames:
x = self._outputs[io]
if not x.constantNode:
outputid = outputid + 1
temptypes.append("typename T%d,int output%dSize" % (typenameID,outputid))
specializedtemptypes.append("int output%dSize" % (outputid))
tempargs.append("%s,output%dSize" % (x.ctype,outputid))
tempgen.append("%s,output%dSize" % (x.ctype,outputid))
constructortypes.append("FIFOBase<%s> &dst%d" % (x.ctype,outputid))
genericconstructorargs.append("dst%d" % outputid)
ios.append("%s,%d" % (x.ctype,x.nbSamples))
typenameID = typenameID + 1
self._realInputs = inputid
self._realOutputs = outputid
# Arguments to use when calling the constructor
self._constructorTypes = "".join(joinit(ios,","))
# Argument
self._constructorArguments = "".join(joinit(self._templateargs,","))
# Template parameters to use when defining the template
self._templateParameters = "".join(joinit(temptypes,","))
# Template parameters to use when defining the template
self._specializedTemplateParameters = "".join(joinit(specializedtemptypes,","))
# Template parameters to use when defining the template
self._templateArguments = "".join(joinit(tempargs,","))
# Template parameters to use when defining the template
self._templateParametersForGeneric = "".join(joinit(tempgen,","))
# Datatypes for the constructors
self._datatypeForConstructor = "".join(joinit(constructortypes,","))
# Args for the generic constructor
self._genericConstructorArgs = "".join(joinit(genericconstructorargs,","))
@property
def pureNodeID(self):
return self._pureNodeID
@property
def realInputs(self):
return self._realInputs
@property
def realOutputs(self):
return self._realOutputs
@property
def constructorTypes(self):
return self._constructorTypes
@property
def constructorArguments(self):
return self._constructorArguments
@property
def templateParameters(self):
return self._templateParameters
@property
def specializedTemplateParameters(self):
return self._specializedTemplateParameters
@property
def templateArguments(self):
return self._templateArguments
@property
def templateParametersForGeneric(self):
return self._templateParametersForGeneric
@property
def datatypeForConstructor(self):
return self._datatypeForConstructor
@property
def genericConstructorArgs(self):
return self._genericConstructorArgs
@property
def nodeKind(self):
if (self._realInputs + self._realOutputs) == 2:
return "GenericNode"
else:
return "GenericNode21"
@property
@ -528,7 +685,7 @@ class GenericFunction(GenericNode):
return "Function"
# To clean
def cRun(self,ctemplate=True):
def cRun(self,ctemplate=True,codeArray=False):
if ctemplate:
theType=self._inputs[self.inputNames[0]].ctype
else:
@ -549,6 +706,8 @@ class GenericFunction(GenericNode):
argsStr=""
inArgsStr=""
outArgsStr=""
inputId=1
outputId=1
for io in self.inputNames:
ioObj = self._inputs[io]
if ioObj.constantNode:
@ -561,15 +720,23 @@ class GenericFunction(GenericNode):
ptrs.append(buf)
args.append(buf)
inargs.append(buf)
if self.realInputs == 1:
readFuncName="getReadBuffer"
else:
readFuncName="getReadBuffer%d"%inputId
# Buffer and fifo
inputs.append((buf,self.listOfargs[theId]))
inputs.append((buf,self.listOfargs[theId],readFuncName))
inputId = inputId + 1
theId = theId + 1
for io in self.outputNames:
buf = "o%d" % theId
ptrs.append(buf)
args.append(buf)
outargs.append(buf)
outputs.append((buf,self.listOfargs[theId]))
writeFuncName="getWriteBuffer"
outputs.append((buf,self.listOfargs[theId],writeFuncName))
outputId = outputId + 1
theId = theId + 1
argsStr="".join(joinit(args,","))
@ -577,15 +744,26 @@ class GenericFunction(GenericNode):
outArgsStr="".join(joinit(outargs,","))
if ctemplate:
result=Dsp.CTEMPLATE.render(func=self._nodeName,
theType = theType,
nb = theLen,
ptrs = ptrs,
args = argsStr,
inputs=inputs,
outputs=outputs,
node=self
)
if codeArray:
result=Dsp.CNODETEMPLATE.render(func=self._nodeName,
theType = theType,
nb = theLen,
ptrs = ptrs,
args = argsStr,
inputs=inputs,
outputs=outputs,
node=self
)
else:
result=Dsp.CTEMPLATE.render(func=self._nodeName,
theType = theType,
nb = theLen,
ptrs = ptrs,
args = argsStr,
inputs=inputs,
outputs=outputs,
node=self
)
else:
result=Dsp.PYTEMPLATE.render(func=self._nodeName,
theType = theType,
@ -600,6 +778,10 @@ class GenericFunction(GenericNode):
)
return(result)
def codeArrayRun(self):
return(self.cRun(codeArray=True))
class Unary(GenericFunction):
def __init__(self,funcname,theType,length):
GenericFunction.__init__(self,funcname,theType,length)
@ -607,6 +789,7 @@ class Unary(GenericFunction):
self.addInput("i",theType,length)
self.addOutput("o",theType,length)
class Binary(GenericFunction):
def __init__(self,funcname,theType,length):
GenericFunction.__init__(self,funcname,theType,length)
@ -617,6 +800,9 @@ class Binary(GenericFunction):
self.addOutput("o",theType,length)
BINARYOP=["scale","add","and","mult","not","or","sub","xor","cmplx_mult_cmplx","cmplx_mult_real"
]
@ -629,14 +815,18 @@ class Dsp(GenericFunction):
cmsisname = "arm_%s_%s" % (name,theType.dspExtension)
GenericFunction.__init__(self, cmsisname,theType,length)
self._binary=True
if name in BINARYOP:
self.addInput("ia",theType,length)
self.addInput("ib",theType,length)
self._binary=True
else:
self.addInput("i",theType,length)
self._binary=False
self.addOutput("o",theType,length)
@property

@ -124,8 +124,14 @@ class FIFO: public FIFOBase<T>
// GENERIC NODES
class NodeBase
{
public:
virtual int run()=0;
};
template<typename IN, int inputSize,typename OUT, int outputSize>
class GenericNode
class GenericNode:public NodeBase
{
public:
GenericNode(FIFOBase<IN> &src,FIFOBase<OUT> &dst):mSrc(src),mDst(dst){};
@ -140,7 +146,7 @@ private:
};
template<typename IN, int inputSize,typename OUT1, int output1Size,typename OUT2, int output2Size>
class GenericNode12
class GenericNode12:public NodeBase
{
public:
GenericNode12(FIFOBase<IN> &src,FIFOBase<OUT1> &dst1,FIFOBase<OUT2> &dst2):mSrc(src),
@ -158,7 +164,7 @@ private:
};
template<typename IN1, int input1Size,typename IN2, int input2Size,typename OUT, int outputSize>
class GenericNode21
class GenericNode21:public NodeBase
{
public:
GenericNode21(FIFOBase<IN1> &src1,FIFOBase<IN2> &src2,FIFOBase<OUT> &dst):mSrc1(src1),
@ -179,7 +185,7 @@ private:
template<typename OUT, int outputSize>
class GenericSource
class GenericSource:public NodeBase
{
public:
GenericSource(FIFOBase<OUT> &dst):mDst(dst){};
@ -192,7 +198,7 @@ private:
};
template<typename IN,int inputSize>
class GenericSink
class GenericSink:public NodeBase
{
public:
GenericSink(FIFOBase<IN> &src):mSrc(src){};

@ -0,0 +1,13 @@
{% for ptr in ptrs %}
{{theType}}* {{ptr}};
{% endfor %}
{% for ptr in inputs %}
{{ptr[0]}}=this->{{ptr[2]}}();
{% endfor %}
{% for ptr in outputs %}
{{ptr[0]}}=this->{{ptr[2]}}();
{% endfor %}
{{func}}({{args}},{{nb}});
return(0);

@ -64,13 +64,14 @@ uint32_t {{config.schedName}}(int *error{{optionalargs()}})
{% endif %}
{% endfor %}
/* Run several schedule iterations */
{% if config.debug %}
while((sdfError==0) && (debugCounter > 0))
{% else %}
while(sdfError==0)
{% endif %}
{
/* Run a schedule iteration */
{% for s in schedule %}
{{nodes[s].cRun()}}
CHECKERROR;

@ -0,0 +1,133 @@
/*
Generated with CMSIS-DSP SDF Scripts.
The generated code is not covered by CMSIS-DSP license.
The support classes and code is covered by CMSIS-DSP license.
*/
{% if config.dumpFIFO %}
#define DEBUGSCHED 1
{% endif %}
#include "arm_math.h"
#include "custom.h"
#include "GenericNodes.h"
#include "AppNodes.h"
#include "scheduler.h"
{% macro optionalargs() -%}
{% if config.cOptionalArgs %},{{config.cOptionalArgs}}{% endif %}
{% endmacro -%}
/* List of nodes */
static NodeBase *nodeArray[{{nbNodes}}]={0};
/*
Description of the scheduling. It is a list of nodes to call.
The values are indexes in the previous array.
*/
static unsigned int schedule[{{schedLen}}]=
{
{{schedDescription}}
};
/***********
FIFO buffers
************/
{% for fifo in fifos %}
#define FIFOSIZE{{fifo.fifoID}} {{fifo.length}}
{% endfor %}
{% for buf in sched._graph._allBuffers %}
#define BUFFERSIZE{{buf._bufferID}} {{buf._length}}
{{buf._theType.ctype}} {{config.prefix}}buf{{buf._bufferID}}[BUFFERSIZE{{buf._bufferID}}]={0};
{% endfor %}
/**************
Classes created for pure function calls (like some CMSIS-DSP functions)
***************/
{% for p in pureNodes %}
{% set node = pureNodes[p] %}
template<{{node.templateParameters}}> class Func{{node.pureClassID}};
template<{{node.specializedTemplateParameters}}>
class Func{{node.pureClassID}}<{{node.templateArguments}}>: public {{node.nodeKind}}<{{node.templateParametersForGeneric}}>
{
public:
Func{{node.pureClassID}}({{node.datatypeForConstructor}}):
{{node.nodeKind}}<{{node.templateParametersForGeneric}}>({{node.genericConstructorArgs}}){};
int run(){
{{node.codeArrayRun()}}
};
};
{% endfor %}
uint32_t {{config.schedName}}(int *error{{optionalargs()}})
{
int sdfError=0;
uint32_t nbSchedule=0;
{% if config.debug %}
int32_t debugCounter={{config.debugLimit}};
{% endif %}
/*
Create FIFOs objects
*/
{% for id in range(nbFifos) %}
{% if fifos[id].hasDelay %}
FIFO<{{fifos[id].theType.ctype}},FIFOSIZE{{id}},{{fifos[id].isArrayAsInt}}> fifo{{id}}({{config.prefix}}buf{{fifos[id].buffer._bufferID}},{{fifos[id].delay}});
{% else %}
FIFO<{{fifos[id].theType.ctype}},FIFOSIZE{{id}},{{fifos[id].isArrayAsInt}}> fifo{{id}}({{config.prefix}}buf{{fifos[id].buffer._bufferID}});
{% endif %}
{% endfor %}
/*
Create node objects
*/
{% for node in nodes %}
{% if node.hasState %}
{{node.typeName}}<{{node.ioTemplate()}}> {{node.nodeName}}({{node.args}});
nodeArray[{{node.codeID}}]=(NodeBase*)&{{node.nodeName}};
{% else %}
Func{{node.pureClassID}}<{{node.constructorTypes}}> func{{node.pureNodeID}}({{node.constructorArguments}});
nodeArray[{{node.codeID}}]=(NodeBase*)&func{{node.pureNodeID}};
{% endif %}
{% endfor %}
/* Run several schedule iterations */
{% if config.debug %}
while((sdfError==0) && (debugCounter > 0))
{% else %}
while(sdfError==0)
{% endif %}
{
/* Run a schedule iteration */
for(unsigned long id=0 ; id < {{schedLen}}; id++)
{
unsigned int nodeId = schedule[id];
sdfError = nodeArray[nodeId]->run();
CHECKERROR;
}
{% if config.debug %}
debugCounter--;
{% endif %}
nbSchedule++;
}
*error=sdfError;
return(nbSchedule);
}
Loading…
Cancel
Save