Improvement to compute graph

New cyclo static scheduler
New code generation mode using switch / case to decrease code size.
pull/53/head
Christophe Favergeon 3 years ago
parent 1edf36dff6
commit c91c8d6cec

@ -0,0 +1,36 @@
# Reference statistics
The different examples should return following schedule statistics:
## Example 1
Schedule length = 17
Memory usage 64 bytes
## Example 2
Schedule length = 302
Memory usage 10720 bytes
## Example 3
Schedule length = 25
Memory usage 11264 bytes
## Example 4
Schedule length = 25
Memory usage 11264 bytes
## Example 5
Schedule length = 292
Memory usage 6614 bytes
## Example 6
Schedule length = 17
Memory usage 2204 bytes
## Example 7
Schedule length = 3
Memory usage 512 bytes
## Example 8
Schedule length = 37
Memory usage 288 bytes

@ -14,6 +14,17 @@ The support classes and code is covered by CMSIS-DSP license.
#include "AppNodes.h" #include "AppNodes.h"
#include "scheduler.h" #include "scheduler.h"
/*
Description of the scheduling. It is a list of nodes to call.
The values are indexes in the previous array.
*/
static unsigned int schedule[17]=
{
2,2,0,1,2,0,1,2,2,0,1,2,0,1,2,0,1,
};
/*********** /***********
FIFO buffers FIFO buffers
@ -51,45 +62,40 @@ uint32_t scheduler(int *error,int someVariable)
/* Run several schedule iterations */ /* Run several schedule iterations */
while((cgStaticError==0) && (debugCounter > 0)) while((cgStaticError==0) && (debugCounter > 0))
{ {
/* Run a schedule iteration */ /* Run a schedule iteration */
cgStaticError = source.run(); for(unsigned long id=0 ; id < 17; id++)
CHECKERROR; {
cgStaticError = source.run(); switch(schedule[id])
CHECKERROR; {
cgStaticError = filter.run(); case 0:
CHECKERROR; {
cgStaticError = sink.run(); cgStaticError = filter.run();
CHECKERROR; CHECKERROR;
cgStaticError = source.run(); }
CHECKERROR; break;
cgStaticError = filter.run();
CHECKERROR; case 1:
cgStaticError = sink.run(); {
CHECKERROR; cgStaticError = sink.run();
cgStaticError = source.run(); CHECKERROR;
CHECKERROR; }
cgStaticError = source.run(); break;
CHECKERROR;
cgStaticError = filter.run(); case 2:
CHECKERROR; {
cgStaticError = sink.run(); cgStaticError = source.run();
CHECKERROR; CHECKERROR;
cgStaticError = source.run(); }
CHECKERROR; break;
cgStaticError = filter.run();
CHECKERROR; default:
cgStaticError = sink.run(); break;
CHECKERROR; }
cgStaticError = source.run(); }
CHECKERROR;
cgStaticError = filter.run();
CHECKERROR;
cgStaticError = sink.run();
CHECKERROR;
debugCounter--; debugCounter--;
nbSchedule++; nbSchedule++;
} }
*error=cgStaticError; *error=cgStaticError;
return(nbSchedule); return(nbSchedule);
} }

File diff suppressed because it is too large Load Diff

