diff --git a/SDFTools/documentation/example1.md b/SDFTools/documentation/example1.md index a38a661a..43aa2ac8 100755 --- a/SDFTools/documentation/example1.md +++ b/SDFTools/documentation/example1.md @@ -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: diff --git a/SDFTools/examples/example1/generated/scheduler.cpp b/SDFTools/examples/example1/generated/scheduler.cpp index ca97fac3..9229f19f 100755 --- a/SDFTools/examples/example1/generated/scheduler.cpp +++ b/SDFTools/examples/example1/generated/scheduler.cpp @@ -48,9 +48,10 @@ uint32_t scheduler(int *error,int someVariable) Sink sink(fifo1); Source source(fifo0); + /* Run several schedule iterations */ while((sdfError==0) && (debugCounter > 0)) { - + /* Run a schedule iteration */ sdfError = source.run(); CHECKERROR; sdfError = source.run(); diff --git a/SDFTools/examples/example1/graph.py b/SDFTools/examples/example1/graph.py index ef09d147..140d8bca 100755 --- a/SDFTools/examples/example1/graph.py +++ b/SDFTools/examples/example1/graph.py @@ -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: diff --git a/SDFTools/examples/example2/generated/scheduler.cpp b/SDFTools/examples/example2/generated/scheduler.cpp index 59f02b14..9ca8320c 100755 --- a/SDFTools/examples/example2/generated/scheduler.cpp +++ b/SDFTools/examples/example2/generated/scheduler.cpp @@ -86,9 +86,10 @@ uint32_t scheduler(int *error,int opt1,int opt2) StereoSource src(fifo0); Unzip toMono(fifo0,fifo1,fifo2); + /* Run several schedule iterations */ while((sdfError==0) && (debugCounter > 0)) { - + /* Run a schedule iteration */ sdfError = src.run(); CHECKERROR; sdfError = toMono.run(); diff --git a/SDFTools/examples/example2/graph.py b/SDFTools/examples/example2/graph.py index ca375007..04a9eca0 100755 --- a/SDFTools/examples/example2/graph.py +++ b/SDFTools/examples/example2/graph.py @@ -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: diff --git a/SDFTools/examples/example3/generated/scheduler.cpp b/SDFTools/examples/example3/generated/scheduler.cpp index 03a9a543..3bb96fe0 100755 --- a/SDFTools/examples/example3/generated/scheduler.cpp +++ b/SDFTools/examples/example3/generated/scheduler.cpp @@ -83,9 +83,10 @@ uint32_t scheduler(int *error) ToComplex toCmplx(fifo2,fifo3); ToReal toReal(fifo5,fifo6); + /* Run several schedule iterations */ while((sdfError==0) && (debugCounter > 0)) { - + /* Run a schedule iteration */ sdfError = src.run(); CHECKERROR; sdfError = audioWin.run(); diff --git a/SDFTools/examples/example3/graph.py b/SDFTools/examples/example3/graph.py index b28fe9ee..aef9a23e 100755 --- a/SDFTools/examples/example3/graph.py +++ b/SDFTools/examples/example3/graph.py @@ -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: diff --git a/SDFTools/examples/example4/sched.py b/SDFTools/examples/example4/sched.py index ce536e8b..93e4b724 100755 --- a/SDFTools/examples/example4/sched.py +++ b/SDFTools/examples/example4/sched.py @@ -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 diff --git a/SDFTools/examples/example6/generated/scheduler.cpp b/SDFTools/examples/example6/generated/scheduler.cpp index 8edc781d..137d3514 100755 --- a/SDFTools/examples/example6/generated/scheduler.cpp +++ b/SDFTools/examples/example6/generated/scheduler.cpp @@ -60,9 +60,10 @@ uint32_t scheduler(int *error,arm_mfcc_instance_f32 *mfccConfig) FileSink sink(fifo3,"output_example6.txt"); FileSource 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(); diff --git a/SDFTools/examples/example6/graph.py b/SDFTools/examples/example6/graph.py index a7972885..3ffe583b 100755 --- a/SDFTools/examples/example6/graph.py +++ b/SDFTools/examples/example6/graph.py @@ -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: diff --git a/SDFTools/examples/example6/sharedconfig.py b/SDFTools/examples/example6/sharedconfig.py index 1b39be27..37fdce2c 100755 --- a/SDFTools/examples/example6/sharedconfig.py +++ b/SDFTools/examples/example6/sharedconfig.py @@ -22,4 +22,4 @@ nbMFCCOutputs = int(np.floor(sample_rate / (FFTSize >> 1))) if nbMFCCOutputs%2 == 1: nbMFCCOutputs = nbMFCCOutputs + 1 -print(nbMFCCOutputs) \ No newline at end of file +print("nbMFCCOutputs = %d " % nbMFCCOutputs) \ No newline at end of file diff --git a/SDFTools/sdf/nodes/cpp/AudioSource.h b/SDFTools/sdf/nodes/cpp/AudioSource.h index e61b3747..08b92284 100755 --- a/SDFTools/sdf/nodes/cpp/AudioSource.h +++ b/SDFTools/sdf/nodes/cpp/AudioSource.h @@ -32,7 +32,7 @@ template -class AudioSource: GenericSource +class AudioSource: public GenericSource { public: AudioSource(FIFOBase &dst,ring_config_t *config): diff --git a/SDFTools/sdf/nodes/cpp/StereoToMono.h b/SDFTools/sdf/nodes/cpp/StereoToMono.h index e885b79a..e85aa54a 100755 --- a/SDFTools/sdf/nodes/cpp/StereoToMono.h +++ b/SDFTools/sdf/nodes/cpp/StereoToMono.h @@ -37,7 +37,7 @@ template class StereoToMono: public GenericNode { public: - StereoToMonoQ15(FIFOBase &src,FIFOBase &dst): + StereoToMono(FIFOBase &src,FIFOBase &dst): GenericNode(src,dst){}; diff --git a/SDFTools/sdf/nodes/cpp/host/FileSource.h b/SDFTools/sdf/nodes/cpp/host/FileSource.h index 3a5a687a..b275ee2d 100755 --- a/SDFTools/sdf/nodes/cpp/host/FileSource.h +++ b/SDFTools/sdf/nodes/cpp/host/FileSource.h @@ -37,7 +37,7 @@ file is reached. */ template -class FileSource: GenericSource +class FileSource: public GenericSource { public: FileSource(FIFOBase &dst,std::string name):GenericSource(dst), diff --git a/SDFTools/sdf/schedule/ccode.py b/SDFTools/sdf/schedule/ccode.py index d263bb5e..2f1964e6 100755 --- a/SDFTools/sdf/schedule/ccode.py +++ b/SDFTools/sdf/schedule/ccode.py @@ -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: diff --git a/SDFTools/sdf/schedule/config.py b/SDFTools/sdf/schedule/config.py index 52aff4df..e34c0437 100755 --- a/SDFTools/sdf/schedule/config.py +++ b/SDFTools/sdf/schedule/config.py @@ -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) diff --git a/SDFTools/sdf/schedule/description.py b/SDFTools/sdf/schedule/description.py index e96e9d35..5f78f982 100755 --- a/SDFTools/sdf/schedule/description.py +++ b/SDFTools/sdf/schedule/description.py @@ -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 diff --git a/SDFTools/sdf/schedule/node.py b/SDFTools/sdf/schedule/node.py index 29fb54dc..ad0808bb 100755 --- a/SDFTools/sdf/schedule/node.py +++ b/SDFTools/sdf/schedule/node.py @@ -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 diff --git a/SDFTools/sdf/src/GenericNodes.h b/SDFTools/sdf/src/GenericNodes.h index 88667378..886f9e43 100755 --- a/SDFTools/sdf/src/GenericNodes.h +++ b/SDFTools/sdf/src/GenericNodes.h @@ -124,8 +124,14 @@ class FIFO: public FIFOBase // GENERIC NODES +class NodeBase +{ +public: + virtual int run()=0; +}; + template -class GenericNode +class GenericNode:public NodeBase { public: GenericNode(FIFOBase &src,FIFOBase &dst):mSrc(src),mDst(dst){}; @@ -140,7 +146,7 @@ private: }; template -class GenericNode12 +class GenericNode12:public NodeBase { public: GenericNode12(FIFOBase &src,FIFOBase &dst1,FIFOBase &dst2):mSrc(src), @@ -158,7 +164,7 @@ private: }; template -class GenericNode21 +class GenericNode21:public NodeBase { public: GenericNode21(FIFOBase &src1,FIFOBase &src2,FIFOBase &dst):mSrc1(src1), @@ -179,7 +185,7 @@ private: template -class GenericSource +class GenericSource:public NodeBase { public: GenericSource(FIFOBase &dst):mDst(dst){}; @@ -192,7 +198,7 @@ private: }; template -class GenericSink +class GenericSink:public NodeBase { public: GenericSink(FIFOBase &src):mSrc(src){}; diff --git a/SDFTools/sdf/templates/cmsisNode.cpp b/SDFTools/sdf/templates/cmsisNode.cpp new file mode 100755 index 00000000..c8650459 --- /dev/null +++ b/SDFTools/sdf/templates/cmsisNode.cpp @@ -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); + \ No newline at end of file diff --git a/SDFTools/sdf/templates/code.cpp b/SDFTools/sdf/templates/code.cpp index e5c1208b..299b814c 100755 --- a/SDFTools/sdf/templates/code.cpp +++ b/SDFTools/sdf/templates/code.cpp @@ -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; diff --git a/SDFTools/sdf/templates/codeArray.cpp b/SDFTools/sdf/templates/codeArray.cpp new file mode 100755 index 00000000..5c1255c0 --- /dev/null +++ b/SDFTools/sdf/templates/codeArray.cpp @@ -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); +} \ No newline at end of file