diff --git a/ComputeGraph/Async.md b/ComputeGraph/Async.md index b61c75d9..6780d7f3 100644 --- a/ComputeGraph/Async.md +++ b/ComputeGraph/Async.md @@ -1,5 +1,7 @@ # Dynamic Data Flow +This feature is illustrated in the [Example 10 : The dynamic dataflow mode](examples/example10/README.md) + Versions of the compute graph corresponding to CMSIS-DSP Version >= `1.14.3` and Python wrapper version >= `1.10.0` are supporting a new dynamic / asynchronous mode. With a dynamic flow, the flow of data is potentially changing at each execution. The IOs can generate or consume a different amount of data at each execution of their node (including no data). diff --git a/ComputeGraph/CycloStatic.md b/ComputeGraph/CycloStatic.md index db57be3b..aefb01a5 100644 --- a/ComputeGraph/CycloStatic.md +++ b/ComputeGraph/CycloStatic.md @@ -1,5 +1,7 @@ # Cyclo static scheduling +This feature is illustrated in the [cyclo](examples/cyclo/README.md) example. + Beginning with the version `1.7.0` of the Python wrapper and version >= `1.12` of CMSIS-DSP, cyclo static scheduling has been added. ## What is the problem it is trying to solve ? diff --git a/ComputeGraph/README.md b/ComputeGraph/README.md index 022d2bf8..95d2522b 100644 --- a/ComputeGraph/README.md +++ b/ComputeGraph/README.md @@ -4,7 +4,11 @@ 1. ### [Introduction](Introduction.md) -2. ### [How to get started](examples/simple/README.md) +2. ### How to get started + + 1. [Simple graph creation example](examples/simple/README.md) + + 2. [Simple graph creation example with CMSIS-DSP](examples/simpledsp/README.md) 3. ### [Examples](examples/README.md) diff --git a/ComputeGraph/documentation/Generic.md b/ComputeGraph/documentation/Generic.md index b1e414b5..f867c7d3 100644 --- a/ComputeGraph/documentation/Generic.md +++ b/ComputeGraph/documentation/Generic.md @@ -1,6 +1,8 @@ -# Generic Nodes +# Generic and functions bodes -The generic node classes are used to build new kind of nodes. There are 3 classes provided by the framework : +The generic and function nodes are the basic nodes that you use to create other kind of nodes in the graph. + +There are 3 generic classes provided by the framework to be used to create new nodes : * `GenericSource` * `GenericNode` @@ -8,6 +10,14 @@ The generic node classes are used to build new kind of nodes. There are 3 classe They are defined in `cmsisdsp.cg.scheduler` +There are 3 other classes that can be used to create new nodes from functions: + +* `Unary` +* `Binary` +* `Dsp` + +## Generic Nodes + Any new kind of node must inherit from one of those classes. Those classes are providing the methods `addInput` and/or `addOutput` to define new IOs. The method `typeName` from the parent class must be overridden. @@ -28,7 +38,7 @@ class ProcessingNode(GenericNode): See the [simple](../examples/simple/README.md) example for more explanation about how to define a new node. -## Methods +### Methods The constructor of the node is using the `addInput` and/or `addOutput` to define new IOs. @@ -56,7 +66,7 @@ def typeName(self): This method defines the name of the C++ class implementing the wrapper for this node. -## Datatypes +### Datatypes Datatypes for the IOs are inheriting from `CGStaticType`. @@ -65,7 +75,7 @@ Currently there are two classes defined: * `CType` for the standard CMSIS-DSP types * `CStructType` for a C struct -### CType +#### CType You create such a type with `CType(id)` where `id` is one of the constant coming from the Python wrapper: @@ -84,7 +94,7 @@ You create such a type with `CType(id)` where `id` is one of the constant coming For instance, to define a `float32_t` type for an IO you can use `CType(F32)` -### CStructType +#### CStructType The constructor has the following definition @@ -100,4 +110,20 @@ In Python, there is no `struct`. This datatype is mapped to an object. Object ha As consequence, in Python side you should never copy those structs since it would copy the reference. You should instead copy the members of the struct. -If you don't plan on generating a Python scheduler, you can just use whatever name you want for the `python_name`. It will be ignored by the C++ code generation. \ No newline at end of file +If you don't plan on generating a Python scheduler, you can just use whatever name you want for the `python_name`. It will be ignored by the C++ code generation. + +## Function and constant nodes + +A Compute graph C++ wrapper is useful when the software components you use have a state that needs to be initialized in the C++ constructor, and preserved between successive calls to the `run` method of the wrapper. + +Most CMSIS-DSP functions have no state. The compute graph framework is providing some ways to easily use functions in the graph without having to write a wrapper. + +This feature is relying on the nodes: + +* `Unary` +* `Binary` +* `Dsp` +* `Constant` + +All of this is explained in detail in the [simple example with CMSIS-DSP](../examples/simpledsp/README.md). + diff --git a/ComputeGraph/documentation/PythonAPI.md b/ComputeGraph/documentation/PythonAPI.md index 98a5151e..3d047f50 100644 --- a/ComputeGraph/documentation/PythonAPI.md +++ b/ComputeGraph/documentation/PythonAPI.md @@ -4,7 +4,7 @@ Python APIs to describe the nodes and graph and generate the C++, Python or Grap 1. ## [Graph class](Graph.md) -2. ## [Generic Node, Source and Sink classes](Generic.md) +2. ## [Generic and function nodes](Generic.md) 3. ## Scheduler diff --git a/ComputeGraph/examples/CMakeLists.txt b/ComputeGraph/examples/CMakeLists.txt index 7683e7b4..3d43aa5e 100644 --- a/ComputeGraph/examples/CMakeLists.txt +++ b/ComputeGraph/examples/CMakeLists.txt @@ -74,6 +74,8 @@ add_subdirectory(example8 bin_example8) add_subdirectory(example9 bin_example9) add_subdirectory(example10 bin_example10) add_subdirectory(simple bin_simple) +add_subdirectory(simpledsp bin_simpledsp) +add_subdirectory(cyclo bin_cyclo) # Python examples add_subdirectory(example4 bin_example4) diff --git a/ComputeGraph/examples/README.md b/ComputeGraph/examples/README.md index f4c3ebd7..064beae3 100644 --- a/ComputeGraph/examples/README.md +++ b/ComputeGraph/examples/README.md @@ -48,8 +48,9 @@ python main.py # List of examples -* [Simple example](simple/README.md) : How to get started -* [Example 1](example1/README.md) : Same as the simple example but explaining how to add arguments to the scheduler API and node constructors. This example is also giving a very detailed explanation of the C++ code generated for the scheduler +* [Simple example without CMSIS-DSP](simple/README.md) : **How to get started** +* [Simple example with CMSIS-DSP](simpledsp/README.md) : **How to get started with CMSIS-DSP** +* [Example 1](example1/README.md) : Same as the simple example but explaining how to add arguments to the scheduler API and node constructors. This example is also giving a **detailed explanation of the C++ code** generated for the scheduler * [Example 2](example2/README.md) : Explain how to use CMSIS-DSP pure functions (no state) and add delay on the arcs of the graph. Explain some configuration options for the schedule generation. * [Example 3](example3/README.md) : A full signal processing example with CMSIS-DSP using FFT and sliding windows and overlap and add node * [Example 4](example4/README.md) : Same as example 3 but where we generate a Python implementation rather than a C++ implementation. The resulting graph can be executed thanks to the CMSIS-DSP Python wrapper @@ -59,4 +60,5 @@ python main.py * [Example 8](example8/README.md) : Introduce structured datatype for the samples and implicit `Duplicate` nodes for the graph * [Example 9](example9/README.md) : Check that duplicate nodes and arc delays are working together and a scheduling is generated * [Example 10 : The dynamic dataflow mode](example10/README.md) +* [Cyclo-static scheduling](cyclo/README.md) diff --git a/ComputeGraph/examples/cyclo/AppNodes.h b/ComputeGraph/examples/cyclo/AppNodes.h new file mode 100644 index 00000000..a35f43e7 --- /dev/null +++ b/ComputeGraph/examples/cyclo/AppNodes.h @@ -0,0 +1,158 @@ +/* ---------------------------------------------------------------------- + * Project: CMSIS DSP Library + * Title: AppNodes.h + * Description: Application nodes for Example 1 + * + * $Date: 29 July 2021 + * $Revision: V1.10.0 + * + * Target Processor: Cortex-M and Cortex-A cores + * -------------------------------------------------------------------- */ +/* + * Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _APPNODES_H_ +#define _APPNODES_H_ + +#include + +template +class Sink: public GenericSink +{ +public: + Sink(FIFOBase &src):GenericSink(src){}; + + int prepareForRunning() final + { + if (this->willUnderflow()) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + + int run() final + { + IN *b=this->getReadBuffer(); + printf("Sink\n"); + for(int i=0;i +class Source: public GenericSource +{ +public: + Source(FIFOBase &dst):GenericSource(dst), + mPeriod(0),mValuePeriodStart(0){}; + + int getSamplesForPeriod() const + { + if (mPeriod == 0) + { + return(3); + } + return(2); + } + + void updatePeriod(){ + mPeriod++; + mValuePeriodStart = 3; + if (mPeriod == 2) + { + mPeriod = 0; + mValuePeriodStart = 0; + } + } + + int prepareForRunning() final + { + /* Cyclo static scheduling do not make sense in + asynchronous mode so the default outputSize is used. + This function is never used in cyclo-static scheduling + */ + if (this->willOverflow()) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + + int run() final{ + OUT *b=this->getWriteBuffer(getSamplesForPeriod()); + + printf("Source\n"); + for(int i=0;i +class ProcessingNode; + + +template +class ProcessingNode: + public GenericNode +{ +public: + ProcessingNode(FIFOBase &src, + FIFOBase &dst):GenericNode(src,dst){}; + + int prepareForRunning() final + { + if (this->willOverflow() || + this->willUnderflow()) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + + int run() final{ + printf("ProcessingNode\n"); + IN *a=this->getReadBuffer(); + IN *b=this->getWriteBuffer(); + for(int i=0;igetWriteBuffer(getSamplesForPeriod()); + +printf("Source\n"); +for(int i=0;i +class Source: public GenericSource +``` + +`outputSize` cannot be the list `[3,2]`. + +The generated code is using the max of the values, so here `3`: + +```C++ +Source source(fifo0); +``` + +## Expected output: + +``` +Schedule length = 26 +Memory usage 88 bytes +``` + +The schedule length is `26` compared to `19` for the simple example where source is generating samples by packet of 5. The source node executions must be a multiple of 2 in this graph because the period of sample generation has length 2. In the original graph, the number of executions could be an odd number. That's why there are more executions in this cyclo-static scheduling. + +The memory usage (FIFO) is the same as the one for the simple example without cyclo-static scheduling. + +The expected output of the execution is still 1,2,3,4,5,1,2,3,4,5 ... but the scheduling is different. There are more source executions. + +``` +Start +Source +Source +Source +ProcessingNode +Sink +1 +2 +3 +4 +5 +Source +Source +Source +ProcessingNode +Sink +1 +2 +3 +4 +5 +Source +Source +Source +ProcessingNode +Sink +1 +2 +3 +4 +5 +Sink +1 +2 +3 +4 +5 +Source +Source +ProcessingNode +Sink +1 +2 +3 +4 +5 +Source +Source +Source +ProcessingNode +Sink +1 +2 +3 +4 +5 +Sink +1 +2 +3 +4 +5 +``` + diff --git a/ComputeGraph/examples/cyclo/create.py b/ComputeGraph/examples/cyclo/create.py new file mode 100644 index 00000000..edbf5676 --- /dev/null +++ b/ComputeGraph/examples/cyclo/create.py @@ -0,0 +1,31 @@ +# Include definition of the nodes +from nodes import * +# Include definition of the graph +from graph import * + +# Create a configuration object +conf=Configuration() +# The number of schedule iteration is limited to 1 +# to prevent the scheduling from running forever +# (which should be the case for a stream computation) +conf.debugLimit=1 +# Disable inclusion of CMSIS-DSP headers so that we don't have +# to recompile CMSIS-DSP for such a simple example +conf.CMSISDSP = False + +# Compute a static scheduling of the graph +# The size of FIFO is also computed +scheduling = the_graph.computeSchedule(config=conf) + +# Print some statistics about the compute schedule +# and the memory usage +print("Schedule length = %d" % scheduling.scheduleLength) +print("Memory usage %d bytes" % scheduling.memory) + +# Generate the C++ code for the static scheduler +scheduling.ccode("generated",conf) + +# Generate a graphviz representation of the graph +with open("cyclo.dot","w") as f: + scheduling.graphviz(f) + diff --git a/ComputeGraph/examples/cyclo/custom.h b/ComputeGraph/examples/cyclo/custom.h new file mode 100644 index 00000000..41b6c6ee --- /dev/null +++ b/ComputeGraph/examples/cyclo/custom.h @@ -0,0 +1,5 @@ +#ifndef _CUSTOM_H_ + +typedef float float32_t; + +#endif \ No newline at end of file diff --git a/ComputeGraph/examples/cyclo/cyclo.dot b/ComputeGraph/examples/cyclo/cyclo.dot new file mode 100644 index 00000000..cefab4f4 --- /dev/null +++ b/ComputeGraph/examples/cyclo/cyclo.dot @@ -0,0 +1,48 @@ + + + + +digraph structs { + node [shape=plaintext] + rankdir=LR + edge [arrowsize=0.5] + fontname="times" + + +processing [label=< + + + + +
processing
(ProcessingNode)
>]; + +sink [label=< + + + + +
sink
(Sink)
>]; + +source [label=< + + + + +
source
(Source)
>]; + + + +source:i -> processing:i [label="f32(11)" +,headlabel=<
7 +
> +,taillabel=<
[3, 2] +
>] + +processing:i -> sink:i [label="f32(11)" +,headlabel=<
5 +
> +,taillabel=<
7 +
>] + + +} diff --git a/ComputeGraph/examples/cyclo/cyclo.exe b/ComputeGraph/examples/cyclo/cyclo.exe new file mode 100644 index 00000000..cfa0d79a Binary files /dev/null and b/ComputeGraph/examples/cyclo/cyclo.exe differ diff --git a/ComputeGraph/examples/cyclo/cyclo.ilk b/ComputeGraph/examples/cyclo/cyclo.ilk new file mode 100644 index 00000000..624ef808 Binary files /dev/null and b/ComputeGraph/examples/cyclo/cyclo.ilk differ diff --git a/ComputeGraph/examples/cyclo/cyclo.pdb b/ComputeGraph/examples/cyclo/cyclo.pdb new file mode 100644 index 00000000..5d4eb75d Binary files /dev/null and b/ComputeGraph/examples/cyclo/cyclo.pdb differ diff --git a/ComputeGraph/examples/cyclo/cyclo.pdf b/ComputeGraph/examples/cyclo/cyclo.pdf new file mode 100644 index 00000000..e8a51280 Binary files /dev/null and b/ComputeGraph/examples/cyclo/cyclo.pdf differ diff --git a/ComputeGraph/examples/cyclo/docassets/cyclo.png b/ComputeGraph/examples/cyclo/docassets/cyclo.png new file mode 100644 index 00000000..97b1f740 Binary files /dev/null and b/ComputeGraph/examples/cyclo/docassets/cyclo.png differ diff --git a/ComputeGraph/examples/cyclo/generated/scheduler.cpp b/ComputeGraph/examples/cyclo/generated/scheduler.cpp new file mode 100644 index 00000000..5eb21ab0 --- /dev/null +++ b/ComputeGraph/examples/cyclo/generated/scheduler.cpp @@ -0,0 +1,170 @@ +/* + +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. + +*/ + + +#include "custom.h" +#include "GenericNodes.h" +#include "AppNodes.h" +#include "scheduler.h" + +#if !defined(CHECKERROR) +#define CHECKERROR if (cgStaticError < 0) \ + {\ + goto errorHandling;\ + } + +#endif + +#if !defined(CG_BEFORE_ITERATION) +#define CG_BEFORE_ITERATION +#endif + +#if !defined(CG_AFTER_ITERATION) +#define CG_AFTER_ITERATION +#endif + +#if !defined(CG_BEFORE_SCHEDULE) +#define CG_BEFORE_SCHEDULE +#endif + +#if !defined(CG_AFTER_SCHEDULE) +#define CG_AFTER_SCHEDULE +#endif + +#if !defined(CG_BEFORE_BUFFER) +#define CG_BEFORE_BUFFER +#endif + +#if !defined(CG_BEFORE_FIFO_BUFFERS) +#define CG_BEFORE_FIFO_BUFFERS +#endif + +#if !defined(CG_BEFORE_FIFO_INIT) +#define CG_BEFORE_FIFO_INIT +#endif + +#if !defined(CG_BEFORE_NODE_INIT) +#define CG_BEFORE_NODE_INIT +#endif + +#if !defined(CG_AFTER_INCLUDES) +#define CG_AFTER_INCLUDES +#endif + +#if !defined(CG_BEFORE_SCHEDULER_FUNCTION) +#define CG_BEFORE_SCHEDULER_FUNCTION +#endif + +#if !defined(CG_BEFORE_NODE_EXECUTION) +#define CG_BEFORE_NODE_EXECUTION +#endif + +#if !defined(CG_AFTER_NODE_EXECUTION) +#define CG_AFTER_NODE_EXECUTION +#endif + +CG_AFTER_INCLUDES + + +/* + +Description of the scheduling. + +*/ +static unsigned int schedule[26]= +{ +2,2,2,0,1,2,2,2,0,1,2,2,2,0,1,1,2,2,0,1,2,2,2,0,1,1, +}; + +CG_BEFORE_FIFO_BUFFERS +/*********** + +FIFO buffers + +************/ +#define FIFOSIZE0 11 +#define FIFOSIZE1 11 + +#define BUFFERSIZE1 11 +CG_BEFORE_BUFFER +float32_t buf1[BUFFERSIZE1]={0}; + +#define BUFFERSIZE2 11 +CG_BEFORE_BUFFER +float32_t buf2[BUFFERSIZE2]={0}; + + +CG_BEFORE_SCHEDULER_FUNCTION +uint32_t scheduler(int *error) +{ + int cgStaticError=0; + uint32_t nbSchedule=0; + int32_t debugCounter=1; + + CG_BEFORE_FIFO_INIT; + /* + Create FIFOs objects + */ + FIFO fifo0(buf1); + FIFO fifo1(buf2); + + CG_BEFORE_NODE_INIT; + /* + Create node objects + */ + ProcessingNode processing(fifo0,fifo1); + Sink sink(fifo1); + Source source(fifo0); + + /* Run several schedule iterations */ + CG_BEFORE_SCHEDULE; + while((cgStaticError==0) && (debugCounter > 0)) + { + /* Run a schedule iteration */ + CG_BEFORE_ITERATION; + for(unsigned long id=0 ; id < 26; id++) + { + CG_BEFORE_NODE_EXECUTION; + + switch(schedule[id]) + { + case 0: + { + cgStaticError = processing.run(); + } + break; + + case 1: + { + cgStaticError = sink.run(); + } + break; + + case 2: + { + cgStaticError = source.run(); + } + break; + + default: + break; + } + CG_AFTER_NODE_EXECUTION; + CHECKERROR; + } + debugCounter--; + CG_AFTER_ITERATION; + nbSchedule++; + } + +errorHandling: + CG_AFTER_SCHEDULE; + *error=cgStaticError; + return(nbSchedule); +} diff --git a/ComputeGraph/examples/cyclo/generated/scheduler.h b/ComputeGraph/examples/cyclo/generated/scheduler.h new file mode 100644 index 00000000..d98d9e63 --- /dev/null +++ b/ComputeGraph/examples/cyclo/generated/scheduler.h @@ -0,0 +1,26 @@ +/* + +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. + +*/ + +#ifndef _SCHEDULER_H_ +#define _SCHEDULER_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + + +extern uint32_t scheduler(int *error); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/ComputeGraph/examples/cyclo/graph.py b/ComputeGraph/examples/cyclo/graph.py new file mode 100644 index 00000000..0cd811e7 --- /dev/null +++ b/ComputeGraph/examples/cyclo/graph.py @@ -0,0 +1,39 @@ +# Include definitions from the Python package to +# define datatype for the IOs and to have access to the +# Graph class +from cmsisdsp.cg.scheduler import * +# Include definition of the nodes +from nodes import * + +# Define the datatype we are using for all the IOs in this +# example +floatType=CType(F32) + +# Instantiate a Source node with a float datatype and +# working with packet of 5 samples (each execution of the +# source in the C code will generate 5 samples) +# "source" is the name of the C variable that will identify +# this node +src=Source("source",floatType,[3,2]) +# Instantiate a Processing node using a float data type for +# both the input and output. The number of samples consumed +# on the input and produced on the output is 7 each time +# the node is executed in the C code +# "processing" is the name of the C variable that will identify +# this node +processing=ProcessingNode("processing",floatType,7,7) +# Instantiate a Sink node with a float datatype and consuming +# 5 samples each time the node is executed in the C code +# "sink" is the name of the C variable that will identify +# this node +sink=Sink("sink",floatType,5) + +# Create a Graph object +the_graph = Graph() + +# Connect the source to the processing node +the_graph.connect(src.o,processing.i) +# Connect the processing node to the sink +the_graph.connect(processing.o,sink.i) + + diff --git a/ComputeGraph/examples/cyclo/main.cpp b/ComputeGraph/examples/cyclo/main.cpp new file mode 100644 index 00000000..a1fd4028 --- /dev/null +++ b/ComputeGraph/examples/cyclo/main.cpp @@ -0,0 +1,11 @@ +#include +#include +#include "scheduler.h" + +int main(int argc, char const *argv[]) +{ + int error; + printf("Start\n"); + uint32_t nbSched=scheduler(&error); + return 0; +} \ No newline at end of file diff --git a/ComputeGraph/examples/cyclo/main.obj b/ComputeGraph/examples/cyclo/main.obj new file mode 100644 index 00000000..281b094c Binary files /dev/null and b/ComputeGraph/examples/cyclo/main.obj differ diff --git a/ComputeGraph/examples/cyclo/nodes.py b/ComputeGraph/examples/cyclo/nodes.py new file mode 100644 index 00000000..6f5a5987 --- /dev/null +++ b/ComputeGraph/examples/cyclo/nodes.py @@ -0,0 +1,77 @@ +# Include definitions from the Python package +from cmsisdsp.cg.scheduler import GenericNode,GenericSink,GenericSource + +### Define new types of Nodes + +class ProcessingNode(GenericNode): + """ + Definition of a ProcessingNode for the graph + + Parameters + ---------- + name : str + Name of the C variable identifying this node + in the C code + theType : CGStaticType + The datatype for the input and output + inLength : int + The number of samples consumed by input + outLength : int + The number of samples produced on output + """ + def __init__(self,name,theType,inLength,outLength): + GenericNode.__init__(self,name) + self.addInput("i",theType,inLength) + self.addOutput("o",theType,outLength) + + @property + def typeName(self): + """The name of the C++ class implementing this node""" + return "ProcessingNode" + +class Sink(GenericSink): + """ + Definition of a Sink node for the graph + + Parameters + ---------- + name : str + Name of the C variable identifying this node + in the C code + theType : CGStaticType + The datatype for the input + inLength : int + The number of samples consumed by input + """ + def __init__(self,name,theType,inLength): + GenericSink.__init__(self,name) + self.addInput("i",theType,inLength) + + @property + def typeName(self): + """The name of the C++ class implementing this node""" + return "Sink" + +class Source(GenericSource): + """ + Definition of a Source node for the graph + + Parameters + ---------- + name : str + Name of the C variable identifying this node + in the C code + theType : CGStaticType + The datatype for the output + outLength : int + The number of samples produced on output + """ + def __init__(self,name,theType,outLength): + GenericSource.__init__(self,name) + self.addOutput("o",theType,outLength) + + @property + def typeName(self): + """The name of the C++ class implementing this node""" + return "Source" + diff --git a/ComputeGraph/examples/cyclo/scheduler.obj b/ComputeGraph/examples/cyclo/scheduler.obj new file mode 100644 index 00000000..e492dcc1 Binary files /dev/null and b/ComputeGraph/examples/cyclo/scheduler.obj differ diff --git a/ComputeGraph/examples/cyclo/vc140.pdb b/ComputeGraph/examples/cyclo/vc140.pdb new file mode 100644 index 00000000..2d98e8d6 Binary files /dev/null and b/ComputeGraph/examples/cyclo/vc140.pdb differ diff --git a/ComputeGraph/examples/example2/README.md b/ComputeGraph/examples/example2/README.md index 265bceda..6cfc1cc8 100644 --- a/ComputeGraph/examples/example2/README.md +++ b/ComputeGraph/examples/example2/README.md @@ -2,11 +2,11 @@ Please refer to the [simple example](../simple/README.md) to have an overview of how to define a graph and it nodes and how to generate the C++ code for the static scheduler. +The [simple example with CMSIS-DSP](../simpledsp/README.md) is giving more details about `Constant` nodes and CMSIS-DSP functions in the compute graph. + In this example. we are just analyzing a much more complex example to see some new features: - Delay -- CMSIS-DSP function -- Constant node - SlidingBuffer This example is not really using a MFCC or a TensorFlow Lite node. It is just providing some wrappers to show how such a nodes could be included in a graph: diff --git a/ComputeGraph/examples/simpledsp/AppNodes.h b/ComputeGraph/examples/simpledsp/AppNodes.h new file mode 100644 index 00000000..f5f65c23 --- /dev/null +++ b/ComputeGraph/examples/simpledsp/AppNodes.h @@ -0,0 +1,128 @@ +/* ---------------------------------------------------------------------- + * Project: CMSIS DSP Library + * Title: AppNodes.h + * Description: Application nodes for Example 1 + * + * $Date: 29 July 2021 + * $Revision: V1.10.0 + * + * Target Processor: Cortex-M and Cortex-A cores + * -------------------------------------------------------------------- */ +/* + * Copyright (C) 2010-2021 ARM Limited or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _APPNODES_H_ +#define _APPNODES_H_ + +#include + +template +class Sink: public GenericSink +{ +public: + Sink(FIFOBase &src):GenericSink(src){}; + + int prepareForRunning() final + { + if (this->willUnderflow()) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + + int run() final + { + IN *b=this->getReadBuffer(); + printf("Sink\n"); + for(int i=0;i +class Source: public GenericSource +{ +public: + Source(FIFOBase &dst):GenericSource(dst){}; + + int prepareForRunning() final + { + if (this->willOverflow()) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + + int run() final{ + OUT *b=this->getWriteBuffer(); + + printf("Source\n"); + for(int i=0;i +class ProcessingNode; + + +template +class ProcessingNode: + public GenericNode +{ +public: + ProcessingNode(FIFOBase &src, + FIFOBase &dst):GenericNode(src,dst){}; + + int prepareForRunning() final + { + if (this->willOverflow() || + this->willUnderflow()) + { + return(CG_SKIP_EXECUTION_ID_CODE); // Skip execution + } + + return(0); + }; + + int run() final{ + printf("ProcessingNode\n"); + IN *a=this->getReadBuffer(); + IN *b=this->getWriteBuffer(); + for(int i=0;i fifo0(buf1); + FIFO fifo1(buf2); + + CG_BEFORE_NODE_INIT; + /* + Create node objects + */ + Sink sink(fifo1); + Source source(fifo0); + + /* Run several schedule iterations */ + CG_BEFORE_SCHEDULE; + while((cgStaticError==0) && (debugCounter > 0)) + { + /* Run a schedule iteration */ + CG_BEFORE_ITERATION; + for(unsigned long id=0 ; id < 19; id++) + { + CG_BEFORE_NODE_EXECUTION; + + switch(schedule[id]) + { + case 0: + { + + { + + float32_t* i0; + float32_t* o2; + i0=fifo0.getReadBuffer(7); + o2=fifo1.getWriteBuffer(7); + arm_offset_f32(i0,OFFSET_VALUE,o2,7); + cgStaticError = 0; + } + } + break; + + case 1: + { + cgStaticError = sink.run(); + } + break; + + case 2: + { + cgStaticError = source.run(); + } + break; + + default: + break; + } + CG_AFTER_NODE_EXECUTION; + CHECKERROR; + } + debugCounter--; + CG_AFTER_ITERATION; + nbSchedule++; + } + +errorHandling: + CG_AFTER_SCHEDULE; + *error=cgStaticError; + return(nbSchedule); +} diff --git a/ComputeGraph/examples/simpledsp/generated/scheduler.h b/ComputeGraph/examples/simpledsp/generated/scheduler.h new file mode 100644 index 00000000..d98d9e63 --- /dev/null +++ b/ComputeGraph/examples/simpledsp/generated/scheduler.h @@ -0,0 +1,26 @@ +/* + +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. + +*/ + +#ifndef _SCHEDULER_H_ +#define _SCHEDULER_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + + +extern uint32_t scheduler(int *error); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/ComputeGraph/examples/simpledsp/graph.py b/ComputeGraph/examples/simpledsp/graph.py new file mode 100644 index 00000000..e6e9c5dc --- /dev/null +++ b/ComputeGraph/examples/simpledsp/graph.py @@ -0,0 +1,41 @@ +# Include definitions from the Python package to +# define datatype for the IOs and to have access to the +# Graph class +from cmsisdsp.cg.scheduler import * +# Include definition of the nodes +from nodes import * + +# Define the datatype we are using for all the IOs in this +# example +floatType=CType(F32) + +# Instantiate a Source node with a float datatype and +# working with packet of 5 samples (each execution of the +# source in the C code will generate 5 samples) +# "source" is the name of the C variable that will identify +# this node +src=Source("source",floatType,5) +# Instantiate a Processing node using a float data type for +# both the input and output. The number of samples consumed +# on the input and produced on the output is 7 each time +# the node is executed in the C code +# "processing" is the name of the C variable that will identify +# this node +processing=Binary("arm_offset_f32",floatType,7) +offsetValue=Constant("OFFSET_VALUE") +# Instantiate a Sink node with a float datatype and consuming +# 5 samples each time the node is executed in the C code +# "sink" is the name of the C variable that will identify +# this node +sink=Sink("sink",floatType,5) + +# Create a Graph object +the_graph = Graph() + +# Connect the source to the processing node +the_graph.connect(src.o,processing.ia) +the_graph.connect(offsetValue,processing.ib) +# Connect the processing node to the sink +the_graph.connect(processing.o,sink.i) + + diff --git a/ComputeGraph/examples/simpledsp/main.cpp b/ComputeGraph/examples/simpledsp/main.cpp new file mode 100644 index 00000000..a1fd4028 --- /dev/null +++ b/ComputeGraph/examples/simpledsp/main.cpp @@ -0,0 +1,11 @@ +#include +#include +#include "scheduler.h" + +int main(int argc, char const *argv[]) +{ + int error; + printf("Start\n"); + uint32_t nbSched=scheduler(&error); + return 0; +} \ No newline at end of file diff --git a/ComputeGraph/examples/simpledsp/nodes.py b/ComputeGraph/examples/simpledsp/nodes.py new file mode 100644 index 00000000..bb82c160 --- /dev/null +++ b/ComputeGraph/examples/simpledsp/nodes.py @@ -0,0 +1,49 @@ +# Include definitions from the Python package +from cmsisdsp.cg.scheduler import GenericNode,GenericSink,GenericSource + +class Sink(GenericSink): + """ + Definition of a Sink node for the graph + + Parameters + ---------- + name : str + Name of the C variable identifying this node + in the C code + theType : CGStaticType + The datatype for the input + inLength : int + The number of samples consumed by input + """ + def __init__(self,name,theType,inLength): + GenericSink.__init__(self,name) + self.addInput("i",theType,inLength) + + @property + def typeName(self): + """The name of the C++ class implementing this node""" + return "Sink" + +class Source(GenericSource): + """ + Definition of a Source node for the graph + + Parameters + ---------- + name : str + Name of the C variable identifying this node + in the C code + theType : CGStaticType + The datatype for the output + outLength : int + The number of samples produced on output + """ + def __init__(self,name,theType,outLength): + GenericSource.__init__(self,name) + self.addOutput("o",theType,outLength) + + @property + def typeName(self): + """The name of the C++ class implementing this node""" + return "Source" + diff --git a/ComputeGraph/examples/simpledsp/simpledsp.dot b/ComputeGraph/examples/simpledsp/simpledsp.dot new file mode 100644 index 00000000..a268450a --- /dev/null +++ b/ComputeGraph/examples/simpledsp/simpledsp.dot @@ -0,0 +1,65 @@ + + + + +digraph structs { + node [shape=plaintext] + rankdir=LR + edge [arrowsize=0.5] + fontname="times" + + + +arm_offset_f321 [label=< + + + + + + + + + + + + +
iaarm_offset_f32
(Function)
o
ib
>]; + +sink [label=< + + + + +
sink
(Sink)
>]; + +source [label=< + + + + +
source
(Source)
>]; + + + +source:i -> arm_offset_f321:ia [label="f32(11)" +,headlabel=<
7 +
> +,taillabel=<
5 +
>] + +arm_offset_f321:o -> sink:i [label="f32(11)" +,headlabel=<
5 +
> +,taillabel=<
7 +
>] + +OFFSET_VALUE [label=< + + + + +
OFFSET_VALUE
>]; + +OFFSET_VALUE:i -> arm_offset_f321:ib + +} diff --git a/ComputeGraph/examples/simpledsp/simpledsp.pdf b/ComputeGraph/examples/simpledsp/simpledsp.pdf new file mode 100644 index 00000000..1a519ed2 Binary files /dev/null and b/ComputeGraph/examples/simpledsp/simpledsp.pdf differ