@ -14,6 +14,17 @@ The support classes and code is covered by CMSIS-DSP license.
#include "AppNodes.h" #include "AppNodes.h"
#include "scheduler.h" #include "scheduler.h"
/*
Description of the scheduling. It is a list of nodes to call.
The values are indexes in the previous array.
*/
static unsigned int schedule[25]=
{
6,2,0,7,3,4,8,1,6,2,0,7,3,4,8,2,0,7,3,4,1,5,8,1,5,
};
/*********** /***********
FIFO buffers FIFO buffers
@ -86,12 +97,14 @@ uint32_t scheduler(int *error)
/* Run several schedule iterations */ /* Run several schedule iterations */
while((cgStaticError==0) && (debugCounter > 0)) while((cgStaticError==0) && (debugCounter > 0))
{ {
/* Run a schedule iteration */ /* Run a schedule iteration */
cgStaticError = src.run(); for(unsigned long id=0 ; id < 25; id++)
CHECKERROR; {
cgStaticError = audioWin.run(); switch(schedule[id])
CHECKERROR; {
{ case 0:
{
{
float32_t* i0; float32_t* i0;
float32_t* o2; float32_t* o2;
i0=fifo1.getReadBuffer(256); i0=fifo1.getReadBuffer(256);
@ -99,69 +112,74 @@ uint32_t scheduler(int *error)
arm_mult_f32(i0,HANN,o2,256); arm_mult_f32(i0,HANN,o2,256);
cgStaticError = 0; cgStaticError = 0;
} }
CHECKERROR; CHECKERROR;
cgStaticError = toCmplx.run(); }
CHECKERROR; break;
cgStaticError = cfft.run();
CHECKERROR; case 1:
cgStaticError = icfft.run(); {
CHECKERROR; cgStaticError = audioOverlap.run();
cgStaticError = toReal.run(); CHECKERROR;
CHECKERROR; }
cgStaticError = audioOverlap.run(); break;
CHECKERROR;
cgStaticError = src.run(); case 2:
CHECKERROR; {
cgStaticError = audioWin.run(); cgStaticError = audioWin.run();
CHECKERROR; CHECKERROR;
{ }
float32_t* i0; break;
float32_t* o2;
i0=fifo1.getReadBuffer(256); case 3:
o2=fifo2.getWriteBuffer(256); {
arm_mult_f32(i0,HANN,o2,256); cgStaticError = cfft.run();
cgStaticError = 0; CHECKERROR;
} }
CHECKERROR; break;
cgStaticError = toCmplx.run();
CHECKERROR; case 4:
cgStaticError = cfft.run(); {
CHECKERROR; cgStaticError = icfft.run();
cgStaticError = icfft.run(); CHECKERROR;
CHECKERROR; }
cgStaticError = toReal.run(); break;
CHECKERROR;
cgStaticError = audioWin.run(); case 5:
CHECKERROR; {
{ cgStaticError = sink.run();
float32_t* i0; CHECKERROR;
float32_t* o2; }
i0=fifo1.getReadBuffer(256); break;
o2=fifo2.getWriteBuffer(256);
arm_mult_f32(i0,HANN,o2,256); case 6:
cgStaticError = 0; {
} cgStaticError = src.run();
CHECKERROR; CHECKERROR;
cgStaticError = toCmplx.run(); }
CHECKERROR; break;
cgStaticError = cfft.run();
CHECKERROR; case 7:
cgStaticError = icfft.run(); {
CHECKERROR; cgStaticError = toCmplx.run();
cgStaticError = audioOverlap.run(); CHECKERROR;
CHECKERROR; }
cgStaticError = sink.run(); break;
CHECKERROR;
cgStaticError = toReal.run(); case 8:
CHECKERROR; {
cgStaticError = audioOverlap.run(); cgStaticError = toReal.run();
CHECKERROR; CHECKERROR;
cgStaticError = sink.run(); }
CHECKERROR; break;
default:
break;
}
}
debugCounter--; debugCounter--;
nbSchedule++; nbSchedule++;
} }
*error=cgStaticError; *error=cgStaticError;
return(nbSchedule); return(nbSchedule);
} }

@ -14,6 +14,17 @@ The support classes and code is covered by CMSIS-DSP license.
#include "AppNodes.h" #include "AppNodes.h"
#include "scheduler.h" #include "scheduler.h"
/*
Description of the scheduling. It is a list of nodes to call.
The values are indexes in the previous array.
*/
static unsigned int schedule[17]=
{
4,0,1,2,3,3,4,0,1,2,3,3,0,1,2,3,3,
};
/*********** /***********
FIFO buffers FIFO buffers
@ -63,45 +74,54 @@ uint32_t scheduler(int *error,arm_mfcc_instance_f32 *mfccConfig)
/* Run several schedule iterations */ /* Run several schedule iterations */
while((cgStaticError==0) && (debugCounter > 0)) while((cgStaticError==0) && (debugCounter > 0))
{ {
/* Run a schedule iteration */ /* Run a schedule iteration */
cgStaticError = src.run(); for(unsigned long id=0 ; id < 17; id++)
CHECKERROR; {
cgStaticError = audioWin.run(); switch(schedule[id])
CHECKERROR; {
cgStaticError = mfcc.run(); case 0:
CHECKERROR; {
cgStaticError = mfccWin.run(); cgStaticError = audioWin.run();
CHECKERROR; CHECKERROR;
cgStaticError = sink.run(); }
CHECKERROR; break;
cgStaticError = sink.run();
CHECKERROR; case 1:
cgStaticError = src.run(); {
CHECKERROR; cgStaticError = mfcc.run();
cgStaticError = audioWin.run(); CHECKERROR;
CHECKERROR; }
cgStaticError = mfcc.run(); break;
CHECKERROR;
cgStaticError = mfccWin.run(); case 2:
CHECKERROR; {
cgStaticError = sink.run(); cgStaticError = mfccWin.run();
CHECKERROR; CHECKERROR;
cgStaticError = sink.run(); }
CHECKERROR; break;
cgStaticError = audioWin.run();
CHECKERROR; case 3:
cgStaticError = mfcc.run(); {
CHECKERROR; cgStaticError = sink.run();
cgStaticError = mfccWin.run(); CHECKERROR;
CHECKERROR; }
cgStaticError = sink.run(); break;
CHECKERROR;
cgStaticError = sink.run(); case 4:
CHECKERROR; {
cgStaticError = src.run();
CHECKERROR;
}
break;
default:
break;
}
}
debugCounter--; debugCounter--;
nbSchedule++; nbSchedule++;
} }
*error=cgStaticError; *error=cgStaticError;
return(nbSchedule); return(nbSchedule);
} }

