diff --git a/SDFTools/examples/example8/AppNodes.h b/SDFTools/examples/example8/AppNodes.h index a5f4c6af..f1ca45fa 100644 --- a/SDFTools/examples/example8/AppNodes.h +++ b/SDFTools/examples/example8/AppNodes.h @@ -74,17 +74,29 @@ public: }; -template -class ProcessingNode: public GenericNode +template +class ProcessingNode: public GenericNode12 { public: - ProcessingNode(FIFOBase &src,FIFOBase &dst,int,const char*,int):GenericNode(src,dst){}; + ProcessingNode(FIFOBase &src, + FIFOBase &dst1, + FIFOBase &dst2, + int,const char*,int): + GenericNode12(src,dst1,dst2){}; int run(){ printf("ProcessingNode\n"); IN *a=this->getReadBuffer(); - OUT *b=this->getWriteBuffer(); - b[0] =(OUT)a[3]; + OUT1 *b=this->getWriteBuffer1(); + OUT2 *c=this->getWriteBuffer2(); + b[0] =(OUT1)a[3]; + c[0] =(OUT2)a[3]; return(0); }; diff --git a/SDFTools/examples/example8/appnodes.py b/SDFTools/examples/example8/appnodes.py index df61f266..a020d69e 100644 --- a/SDFTools/examples/example8/appnodes.py +++ b/SDFTools/examples/example8/appnodes.py @@ -44,14 +44,15 @@ class Sink(GenericSink): print("%f + I %f" % (c.re,c.im)) return(0) -class ProcessingNode(GenericNode): - def __init__(self,inputSize,outputSize,fifoin,fifoout,i,s,v): - GenericNode.__init__(self,inputSize,outputSize,fifoin,fifoout) +class ProcessingNode(GenericNode12): + def __init__(self,inputSize,outputSize1,outputSize2,fifoin,fifoout1,fifoout2,i,s,v): + GenericNode12.__init__(self,inputSize,outputSize1,outputSize2,fifoin,fifoout1,fifoout2) def run(self): print("ProcessingNode"); a=self.getReadBuffer() - b=self.getWriteBuffer() + b=self.getWriteBuffer1() + c=self.getWriteBuffer2() # Python objects have reference semantic and not # value semantic. # So in a write buffer, we can change the @@ -59,6 +60,7 @@ class ProcessingNode(GenericNode): # replace the object and risk creating sharing # Duplicating the a object may be ok b[0]=a[3] + c[0]=a[3] return(0) class Source(GenericSource): diff --git a/SDFTools/examples/example8/generated/scheduler.cpp b/SDFTools/examples/example8/generated/scheduler.cpp index 3b320e24..0a75cf66 100644 --- a/SDFTools/examples/example8/generated/scheduler.cpp +++ b/SDFTools/examples/example8/generated/scheduler.cpp @@ -24,6 +24,7 @@ FIFO buffers #define FIFOSIZE2 5 #define FIFOSIZE3 5 #define FIFOSIZE4 5 +#define FIFOSIZE5 5 #define BUFFERSIZE0 11 complex buf0[BUFFERSIZE0]={0}; @@ -40,6 +41,9 @@ complex buf3[BUFFERSIZE3]={0}; #define BUFFERSIZE4 5 complex buf4[BUFFERSIZE4]={0}; +#define BUFFERSIZE5 5 +complex buf5[BUFFERSIZE5]={0}; + uint32_t scheduler(int *error,int someVariable) { @@ -55,15 +59,17 @@ uint32_t scheduler(int *error,int someVariable) FIFO fifo2(buf2); FIFO fifo3(buf3); FIFO fifo4(buf4); + FIFO fifo5(buf5); /* Create node objects */ - Duplicate3 dup(fifo1,fifo2,fifo3,fifo4); - ProcessingNode filter(fifo0,fifo1,4,"Test",someVariable); - Sink sa(fifo2); - Sink sb(fifo3); - Sink sc(fifo4); + Duplicate3 dup0(fifo2,fifo3,fifo4,fifo5); + ProcessingNode filter(fifo0,fifo2,fifo1,4,"Test",someVariable); + Sink sa(fifo3); + Sink sb(fifo4); + Sink sc(fifo5); + Sink sd(fifo1); Source source(fifo0); /* Run several schedule iterations */ @@ -76,7 +82,9 @@ uint32_t scheduler(int *error,int someVariable) CHECKERROR; sdfError = filter.run(); CHECKERROR; - sdfError = dup.run(); + sdfError = sd.run(); + CHECKERROR; + sdfError = dup0.run(); CHECKERROR; sdfError = sc.run(); CHECKERROR; @@ -90,7 +98,9 @@ uint32_t scheduler(int *error,int someVariable) CHECKERROR; sdfError = source.run(); CHECKERROR; - sdfError = dup.run(); + sdfError = sd.run(); + CHECKERROR; + sdfError = dup0.run(); CHECKERROR; sdfError = sc.run(); CHECKERROR; @@ -102,7 +112,9 @@ uint32_t scheduler(int *error,int someVariable) CHECKERROR; sdfError = filter.run(); CHECKERROR; - sdfError = dup.run(); + sdfError = sd.run(); + CHECKERROR; + sdfError = dup0.run(); CHECKERROR; sdfError = sc.run(); CHECKERROR; @@ -116,7 +128,9 @@ uint32_t scheduler(int *error,int someVariable) CHECKERROR; sdfError = source.run(); CHECKERROR; - sdfError = dup.run(); + sdfError = sd.run(); + CHECKERROR; + sdfError = dup0.run(); CHECKERROR; sdfError = sc.run(); CHECKERROR; @@ -126,7 +140,9 @@ uint32_t scheduler(int *error,int someVariable) CHECKERROR; sdfError = filter.run(); CHECKERROR; - sdfError = dup.run(); + sdfError = sd.run(); + CHECKERROR; + sdfError = dup0.run(); CHECKERROR; sdfError = sc.run(); CHECKERROR; diff --git a/SDFTools/examples/example8/graph.py b/SDFTools/examples/example8/graph.py index 7124c645..a8c84851 100644 --- a/SDFTools/examples/example8/graph.py +++ b/SDFTools/examples/example8/graph.py @@ -6,7 +6,8 @@ class Node(GenericNode): def __init__(self,name,theType,inLength,outLength): GenericNode.__init__(self,name) self.addInput("i",theType,inLength) - self.addOutput("o",theType,outLength) + self.addOutput("oa",theType,outLength) + self.addOutput("ob",theType,outLength) class Sink(GenericSink): def __init__(self,name,theType,inLength): @@ -52,44 +53,54 @@ b.addVariableArg("someVariable") na = Sink("sa",complexType,5) nb = Sink("sb",complexType,5) nc = Sink("sc",complexType,5) -dup=Duplicate3("dup",complexType,5) +nd = Sink("sd",complexType,5) + +#dup=Duplicate3("dup",complexType,5) g = Graph() g.connect(src.o,b.i) -g.connect(b.o,dup.i) -g.connect(dup.oa,na.i) -g.connect(dup.ob,nb.i) -g.connect(dup.oc,nc.i) +#g.connect(b.o,dup.i) +#g.connect(dup.oa,na.i) +#g.connect(dup.ob,nb.i) +#g.connect(dup.oc,nc.i) + +g.connect(b.oa,na.i) +g.connect(b.oa,nb.i) +g.connect(b.oa,nc.i) +g.connect(b.ob,nd.i) + +GEN_PYTHON = False print("Generate graphviz and code") +sched = g.computeSchedule() +print("Schedule length = %d" % sched.scheduleLength) +print("Memory usage %d bytes" % sched.memory) + +# Generation of the schedule is modifying the original graph +# (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.displayFIFOSizes=True -# Prefix for global FIFO buffers -#conf.prefix="sched1" - -#print(g.nullVector()) -sched1 = g.computeSchedule() -#print(sched.schedule) -print("Schedule length = %d" % sched1.scheduleLength) -print("Memory usage %d bytes" % sched1.memory) -# - #conf.codeArray=True -# C++ implementation -sched1.ccode("generated",conf) +conf.memoryOptimization=True + +if not GEN_PYTHON: + # C++ implementation + sched.ccode("generated",conf) +else: + # Python implementation + conf.pyOptionalArgs="someVariable" + sched.pythoncode(".",config=conf) -sched2 = g.computeSchedule() -# Python implementation -#conf.prefix="sched1" -conf.pyOptionalArgs="someVariable" -#conf.dumpFIFO=True -sched2.pythoncode(".",config=conf) +# When true it is displaying the name of the FIFO buffer +# When false it is displaying the size of the FIFO (default) +conf.displayFIFOBuf=False with open("test.dot","w") as f: - sched1.graphviz(f) + sched.graphviz(f,config=conf) diff --git a/SDFTools/examples/example8/sched.py b/SDFTools/examples/example8/sched.py index 9d326446..00603162 100644 --- a/SDFTools/examples/example8/sched.py +++ b/SDFTools/examples/example8/sched.py @@ -51,6 +51,12 @@ buf4=np.empty(FIFOSIZE4,dtype=object) for i in range(FIFOSIZE4): buf4[i] = MyComplex() +FIFOSIZE5=5 + +buf5=np.empty(FIFOSIZE5,dtype=object) +for i in range(FIFOSIZE5): + buf5[i] = MyComplex() + def scheduler(someVariable): sdfError=0 @@ -65,15 +71,17 @@ def scheduler(someVariable): fifo2=FIFO(FIFOSIZE2,buf2) fifo3=FIFO(FIFOSIZE3,buf3) fifo4=FIFO(FIFOSIZE4,buf4) + fifo5=FIFO(FIFOSIZE5,buf5) # # Create node objects # - dup = Duplicate3(5,5,5,5,fifo1,fifo2,fifo3,fifo4) - filter = ProcessingNode(7,5,fifo0,fifo1,4,"Test",someVariable) - sa = Sink(5,fifo2) - sb = Sink(5,fifo3) - sc = Sink(5,fifo4) + dup0 = Duplicate3(5,5,5,5,fifo2,fifo3,fifo4,fifo5) + filter = ProcessingNode(7,5,5,fifo0,fifo2,fifo1,4,"Test",someVariable) + sa = Sink(5,fifo3) + sb = Sink(5,fifo4) + sc = Sink(5,fifo5) + sd = Sink(5,fifo1) source = Source(5,fifo0) while((sdfError==0) and (debugCounter > 0)): @@ -88,7 +96,10 @@ def scheduler(someVariable): sdfError = filter.run() if sdfError < 0: break - sdfError = dup.run() + sdfError = sd.run() + if sdfError < 0: + break + sdfError = dup0.run() if sdfError < 0: break sdfError = sc.run() @@ -109,7 +120,10 @@ def scheduler(someVariable): sdfError = source.run() if sdfError < 0: break - sdfError = dup.run() + sdfError = sd.run() + if sdfError < 0: + break + sdfError = dup0.run() if sdfError < 0: break sdfError = sc.run() @@ -127,7 +141,10 @@ def scheduler(someVariable): sdfError = filter.run() if sdfError < 0: break - sdfError = dup.run() + sdfError = sd.run() + if sdfError < 0: + break + sdfError = dup0.run() if sdfError < 0: break sdfError = sc.run() @@ -148,7 +165,10 @@ def scheduler(someVariable): sdfError = source.run() if sdfError < 0: break - sdfError = dup.run() + sdfError = sd.run() + if sdfError < 0: + break + sdfError = dup0.run() if sdfError < 0: break sdfError = sc.run() @@ -163,7 +183,10 @@ def scheduler(someVariable): sdfError = filter.run() if sdfError < 0: break - sdfError = dup.run() + sdfError = sd.run() + if sdfError < 0: + break + sdfError = dup0.run() if sdfError < 0: break sdfError = sc.run() diff --git a/SDFTools/examples/example8/test.dot b/SDFTools/examples/example8/test.dot index 178f4722..992660b3 100644 --- a/SDFTools/examples/example8/test.dot +++ b/SDFTools/examples/example8/test.dot @@ -1,6 +1,7 @@ + digraph structs { node [shape=plaintext] rankdir=LR @@ -8,33 +9,24 @@ digraph structs { fontname="times" +dup0 [shape=point,label=dup0] + -dup [label=< +filter [label=< - + - - - -
idup
(Duplicate3)
filter
(ProcessingNode)
oa
ob
oc
>]; -filter [label=< - - - - -
filter
(ProcessingNode)
>]; - sa [label=< @@ -56,6 +48,13 @@ sc [label=<
>]; +sd [label=< + + + + +
sd
(Sink)
>]; + source [label=< @@ -65,35 +64,41 @@ source [label=< -source:i -> filter:i [headlabel=< -
7 -
>,taillabel=< -
5 -
>,label="complex(11)"] - -filter:i -> dup:i [headlabel=< -
5 -
>,taillabel=< -
5 -
>,label="complex(5)"] - -dup:oa -> sa:i [headlabel=< -
5 -
>,taillabel=< -
5 -
>,label="complex(5)"] - -dup:ob -> sb:i [headlabel=< -
5 -
>,taillabel=< -
5 -
>,label="complex(5)"] - -dup:oc -> sc:i [headlabel=< -
5 -
>,taillabel=< -
5 -
>,label="complex(5)"] +source:i -> filter:i [label="complex(11)" +,headlabel=<
7 +
> +,taillabel=<
5 +
>] + +filter:ob -> sd:i [label="complex(5)" +,headlabel=<
5 +
> +,taillabel=<
5 +
>] + +filter:oa -> +dup0 [label="complex(5)" + +,taillabel=<
5 +
>] + + +dup0 -> sa:i [label="complex(5)" +,headlabel=<
5 +
> +] + + +dup0 -> sb:i [label="complex(5)" +,headlabel=<
5 +
> +] + + +dup0 -> sc:i [label="complex(5)" +,headlabel=<
5 +
> +] } diff --git a/SDFTools/examples/example8/test.pdf b/SDFTools/examples/example8/test.pdf index 1d2051e5..acfc5690 100644 Binary files a/SDFTools/examples/example8/test.pdf and b/SDFTools/examples/example8/test.pdf differ diff --git a/cmsisdsp/sdf/nodes/Duplicate.py b/cmsisdsp/sdf/nodes/Duplicate.py index 4a00e024..ab86284c 100644 --- a/cmsisdsp/sdf/nodes/Duplicate.py +++ b/cmsisdsp/sdf/nodes/Duplicate.py @@ -43,7 +43,7 @@ class Duplicate2(GenericNode12): class Duplicate3(GenericNode13): def __init__(self,inputSize,outputSize1,outputSize2,outputSize3,fifoin,fifoout1,fifoout2,fifoout3): GenericNode13.__init__(self,inputSize,outputSize1,outputSize2,outputSize3,fifoin,fifoout1,fifoout2,fifoout3) - + def run(self): a=self.getReadBuffer() b=self.getWriteBuffer1() diff --git a/cmsisdsp/sdf/scheduler/description.py b/cmsisdsp/sdf/scheduler/description.py index 6a31ddf3..14270466 100755 --- a/cmsisdsp/sdf/scheduler/description.py +++ b/cmsisdsp/sdf/scheduler/description.py @@ -38,6 +38,8 @@ import cmsisdsp.sdf.scheduler.pythoncode from .node import * from .config import * +from .standard import Duplicate2,Duplicate3 + from ..types import * # To debug graph coloring for memory optimization @@ -58,6 +60,9 @@ class DeadlockError(Exception): class CannotDelayConstantError(Exception): pass +class TooManyNodesOnOutput(Exception): + pass + class FifoBuffer: """Buffer used by a FIFO""" def __init__(self,bufferID,theType,length): @@ -165,7 +170,128 @@ class Graph(): self._allFIFOs = None self._allBuffers = None - def connect(self,nodea,nodeb): + def connectDup(self,destination,outputIO,theId): + if (destination[theId][1]!=0): + self.connectWithDelay(outputIO,destination[theId][0],destination[theId][1],dupAllowed=False) + else: + self.connect(outputIO,destination[theId][0],dupAllowed=False) + + + + def insertDuplicates(self): + # Insert dup nodes (Duplicate2 and Duplicate3) to + # ensure that an output is connected to only one input + dupNb = 0 + # Scan all nodes + allNodes = list(self._g) + for n in allNodes: + # For each nodes, get the IOs + for k in n._outputs.keys(): + output = n._outputs[k] + fifo = output.fifo + # Check if the FIFO list has only 1 element + # In this case, we convert it into a value + # since code using the graph is expecting an + # IO to be connected top one and only one other IO + # so the FIFO must be a value and not a list + if len(fifo)==1: + # Extract first element of list + fifo = fifo[0] + fifo[0].fifo = fifo + fifo[1].fifo = fifo + # If the FIFO has more elements, we need to + # restructure the graph and add Duplicate nodes + else: + # Currently the library is only providing + # Duplicate2 and Duplicate3 nodes. + # So an output cannot be connected to more than + # 3 inputs + if (len(fifo)>3): + raise TooManyNodesOnOutput + + dup = None + # We extract from the IO the nb of produced + # samples and the data type + # Duplicate will use the same + inputSize = output.nbSamples + theType = output.theType + + # We create a duplicate node + if len(fifo)==2: + dup = Duplicate2("dup%d" % dupNb,theType,inputSize) + + if len(fifo)==3: + dup = Duplicate3("dup%d" % dupNb,theType,inputSize) + + #print(dup) + + + + dupNb = dupNb + 1 + # We disconnect all the fifo element (a,b) + # and remember the destination b + # We must rewrite the edges of self._g + # self._edges + # self._nodes + # the node fifo + destinations = [] + delays = [] + + self._sortedNodes = None + self._sortedEdges = None + for f in fifo: + # IO connected to the FIFOs + # nodea is the IO belowing to nodea + # but not the node itself + nodea = f[0] + nodeb = f[1] + + if (nodea,nodeb) in self._delays: + delay = self._delays[(nodea,nodeb)] + else: + delay = 0 + + destinations.append((nodeb,delay)) + + nodea.fifo=None + nodeb.fifo=None + # Since we are not using a multi graph + # and there no parallel edges, it will + # remove all edges between two nodes. + # But some edges may come from other IO + # and be valid. + # Anyway, those edges are not used + # in the algorithm at all + # Instead we have our own graph with self._edges + # (This script will have to be rewritten in a much + # cleaner way) + self._g.remove_edge(nodea.owner,nodeb.owner) + del self._edges[(nodea,nodeb)] + if (nodea,nodeb) in self._delays: + del self._delays[(nodea,nodeb)] + + + + # Now for each fifo destination we need + # create a new + # connection dup -> b + # And we create a new connection a -> dup + + self.connect(output,dup.i,dupAllowed=False) + + if len(destinations)==2: + self.connectDup(destinations,dup.oa,0) + self.connectDup(destinations,dup.ob,1) + + if len(destinations)==3: + self.connectDup(destinations,dup.oa,0) + self.connectDup(destinations,dup.ob,1) + self.connectDup(destinations,dup.oc,2) + + + + + def connect(self,nodea,nodeb,dupAllowed=True): # When connecting to a constant node we do nothing # since there is no FIFO in this case # and it does not participate to the scheduling. @@ -178,8 +304,12 @@ class Graph(): self._sortedEdges = None self._g.add_edge(nodea.owner,nodeb.owner) - nodea.fifo = (nodea,nodeb) - nodeb.fifo = (nodea,nodeb) + if dupAllowed: + nodea.fifo.append((nodea,nodeb)) + nodeb.fifo.append((nodea,nodeb)) + else: + nodea.fifo=(nodea,nodeb) + nodeb.fifo=(nodea,nodeb) self._edges[(nodea,nodeb)]=True if not (nodea.owner in self._nodes): self._nodes[nodea.owner]=True @@ -188,12 +318,12 @@ class Graph(): else: raise IncompatibleIO - def connectWithDelay(self,nodea,nodeb,delay): + def connectWithDelay(self,nodea,nodeb,delay,dupAllowed=True): # We cannot connect with delay to a constant node if (isinstance(nodea,Constant)): raise CannotDelayConstantError else: - self.connect(nodea,nodeb) + self.connect(nodea,nodeb,dupAllowed=dupAllowed) self._delays[(nodea,nodeb)] = delay def __str__(self): @@ -426,7 +556,14 @@ class Graph(): v[nodeID] = 1 return(v) + + def computeSchedule(self,config=Configuration()): + # First we must rewrite the graph and insert duplication + # nodes when an ouput is connected to several inputs. + # After this transform, each output should be connected to + # only one output. + self.insertDuplicates() # Init values initB = self.initEvolutionVector initN = self.nullVector() diff --git a/cmsisdsp/sdf/scheduler/node.py b/cmsisdsp/sdf/scheduler/node.py index f7c0ee21..9b405a2b 100755 --- a/cmsisdsp/sdf/scheduler/node.py +++ b/cmsisdsp/sdf/scheduler/node.py @@ -54,7 +54,7 @@ class IO: self._nbSamples = nbSamples self._owner = owner self._name = name - self._fifo = None + self._fifo = [] self.constantNode = None @property @@ -214,6 +214,10 @@ class BaseNode: def isConstantNode(self): return False + @property + def isDuplicateNode(self): + return False + @property def hasState(self): """False if the node is a pure functiom with no state diff --git a/cmsisdsp/sdf/scheduler/standard.py b/cmsisdsp/sdf/scheduler/standard.py index 0d63dfc0..21d66bc1 100755 --- a/cmsisdsp/sdf/scheduler/standard.py +++ b/cmsisdsp/sdf/scheduler/standard.py @@ -144,6 +144,10 @@ class Duplicate2(GenericNode): self.addOutput("oa",theType,inLength) self.addOutput("ob",theType,inLength) + @property + def isDuplicateNode(self): + return True + @property def typeName(self): return "Duplicate2" @@ -157,6 +161,10 @@ class Duplicate3(GenericNode): self.addOutput("ob",theType,inLength) self.addOutput("oc",theType,inLength) + @property + def isDuplicateNode(self): + return True + @property def typeName(self): return "Duplicate3" diff --git a/cmsisdsp/sdf/scheduler/templates/dot_template.dot b/cmsisdsp/sdf/scheduler/templates/dot_template.dot index abfa5a74..083a6c05 100755 --- a/cmsisdsp/sdf/scheduler/templates/dot_template.dot +++ b/cmsisdsp/sdf/scheduler/templates/dot_template.dot @@ -1,5 +1,14 @@ {% macro io(node,theio) -%} +{% if node.isDuplicateNode %} +{{node.nodeName}}{% else %} {% if not node.hasManyIOs %}{{node.nodeID}}:i{% else %}{{node.nodeID}}:{{theio.name}}{% endif %} +{% endif %} +{%- endmacro %} + +{% macro edgelabel(ioport,name) -%} +{% if not ioport.owner.isDuplicateNode %} +,{{name}}=<
{{ioport.nbSamples}} +
>{% endif %} {%- endmacro %} {% macro constdst(theID) -%} @@ -29,6 +38,9 @@ digraph structs { {% for item in nodes %} +{% if item.isDuplicateNode %} +{{item.nodeID}} [shape=point,label={{item.nodeName}}] +{% else %} {% if not item.hasManyIOs %} {{item.nodeID}} [label=< @@ -104,6 +116,7 @@ digraph structs {
>]; {% endif %} {% endif %} +{% endif %} {% endfor %} @@ -111,33 +124,25 @@ digraph structs { {{delayBox(id)}} {% if fifos[id].hasDelay %} -{{io(fifos[id].src.owner,fifos[id].src)}} -> {{delayBoxID(id)}}:i [taillabel=< -
{{fifos[id].src.nbSamples}} -
>] +{{io(fifos[id].src.owner,fifos[id].src)}} -> {{delayBoxID(id)}}:i [label=""{{edgelabel(fifos[id].src,"taillabel")}}] {% if config.displayFIFOBuf %} -{{delayBoxID(id)}}:i -> {{io(fifos[id].dst.owner,fifos[id].dst)}} [headlabel=< -
{{fifos[id].dst.nbSamples}} -
>,label="buf{{fifos[id].bufferID}}"] +{{delayBoxID(id)}}:i -> {{io(fifos[id].dst.owner,fifos[id].dst)}} [label="buf{{fifos[id].bufferID}}" +{{edgelabel(fifos[id].src,"headlabel")}}] {% else %} -{{delayBoxID(id)}}:i -> {{io(fifos[id].dst.owner,fifos[id].dst)}} [headlabel=< -
{{fifos[id].dst.nbSamples}} -
>,label="{{fifos[id].theType.graphViztype}}({{fifos[id].length}})"] +{{delayBoxID(id)}}:i -> {{io(fifos[id].dst.owner,fifos[id].dst)}} [label="{{fifos[id].theType.graphViztype}}({{fifos[id].length}})" +{{edgelabel(fifos[id].dst,"headlabel")}}] {% endif %} {% else %} {% if config.displayFIFOBuf %} -{{io(fifos[id].src.owner,fifos[id].src)}} -> {{io(fifos[id].dst.owner,fifos[id].dst)}} [headlabel=< -
{{fifos[id].dst.nbSamples}} -
>,taillabel=< -
{{fifos[id].src.nbSamples}} -
>,label="buf{{fifos[id].bufferID}}"] +{{io(fifos[id].src.owner,fifos[id].src)}} -> {{io(fifos[id].dst.owner,fifos[id].dst)}} [label="buf{{fifos[id].bufferID}}" +{{edgelabel(fifos[id].dst,"headlabel")}} +{{edgelabel(fifos[id].src,"taillabel")}}] {% else %} -{{io(fifos[id].src.owner,fifos[id].src)}} -> {{io(fifos[id].dst.owner,fifos[id].dst)}} [headlabel=< -
{{fifos[id].dst.nbSamples}} -
>,taillabel=< -
{{fifos[id].src.nbSamples}} -
>,label="{{fifos[id].theType.graphViztype}}({{fifos[id].length}})"] +{{io(fifos[id].src.owner,fifos[id].src)}} -> {{io(fifos[id].dst.owner,fifos[id].dst)}} [label="{{fifos[id].theType.graphViztype}}({{fifos[id].length}})" +{{edgelabel(fifos[id].dst,"headlabel")}} +{{edgelabel(fifos[id].src,"taillabel")}}] {% endif %} {% endif %} {% endfor %}