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: 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) - `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. - `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) - `schedName` : The name of the scheduler function (`scheduler` by default)
- cOptionalArgs and pyOptionalArgs for passing additional arguments to the scheduling function - `cOptionalArgs` and pyOptionalArgs for passing additional arguments to the scheduling function
- prefix to prefix the same of the global buffers - `prefix` to prefix the same of the global buffers
- memoryOptimization : Experimental. It is attempting to reuse buffer memory and share it between several FIFOs - `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 - `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: 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); Sink<float32_t,5> sink(fifo1);
Source<float32_t,5> source(fifo0); Source<float32_t,5> source(fifo0);
/* Run several schedule iterations */
while((sdfError==0) && (debugCounter > 0)) while((sdfError==0) && (debugCounter > 0))
{ {
/* Run a schedule iteration */
sdfError = source.run(); sdfError = source.run();
CHECKERROR; CHECKERROR;
sdfError = source.run(); sdfError = source.run();

@ -68,7 +68,7 @@ print("Schedule length = %d" % sched.scheduleLength)
print("Memory usage %d bytes" % sched.memory) print("Memory usage %d bytes" % sched.memory)
# #
#conf.codeArray=True
sched.ccode("generated",conf) sched.ccode("generated",conf)
with open("test.dot","w") as f: 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); StereoSource<float32_t,320> src(fifo0);
Unzip<float32_t,320,float32_t,160,float32_t,160> toMono(fifo0,fifo1,fifo2); Unzip<float32_t,320,float32_t,160,float32_t,160> toMono(fifo0,fifo1,fifo2);
/* Run several schedule iterations */
while((sdfError==0) && (debugCounter > 0)) while((sdfError==0) && (debugCounter > 0))
{ {
/* Run a schedule iteration */
sdfError = src.run(); sdfError = src.run();
CHECKERROR; CHECKERROR;
sdfError = toMono.run(); sdfError = toMono.run();

@ -104,6 +104,7 @@ print("Schedule length = %d" % sched.scheduleLength)
print("Memory usage %d bytes" % sched.memory) print("Memory usage %d bytes" % sched.memory)
# #
#conf.codeArray=True
sched.ccode("generated",conf) sched.ccode("generated",conf)
with open("test.dot","w") as f: 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); ToComplex<float32_t,256,float32_t,512> toCmplx(fifo2,fifo3);
ToReal<float32_t,512,float32_t,256> toReal(fifo5,fifo6); ToReal<float32_t,512,float32_t,256> toReal(fifo5,fifo6);
/* Run several schedule iterations */
while((sdfError==0) && (debugCounter > 0)) while((sdfError==0) && (debugCounter > 0))
{ {
/* Run a schedule iteration */
sdfError = src.run(); sdfError = src.run();
CHECKERROR; CHECKERROR;
sdfError = audioWin.run(); sdfError = audioWin.run();

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

@ -51,7 +51,7 @@ buf7=np.zeros(FIFOSIZE7,dtype=np.float32)
def scheduler(dispbuf): def scheduler(dispbuf):
sdfError=0 sdfError=0
nbSchedule=0 nbSchedule=0
debugCounter=42; debugCounter=42
# #
# Create FIFOs objects # Create FIFOs objects
@ -68,14 +68,14 @@ def scheduler(dispbuf):
# #
# Create node objects # Create node objects
# #
audioOverlap = OverlapAdd(256,128,fifo6,fifo7); audioOverlap = OverlapAdd(256,128,fifo6,fifo7)
audioWin = SlidingBuffer(256,128,fifo0,fifo1); audioWin = SlidingBuffer(256,128,fifo0,fifo1)
cfft = CFFT(512,512,fifo3,fifo4); cfft = CFFT(512,512,fifo3,fifo4)
icfft = ICFFT(512,512,fifo4,fifo5); icfft = ICFFT(512,512,fifo4,fifo5)
sink = FileSink(192,fifo7,"output_example3.txt",dispbuf); sink = FileSink(192,fifo7,"output_example3.txt",dispbuf)
src = FileSource(192,fifo0,"input_example3.txt"); src = FileSource(192,fifo0,"input_example3.txt")
toCmplx = ToComplex(256,512,fifo2,fifo3); toCmplx = ToComplex(256,512,fifo2,fifo3)
toReal = ToReal(512,256,fifo5,fifo6); toReal = ToReal(512,256,fifo5,fifo6)
while((sdfError==0) and (debugCounter > 0)): while((sdfError==0) and (debugCounter > 0)):
nbSchedule = nbSchedule + 1 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"); FileSink<float32_t,13> sink(fifo3,"output_example6.txt");
FileSource<float32_t,192> src(fifo0,"input_example6.txt"); FileSource<float32_t,192> src(fifo0,"input_example6.txt");
/* Run several schedule iterations */
while((sdfError==0) && (debugCounter > 0)) while((sdfError==0) && (debugCounter > 0))
{ {
/* Run a schedule iteration */
sdfError = src.run(); sdfError = src.run();
CHECKERROR; CHECKERROR;
sdfError = audioWin.run(); sdfError = audioWin.run();

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

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

@ -32,7 +32,7 @@
template<typename OUT,int outputSize> template<typename OUT,int outputSize>
class AudioSource: GenericSource<OUT,outputSize> class AudioSource: public GenericSource<OUT,outputSize>
{ {
public: public:
AudioSource(FIFOBase<OUT> &dst,ring_config_t *config): 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> class StereoToMono<q15_t,inputSize,q15_t,outputSize>: public GenericNode<q15_t,inputSize,q15_t,outputSize>
{ {
public: 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){}; GenericNode<q15_t,inputSize,q15_t,outputSize>(src,dst){};

@ -37,7 +37,7 @@ file is reached.
*/ */
template<int outputSize> template<int outputSize>
class FileSource<float32_t,outputSize>: GenericSource<float32_t,outputSize> class FileSource<float32_t,outputSize>: public GenericSource<float32_t,outputSize>
{ {
public: public:
FileSource(FIFOBase<float32_t> &dst,std::string name):GenericSource<float32_t,outputSize>(dst), 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 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") htemplate = env.get_template("code.h")
@ -48,10 +60,14 @@ def gencode(sched,directory,config):
with open(cfile,"w") as f: with open(cfile,"w") as f:
print(ctemplate.render(fifos=sched._graph._allFIFOs, print(ctemplate.render(fifos=sched._graph._allFIFOs,
nbFifos=nbFifos, nbFifos=nbFifos,
nbNodes=len(sched.nodes),
nodes=sched.nodes, nodes=sched.nodes,
pureNodes=sched.pureNodes,
schedule=sched.schedule, schedule=sched.schedule,
schedLen=len(sched.schedule),
config=config, config=config,
sched=sched sched=sched,
schedDescription=schedDescription
),file=f) ),file=f)
with open(hfile,"w") as f: with open(hfile,"w") as f:

@ -60,6 +60,11 @@ class Configuration:
# Path to SDF module for Python simu # Path to SDF module for Python simu
self.pathToSDFModule="../.." 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 @property
def debug(self): def debug(self):
return (self.debugLimit > 0) return (self.debugLimit > 0)

@ -541,29 +541,67 @@ class Schedule:
self._sortedEdges=sortedEdges self._sortedEdges=sortedEdges
self._schedule = schedule self._schedule = schedule
self._graph = g 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: for n in self.nodes:
n.codeID = nodeCodeID
nodeCodeID = nodeCodeID + 1
# Constant nodes are ignored since they have # Constant nodes are ignored since they have
# no arcs, and are connected to no FIFOs # no arcs, and are connected to no FIFOs
theArgs=[] theArgs=[]
theArgTypes=[]
i,o=n.allIOs() i,o=n.allIOs()
for io in i: for io in i:
# An io connected to a constant node has no fifo # An io connected to a constant node has no fifo
if not io.fifo is None: if not io.fifo is None:
theArgs.append(self.fifoID(io.fifo)) theArgs.append(self.fifoID(io.fifo))
theArgTypes.append(io.ctype)
else: else:
# Instead the arg is the name of a constant node # Instead the arg is the name of a constant node
# instead of being a fifo ID # instead of being a fifo ID
theArgs.append(io.constantNode.name) theArgs.append(io.constantNode.name)
theArgTypes.append(io.constantNode.name)
for io in o: for io in o:
theArgs.append(self.fifoID(io.fifo)) theArgs.append(self.fifoID(io.fifo))
theArgTypes.append(io.ctype)
n.args=theArgs 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): def hasDelay(self,edge):
return(self._graph.hasDelay(edge)) return(self._graph.hasDelay(edge))
def getDelay(self,edge): def getDelay(self,edge):
return(self._graph.getDelay(edge)) return(self._graph.getDelay(edge))
@property
def pureNodes(self):
return self._pureNodes
@property @property
def constantEdges(self): def constantEdges(self):
return self._graph.constantEdges return self._graph.constantEdges

@ -28,6 +28,10 @@
"""Description of the basic types used to build a dataflow graph""" """Description of the basic types used to build a dataflow graph"""
from jinja2 import Environment, PackageLoader, select_autoescape from jinja2 import Environment, PackageLoader, select_autoescape
class NoFunctionArrayInPython(Exception):
pass
def camelCase(st): def camelCase(st):
output = ''.join(x for x in st.title() if x.isalnum()) output = ''.join(x for x in st.title() if x.isalnum())
return output[0].lower() + output[1:] return output[0].lower() + output[1:]
@ -254,6 +258,10 @@ class BaseNode:
return(ins,outs) return(ins,outs)
def ioTemplate(self): def ioTemplate(self):
"""Template arguments for C """Template arguments for C
input type, input size ... input type, input size ...
@ -303,6 +311,16 @@ class BaseNode:
return ("sdfError = %s.run();" % self.nodeName) return ("sdfError = %s.run();" % self.nodeName)
else: else:
return ("sdfError = %s.run()" % self.nodeName) 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 @property
@ -310,11 +328,13 @@ class BaseNode:
"""List of fifos args for object initialization""" """List of fifos args for object initialization"""
return self._args return self._args
@property @property
def args(self): def args(self):
"""String of fifo args for object initialization """String of fifo args for object initialization
with literal argument and variable arguments""" with literal argument and variable arguments"""
allArgs=self.listOfargs allArgs=self.listOfargs
# Add specific argrs after FIFOs
if self.schedArgs: if self.schedArgs:
for lit in self.schedArgs: for lit in self.schedArgs:
allArgs.append(lit.arg) allArgs.append(lit.arg)
@ -323,15 +343,23 @@ class BaseNode:
@args.setter @args.setter
def args(self,fifoIDs): def args(self,fifoIDs):
res=[] 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: for x in fifoIDs:
# If args is a FIFO we generate a name using fifo ids # If args is a FIFO we generate a name using fifo ids
if isinstance(x,int): if isinstance(x,int):
res.append("fifo%d" % x) res.append("fifo%d" % x)
templateargs.append("fifo%d" % x)
# If args is a constant node, we just use the constant node name # If args is a constant node, we just use the constant node name
# (Defined in C code) # (Defined in C code)
else: else:
res.append(x) res.append(x)
self._args=res self._args=res
self._templateargs=templateargs
# For graphviz generation # For graphviz generation
@ -491,9 +519,10 @@ class GenericFunction(GenericNode):
# Used to generate unique ID and names when # Used to generate unique ID and names when
# unique names are required # unique names are required
# like for creating the graph where each call to # 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 # separate node
NODEID={} NODEID={}
PUREID=1
ENV = Environment( ENV = Environment(
loader=PackageLoader("sdf"), loader=PackageLoader("sdf"),
@ -503,19 +532,147 @@ class GenericFunction(GenericNode):
) )
CTEMPLATE = ENV.get_template("cmsis.cpp") CTEMPLATE = ENV.get_template("cmsis.cpp")
CNODETEMPLATE = ENV.get_template("cmsisNode.cpp")
PYTEMPLATE = ENV.get_template("cmsis.py") PYTEMPLATE = ENV.get_template("cmsis.py")
def __init__(self,funcname,theType,length): def __init__(self,funcname,theType,length):
if not (funcname in Dsp.NODEID): if not (funcname in GenericFunction.NODEID):
Dsp.NODEID[funcname]=1 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._hasState = False
self._length = length self._length = length
self._nodeName = funcname 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 @property
@ -528,7 +685,7 @@ class GenericFunction(GenericNode):
return "Function" return "Function"
# To clean # To clean
def cRun(self,ctemplate=True): def cRun(self,ctemplate=True,codeArray=False):
if ctemplate: if ctemplate:
theType=self._inputs[self.inputNames[0]].ctype theType=self._inputs[self.inputNames[0]].ctype
else: else:
@ -549,6 +706,8 @@ class GenericFunction(GenericNode):
argsStr="" argsStr=""
inArgsStr="" inArgsStr=""
outArgsStr="" outArgsStr=""
inputId=1
outputId=1
for io in self.inputNames: for io in self.inputNames:
ioObj = self._inputs[io] ioObj = self._inputs[io]
if ioObj.constantNode: if ioObj.constantNode:
@ -561,15 +720,23 @@ class GenericFunction(GenericNode):
ptrs.append(buf) ptrs.append(buf)
args.append(buf) args.append(buf)
inargs.append(buf) inargs.append(buf)
if self.realInputs == 1:
readFuncName="getReadBuffer"
else:
readFuncName="getReadBuffer%d"%inputId
# Buffer and fifo # Buffer and fifo
inputs.append((buf,self.listOfargs[theId])) inputs.append((buf,self.listOfargs[theId],readFuncName))
inputId = inputId + 1
theId = theId + 1 theId = theId + 1
for io in self.outputNames: for io in self.outputNames:
buf = "o%d" % theId buf = "o%d" % theId
ptrs.append(buf) ptrs.append(buf)
args.append(buf) args.append(buf)
outargs.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 theId = theId + 1
argsStr="".join(joinit(args,",")) argsStr="".join(joinit(args,","))
@ -577,15 +744,26 @@ class GenericFunction(GenericNode):
outArgsStr="".join(joinit(outargs,",")) outArgsStr="".join(joinit(outargs,","))
if ctemplate: if ctemplate:
result=Dsp.CTEMPLATE.render(func=self._nodeName, if codeArray:
theType = theType, result=Dsp.CNODETEMPLATE.render(func=self._nodeName,
nb = theLen, theType = theType,
ptrs = ptrs, nb = theLen,
args = argsStr, ptrs = ptrs,
inputs=inputs, args = argsStr,
outputs=outputs, inputs=inputs,
node=self 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: else:
result=Dsp.PYTEMPLATE.render(func=self._nodeName, result=Dsp.PYTEMPLATE.render(func=self._nodeName,
theType = theType, theType = theType,
@ -600,6 +778,10 @@ class GenericFunction(GenericNode):
) )
return(result) return(result)
def codeArrayRun(self):
return(self.cRun(codeArray=True))
class Unary(GenericFunction): class Unary(GenericFunction):
def __init__(self,funcname,theType,length): def __init__(self,funcname,theType,length):
GenericFunction.__init__(self,funcname,theType,length) GenericFunction.__init__(self,funcname,theType,length)
@ -607,6 +789,7 @@ class Unary(GenericFunction):
self.addInput("i",theType,length) self.addInput("i",theType,length)
self.addOutput("o",theType,length) self.addOutput("o",theType,length)
class Binary(GenericFunction): class Binary(GenericFunction):
def __init__(self,funcname,theType,length): def __init__(self,funcname,theType,length):
GenericFunction.__init__(self,funcname,theType,length) GenericFunction.__init__(self,funcname,theType,length)
@ -617,6 +800,9 @@ class Binary(GenericFunction):
self.addOutput("o",theType,length) self.addOutput("o",theType,length)
BINARYOP=["scale","add","and","mult","not","or","sub","xor","cmplx_mult_cmplx","cmplx_mult_real" 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) cmsisname = "arm_%s_%s" % (name,theType.dspExtension)
GenericFunction.__init__(self, cmsisname,theType,length) GenericFunction.__init__(self, cmsisname,theType,length)
self._binary=True
if name in BINARYOP: if name in BINARYOP:
self.addInput("ia",theType,length) self.addInput("ia",theType,length)
self.addInput("ib",theType,length) self.addInput("ib",theType,length)
self._binary=True
else: else:
self.addInput("i",theType,length) self.addInput("i",theType,length)
self._binary=False
self.addOutput("o",theType,length) self.addOutput("o",theType,length)
@property @property

@ -124,8 +124,14 @@ class FIFO: public FIFOBase<T>
// GENERIC NODES // GENERIC NODES
class NodeBase
{
public:
virtual int run()=0;
};
template<typename IN, int inputSize,typename OUT, int outputSize> template<typename IN, int inputSize,typename OUT, int outputSize>
class GenericNode class GenericNode:public NodeBase
{ {
public: public:
GenericNode(FIFOBase<IN> &src,FIFOBase<OUT> &dst):mSrc(src),mDst(dst){}; 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> template<typename IN, int inputSize,typename OUT1, int output1Size,typename OUT2, int output2Size>
class GenericNode12 class GenericNode12:public NodeBase
{ {
public: public:
GenericNode12(FIFOBase<IN> &src,FIFOBase<OUT1> &dst1,FIFOBase<OUT2> &dst2):mSrc(src), 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> template<typename IN1, int input1Size,typename IN2, int input2Size,typename OUT, int outputSize>
class GenericNode21 class GenericNode21:public NodeBase
{ {
public: public:
GenericNode21(FIFOBase<IN1> &src1,FIFOBase<IN2> &src2,FIFOBase<OUT> &dst):mSrc1(src1), GenericNode21(FIFOBase<IN1> &src1,FIFOBase<IN2> &src2,FIFOBase<OUT> &dst):mSrc1(src1),
@ -179,7 +185,7 @@ private:
template<typename OUT, int outputSize> template<typename OUT, int outputSize>
class GenericSource class GenericSource:public NodeBase
{ {
public: public:
GenericSource(FIFOBase<OUT> &dst):mDst(dst){}; GenericSource(FIFOBase<OUT> &dst):mDst(dst){};
@ -192,7 +198,7 @@ private:
}; };
template<typename IN,int inputSize> template<typename IN,int inputSize>
class GenericSink class GenericSink:public NodeBase
{ {
public: public:
GenericSink(FIFOBase<IN> &src):mSrc(src){}; 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 %} {% endif %}
{% endfor %} {% endfor %}
/* Run several schedule iterations */
{% if config.debug %} {% if config.debug %}
while((sdfError==0) && (debugCounter > 0)) while((sdfError==0) && (debugCounter > 0))
{% else %} {% else %}
while(sdfError==0) while(sdfError==0)
{% endif %} {% endif %}
{ {
/* Run a schedule iteration */
{% for s in schedule %} {% for s in schedule %}
{{nodes[s].cRun()}} {{nodes[s].cRun()}}
CHECKERROR; 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