@ -14,6 +14,17 @@ The support classes and code is covered by CMSIS-DSP license.
#include "AppNodes.h" #include "AppNodes.h"
#include "scheduler.h" #include "scheduler.h"
/*
Description of the scheduling. It is a list of nodes to call.
The values are indexes in the previous array.
*/
static unsigned int schedule[37]=
{
6,6,1,5,0,4,3,2,6,1,6,5,0,4,3,2,6,1,5,0,4,3,2,6,1,6,5,0,4,3,2,1,5,0,4,3,2,
};
/*********** /***********
FIFO buffers FIFO buffers
@ -75,85 +86,68 @@ uint32_t scheduler(int *error,int someVariable)
/* Run several schedule iterations */ /* Run several schedule iterations */
while((cgStaticError==0) && (debugCounter > 0)) while((cgStaticError==0) && (debugCounter > 0))
{ {
/* Run a schedule iteration */ /* Run a schedule iteration */
cgStaticError = source.run(); for(unsigned long id=0 ; id < 37; id++)
CHECKERROR; {
cgStaticError = source.run(); switch(schedule[id])
CHECKERROR; {
cgStaticError = filter.run(); case 0:
CHECKERROR; {
cgStaticError = sd.run(); cgStaticError = dup0.run();
CHECKERROR; CHECKERROR;
cgStaticError = dup0.run(); }
CHECKERROR; break;
cgStaticError = sc.run();
CHECKERROR; case 1:
cgStaticError = sb.run(); {
CHECKERROR; cgStaticError = filter.run();
cgStaticError = sa.run(); CHECKERROR;
CHECKERROR; }
cgStaticError = source.run(); break;
CHECKERROR;
cgStaticError = filter.run(); case 2:
CHECKERROR; {
cgStaticError = source.run(); cgStaticError = sa.run();
CHECKERROR; CHECKERROR;
cgStaticError = sd.run(); }
CHECKERROR; break;
cgStaticError = dup0.run();
CHECKERROR; case 3:
cgStaticError = sc.run(); {
CHECKERROR; cgStaticError = sb.run();
cgStaticError = sb.run(); CHECKERROR;
CHECKERROR; }
cgStaticError = sa.run(); break;
CHECKERROR;
cgStaticError = source.run(); case 4:
CHECKERROR; {
cgStaticError = filter.run(); cgStaticError = sc.run();
CHECKERROR; CHECKERROR;
cgStaticError = sd.run(); }
CHECKERROR; break;
cgStaticError = dup0.run();
CHECKERROR; case 5:
cgStaticError = sc.run(); {
CHECKERROR; cgStaticError = sd.run();
cgStaticError = sb.run(); CHECKERROR;
CHECKERROR; }
cgStaticError = sa.run(); break;
CHECKERROR;
cgStaticError = source.run(); case 6:
CHECKERROR; {
cgStaticError = filter.run(); cgStaticError = source.run();
CHECKERROR; CHECKERROR;
cgStaticError = source.run(); }
CHECKERROR; break;
cgStaticError = sd.run();
CHECKERROR; default:
cgStaticError = dup0.run(); break;
CHECKERROR; }
cgStaticError = sc.run(); }
CHECKERROR;
cgStaticError = sb.run();
CHECKERROR;
cgStaticError = sa.run();
CHECKERROR;
cgStaticError = filter.run();
CHECKERROR;
cgStaticError = sd.run();
CHECKERROR;
cgStaticError = dup0.run();
CHECKERROR;
cgStaticError = sc.run();
CHECKERROR;
cgStaticError = sb.run();
CHECKERROR;
cgStaticError = sa.run();
CHECKERROR;
debugCounter--; debugCounter--;
nbSchedule++; nbSchedule++;
} }
*error=cgStaticError; *error=cgStaticError;
return(nbSchedule); return(nbSchedule);
} }

@ -42,7 +42,10 @@ def gencode(sched,directory,config):
schedDescription="" schedDescription=""
if config.codeArray: if config.codeArray:
ctemplate = env.get_template("codeArray.cpp") if config.switchCase:
ctemplate = env.get_template("codeSwitch.cpp")
else:
ctemplate = env.get_template("codeArray.cpp")
nb = 0 nb = 0
for s in sched.schedule: for s in sched.schedule:
schedDescription = schedDescription + ("%d," % sched.nodes[s].codeID) schedDescription = schedDescription + ("%d," % sched.nodes[s].codeID)

@ -63,7 +63,10 @@ class Configuration:
# When codeArray is true, instead of using # When codeArray is true, instead of using
# function calls we parse un array giving # function calls we parse un array giving
# the index of functions to call in another array # the index of functions to call in another array
self.codeArray = False self.codeArray = True
# If users do not want to use function pointers,
# we can generate a switch/case instead
self.switchCase = True
# True for an horizontal graphviz layout # True for an horizontal graphviz layout
self.horizontal = True self.horizontal = True

@ -505,6 +505,10 @@ class Graph():
def topologyMatrix(self): def topologyMatrix(self):
self.checkGraph() self.checkGraph()
# For cyclo static scheduling : compute the node periods
for n in self.nodes:
n.computeCyclePeriod()
rows=[] rows=[]
# This is used in schedule generation # This is used in schedule generation
# and all functions must use the same node ordering # and all functions must use the same node ordering
@ -522,21 +526,27 @@ class Graph():
ib=self._sortedNodes.index(nb.owner) ib=self._sortedNodes.index(nb.owner)
# Produced by na on the edge # Produced by na on the edge
currentRow[ia] = na.nbSamples # for execution of one cycle of the na.owner node
totalProduced = na.cycleTotal * na.owner.cyclePeriod // na.cyclePeriod
currentRow[ia] = totalProduced
# Consumed by nb on the edge # Consumed by nb on the edge
currentRow[ib] = -nb.nbSamples # for execution of a full cycle of the node
totalProduced = nb.cycleTotal * nb.owner.cyclePeriod // nb.cyclePeriod
currentRow[ib] = -totalProduced
rows.append(currentRow) rows.append(currentRow)
return(np.array(rows)) return(np.array(rows))
def nullVector(self): def nullVector(self,m):
m = self.topologyMatrix() #print("Null vector")
# m is topology matrix computed with toplogyMatrix
r=Matrix(m).nullspace() r=Matrix(m).nullspace()
if len(r) != 1: if len(r) != 1:
raise NotSchedulableError raise NotSchedulableError
result=list(r[0]) result=list(r[0])
#print(result)
denominators = [x.q for x in result] denominators = [x.q for x in result]
# Remove denominators # Remove denominators
ppcm = ilcm(*denominators) ppcm = ilcm(*denominators)
@ -544,17 +554,72 @@ class Graph():
intValues = [x * ppcm for x in result] intValues = [x * ppcm for x in result]
# Convert intValues to the smallest possible values # Convert intValues to the smallest possible values
gcd = igcd(*intValues) gcd = igcd(*intValues)
return([x / gcd for x in intValues]) return([x // gcd for x in intValues])
@property @property
def initEvolutionVector(self): def initEvolutionVector(self):
"""Initial FIFO state taking into account delays""" """Initial FIFO state taking into account delays"""
return(np.array([self.getDelay(x) for x in self.edges])) return(np.array([self.getDelay(x) for x in self.edges]))
def evolutionVectorForNode(self,nodeID): def evolutionVectorForNode(self,nodeID,test=True):
"""Return the evolution vector corresponding to a selected node""" """Return the evolution vector corresponding to a selected node"""
v = np.zeros(len(self._sortedNodes)) # For a simple static scheduling, the toplogy matrix T
v[nodeID] = 1 # is enough. If we know which node is executed, we have
# a node vector V where there is a 1 at the position of the
# execute node.
# And the data generated on the fifos is:
# T . V so the new fifo vector B' is given with
# B' = T .V + B
# But in case of cyclo static scheduling the topology
# matrix is for a full period of the node
# so 1 or several periods of each IO.
# And we don't have the granularity corresponding to
# one node execution.
# For one node execution, we need to know where we are
# in the cycle on each IO
# and where we are in the cycle for the node to
# track how many full eriods of the node execution have been
# done.
# So this function is not just returning the node vector
# and letting the main function updating the fifos.
# This function is returning the new fifo state
# Node vector would have been
# v = np.zeros(len(self._sortedNodes))
# v[nodeID] = 1
# for cyclo static scheduling
n = self._sortedNodes[nodeID]
#print(n)
v = np.zeros(len(self._sortedEdges))
# In test mode we are testing several nodes
# to find the best one to schedule.
# So we should not advance the cycle
# of the IOs
for i in n._inputs:
io = n._inputs[i]
edge = io._fifo
# If 0, it is a connection to a constant node
# so there is no FIFO
if len(edge)>0:
pos = self._sortedEdges.index(edge)
v[pos] = -io.cycleValue
if not test:
io.advanceCycle()
for o in n._outputs:
io = n._outputs[o]
edge = io._fifo
# If 0 it is a connection to a constant edge
# so there is no FIFO
if len(edge)>0:
pos = self._sortedEdges.index(edge)
v[pos] = io.cycleValue
if not test:
io.advanceCycle()
#print(v)
return(v) return(v)
@ -565,9 +630,21 @@ class Graph():
# After this transform, each output should be connected to # After this transform, each output should be connected to
# only one output. # only one output.
self.insertDuplicates() self.insertDuplicates()
networkMatrix = self.topologyMatrix()
# Init values # Init values
initB = self.initEvolutionVector initB = self.initEvolutionVector
initN = self.nullVector() initN = self.nullVector(networkMatrix)
#print(initN)
# nullVector is giving the number of repetitions
# for a node cycle.
# So the number of iteration of the node must be multiplied
# by the cycle period for this node.
#for nodeID in range(len(self._sortedNodes)):
# initN[nodeID] = initN[nodeID] * self._sortedNodes[nodeID].cyclePeriod
# Current values (copys) # Current values (copys)
b = np.array(initB) b = np.array(initB)
@ -579,7 +656,9 @@ class Graph():
print(b) print(b)
# Topology matrix # Topology matrix
t = np.array(self.topologyMatrix()) t = np.array(networkMatrix)
#print(t)
#print(n)
# Define the list of FIFOs objects # Define the list of FIFOs objects
nbFIFOS = t.shape[0] nbFIFOS = t.shape[0]
@ -588,7 +667,18 @@ class Graph():
allFIFOs.append(FIFODesc(i)) allFIFOs.append(FIFODesc(i))
# Normalization vector # Normalization vector
normV = 1.0*np.apply_along_axis(abs,1,t).max(axis=1) # For static scheduling it is
#normV = 1.0*np.apply_along_axis(abs,1,t).max(axis=1)
# For cyclo static scheduling we need the max per execution
# and not the accumulated value for the run of a node
# period which may be several periods of an IO
#print(normV)
normV = np.zeros(len(self._sortedEdges))
for e in range(len(self._sortedEdges)):
(src,dst) = self._sortedEdges[e]
normV[e] = max(src.cycleMax, dst.cycleMax)
#print(normV)
# bMax below is used to track maximum FIFO size # bMax below is used to track maximum FIFO size
# occuring during a run of the schedule # occuring during a run of the schedule
@ -615,8 +705,11 @@ class Graph():
zeroVec = np.zeros(len(self._sortedNodes)) zeroVec = np.zeros(len(self._sortedNodes))
evolutionTime = 0 evolutionTime = 0
# While there are remaining nodes to schedule #print(self._sortedNodes)
# While there are remaining node periods to schedule
while (n != zeroVec).any(): while (n != zeroVec).any():
#print("")
#print(n)
# Look for the best node to schedule # Look for the best node to schedule
# which is the one giving the minimum FIFO increase # which is the one giving the minimum FIFO increase
@ -629,10 +722,14 @@ class Graph():
for node in self._sortedNodes: for node in self._sortedNodes:
# If the node can be scheduled # If the node can be scheduled
if n[nodeID] > 0: if n[nodeID] > 0:
# Evolution vector if this node is selected # Evolution vector for static scheduling
v = self.evolutionVectorForNode(nodeID) # v = self.evolutionVectorForNode(nodeID)
# for cyclo static we need the new fifo state
newB = self.evolutionVectorForNode(nodeID) + b
# New fifos size after this evolution # New fifos size after this evolution
newB = np.dot(t,v) + b # For static scheduling, fifo update would have been
# newB = np.dot(t,v) + b
#print(newB)
# Check that there is no FIFO underflow: # Check that there is no FIFO underflow:
if np.all(newB >= 0): if np.all(newB >= 0):
@ -656,13 +753,28 @@ class Graph():
# Now we have evaluated all schedulable nodes for this run # Now we have evaluated all schedulable nodes for this run
# and selected the one giving the smallest FIFO increase # and selected the one giving the smallest FIFO increase
# Implementation for static scheduling
# Real evolution vector for selected node # Real evolution vector for selected node
evol = self.evolutionVectorForNode(selected) # evol = self.evolutionVectorForNode(selected)
# Keep track that this node has been schedule # Keep track that this node has been schedule
n = n - evol # n = n - evol
# Compute new fifo state # Compute new fifo state
fifoChange = np.dot(t,evol) # fifoChange = np.dot(t,evol)
# b = fifoChange + b
# Implementation for cyclo static scheduling
#print("selected")
fifoChange = self.evolutionVectorForNode(selected,test=False)
b = fifoChange + b b = fifoChange + b
# For cyclo static, we create an evolution vector
# which contains a 1
# at a node position only if this node has executed
# its period.
# Otherwise the null vector is not decreased
v = np.zeros(len(self._sortedNodes))
v[selected] = self._sortedNodes[selected].executeNode()
n = n - v
if config.displayFIFOSizes: if config.displayFIFOSizes:
print(b) print(b)

@ -29,6 +29,10 @@
from jinja2 import Environment, FileSystemLoader, PackageLoader,select_autoescape from jinja2 import Environment, FileSystemLoader, PackageLoader,select_autoescape
import pathlib import pathlib
import os.path import os.path
import numpy as np
from sympy.core.numbers import ilcm
class NoFunctionArrayInPython(Exception): class NoFunctionArrayInPython(Exception):
pass pass
@ -51,12 +55,69 @@ class IO:
"""Class of input / outputs""" """Class of input / outputs"""
def __init__(self,owner,name,theType,nbSamples): def __init__(self,owner,name,theType,nbSamples):
self._theType = theType self._theType = theType
# For cycle static scheduling it may also be
# a list of samples for each iteration of a cycle
self._nbSamples = nbSamples self._nbSamples = nbSamples
self._owner = owner self._owner = owner
self._name = name self._name = name
self._fifo = [] self._fifo = []
self.constantNode = None self.constantNode = None
# For cyclo static scheduling nbSamples is a list
# and when simulating the schedule we need to know
# where we currently are in the list
self._cyclePosition = 0
# For cyclo static scheduling we advance the position in the
# cycle
def advanceCycle(self):
if isinstance(self.nbSamples,int):
pass
else:
self._cyclePosition = self._cyclePosition + 1
if self._cyclePosition == len(self.nbSamples):
self._cyclePosition = 0
# For cyclo static scheduling, get the value in the current
# cycle position
@property
def cycleValue(self):
if isinstance(self.nbSamples,int):
return(self.nbSamples)
else:
return(self.nbSamples[self._cyclePosition])
# For cyclo static scheduling: we need
# the length of the cycle
@property
def cyclePeriod(self):
if isinstance(self.nbSamples,int):
return(1)
else:
return(len(self.nbSamples))
# For cyclo static scheduling we need the total
# data generated by a run of the cycle
@property
def cycleTotal(self):
if isinstance(self.nbSamples,int):
return(self.nbSamples)
else:
return(np.sum(self.nbSamples))
# For cyclo static we need the max in a period top use
# as a normalization factor to identify the most filled
# FIFO
@property
def cycleMax(self):
if isinstance(self.nbSamples,int):
return(self.nbSamples)
else:
return(np.max(self.nbSamples))
@property @property
def fifo(self): def fifo(self):
return self._fifo return self._fifo
@ -182,6 +243,15 @@ class BaseNode:
# Literal arguments # Literal arguments
self.schedArgs=None self.schedArgs=None
# For cyclo static scheduling
# It is the LCM of the cycles of all
# connected edges.
# After this period, the node and its edges
# should be back to the original state
self.cyclePeriod = 1
self._positionInCycle = 0
def __getattr__(self,name): def __getattr__(self,name):
"""Present inputs / outputs as attributes""" """Present inputs / outputs as attributes"""
if name in self._inputs: if name in self._inputs:
@ -198,6 +268,33 @@ class BaseNode:
return(self._outputs[name]) return(self._outputs[name])
raise IndexError raise IndexError
# For cyclo static scheduling we need to compute
# the cycle period for this node based upon the
# period of the IOs
def computeCyclePeriod(self):
allLengths = []
for k in self._inputs:
allLengths.append(self._inputs[k].cyclePeriod)
for k in self._outputs:
allLengths.append(self._outputs[k].cyclePeriod)
if len(allLengths)>1:
self.cyclePeriod = ilcm(*allLengths)
else:
self.cyclePeriod = allLengths[0]
# For cyclo static scheduling, we need to track the number
# of period execution
# We return 1 when a period has been executed
# and this is used to decrease the period count in
# the null vector of the toplogy matrix
def executeNode(self):
self._positionInCycle = self._positionInCycle + 1
if (self._positionInCycle == self.cyclePeriod):
self._positionInCycle = 0
return(1)
else:
return(0)
def addLiteralArg(self,l): def addLiteralArg(self,l):
if self.schedArgs: if self.schedArgs:
self.schedArgs.append(ArgLiteral(l)) self.schedArgs.append(ArgLiteral(l))
@ -279,11 +376,24 @@ class BaseNode:
# Use ordered io names # Use ordered io names
for io in self.inputNames: for io in self.inputNames:
x = self._inputs[io] x = self._inputs[io]
ios.append("%s,%d" % (x.ctype,x.nbSamples)) # For cyclo static scheduling, we may have a list
# of samples
if isinstance(x.nbSamples,int):
ios.append("%s,%d" % (x.ctype,x.nbSamples))
else:
# In case of a list of samples
# thne templaet is receiving only the
# max value
m = np.max(x.nbSamples)
ios.append("%s,%d" % (x.ctype,m))
for io in self.outputNames: for io in self.outputNames:
x = self._outputs[io] x = self._outputs[io]
ios.append("%s,%d" % (x.ctype,x.nbSamples)) if isinstance(x.nbSamples,int):
ios.append("%s,%d" % (x.ctype,x.nbSamples))
else:
m = np.max(x.nbSamples)
ios.append("%s,%d" % (x.ctype,m))
return("".join(joinit(ios,","))) return("".join(joinit(ios,",")))
@ -297,13 +407,24 @@ class BaseNode:
""" """
ios=[] ios=[]
# Use ordered io names # Use ordered io names
# For cyclo static scheduling, the nbSamples
# may be a list and we use the max value
# for the code generayion
for io in self.inputNames: for io in self.inputNames:
x = self._inputs[io] x = self._inputs[io]
ios.append("%d" % x.nbSamples) if isinstance(x.nbSamples,int):
ios.append("%d" % x.nbSamples)
else:
m = np.max(x.nbSamples)
ios.append("%d" % m)
for io in self.outputNames: for io in self.outputNames:
x = self._outputs[io] x = self._outputs[io]
ios.append("%d" % x.nbSamples) if isinstance(x.nbSamples,int):
ios.append("%d" % x.nbSamples)
else:
m = np.max(x.nbSamples)
ios.append("%d" % m)
return("".join(joinit(ios,","))) return("".join(joinit(ios,",")))
@ -589,7 +710,12 @@ class GenericFunction(GenericNode):
tempgen.append("%s,input%dSize" % (x.ctype,inputid)) tempgen.append("%s,input%dSize" % (x.ctype,inputid))
constructortypes.append("FIFOBase<%s> &src%d" % (x.ctype,inputid)) constructortypes.append("FIFOBase<%s> &src%d" % (x.ctype,inputid))
genericconstructorargs.append("src%d" % inputid) genericconstructorargs.append("src%d" % inputid)
ios.append("%s,%d" % (x.ctype,x.nbSamples)) # For cyclo static scheduling, nbSamples may be a list
if isinstance(x.nbSamples,int):
ios.append("%s,%d" % (x.ctype,x.nbSamples))
else:
m = np.max(x.nbSamples)
ios.append("%s,%d" % (x.ctype,m))
typenameID = typenameID + 1 typenameID = typenameID + 1
for io in self.outputNames: for io in self.outputNames:
@ -602,7 +728,11 @@ class GenericFunction(GenericNode):
tempgen.append("%s,output%dSize" % (x.ctype,outputid)) tempgen.append("%s,output%dSize" % (x.ctype,outputid))
constructortypes.append("FIFOBase<%s> &dst%d" % (x.ctype,outputid)) constructortypes.append("FIFOBase<%s> &dst%d" % (x.ctype,outputid))
genericconstructorargs.append("dst%d" % outputid) genericconstructorargs.append("dst%d" % outputid)
ios.append("%s,%d" % (x.ctype,x.nbSamples)) if isinstance(x.nbSamples,int):
ios.append("%s,%d" % (x.ctype,x.nbSamples))
else:
m = np.max(x.nbSamples)
ios.append("%s,%d" % (x.ctype,m))
typenameID = typenameID + 1 typenameID = typenameID + 1
self._realInputs = inputid self._realInputs = inputid
@ -697,7 +827,13 @@ class GenericFunction(GenericNode):
theType=self._inputs[self.inputNames[0]].ctype theType=self._inputs[self.inputNames[0]].ctype
else: else:
theType=self._inputs[self.inputNames[0]].nptype theType=self._inputs[self.inputNames[0]].nptype
theLen = self._inputs[self.inputNames[0]].nbSamples # For cyclo static scheduling, nbSamples may be a list
# and in this case we are uding the mac value
nbSamples = self._inputs[self.inputNames[0]].nbSamples
if isinstance(nbSamples,int):
theLen = self._inputs[self.inputNames[0]].nbSamples
else:
theLen = np.max(nbSamples)
theId = 0 theId = 0
# List of buffer and corresponding fifo to initialize buffers # List of buffer and corresponding fifo to initialize buffers
inputs=[] inputs=[]

@ -1,3 +1,4 @@
{% extends "commonc.cpp" %}
/* /*
Generated with CMSIS-DSP Compute Graph Scripts. Generated with CMSIS-DSP Compute Graph Scripts.
@ -21,50 +22,10 @@ The support classes and code is covered by CMSIS-DSP license.
{% if config.cOptionalArgs %},{{config.cOptionalArgs}}{% endif %} {% if config.cOptionalArgs %},{{config.cOptionalArgs}}{% endif %}
{% endmacro -%} {% endmacro -%}
/*********** {% block schedArray %}
{% endblock %}
FIFO buffers {% block scheduleLoop %}
************/
{% 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 %}
uint32_t {{config.schedName}}(int *error{{optionalargs()}})
{
int cgStaticError=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}});
{% endif %}
{% endfor %}
/* Run several schedule iterations */
{% if config.debug %} {% if config.debug %}
while((cgStaticError==0) && (debugCounter > 0)) while((cgStaticError==0) && (debugCounter > 0))
{% else %} {% else %}
@ -88,6 +49,5 @@ uint32_t {{config.schedName}}(int *error{{optionalargs()}})
{% endif %} {% endif %}
nbSchedule++; nbSchedule++;
} }
*error=cgStaticError;
return(nbSchedule); {% endblock %}
}

@ -0,0 +1,46 @@
{% extends "commonc.cpp" %}
{% block schedArray %}
/*
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}}
};
{% endblock %}
{% block scheduleLoop %}
{% if config.debug %}
while((cgStaticError==0) && (debugCounter > 0))
{% else %}
while(cgStaticError==0)
{% endif %}
{
/* Run a schedule iteration */
for(unsigned long id=0 ; id < {{schedLen}}; id++)
{
switch(schedule[id])
{
{% for nodeID in range(nbNodes) -%}
case {{nodeID}}:
{
{{nodes[nodeID].cRun()}}
CHECKERROR;
}
break;
{% endfor %}default:
break;
}
}
{% if config.debug %}
debugCounter--;
{% endif %}
nbSchedule++;
}
{% endblock %}

@ -0,0 +1,75 @@
/*
Generated with CMSIS-DSP Compute Graph Scripts.
The generated code is not covered by CMSIS-DSP license.
The support classes and code is covered by CMSIS-DSP license.
*/
{% 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 -%}
{% block schedArray %}
{% endblock %}
/***********
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 %}
uint32_t {{config.schedName}}(int *error{{optionalargs()}})
{
int cgStaticError=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}});
{% endif %}
{% endfor %}
/* Run several schedule iterations */
{% block scheduleLoop %}
{% endblock %}
*error=cgStaticError;
return(nbSchedule);
}
Loading…
Cancel
Save