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 :
There are 3 generic classes provided by the framework to be used to create new nodes.
To create a new kind of node, you inherit from one of those classes:
* `GenericSource`
* `GenericNode`
* `GenericSink`
They are defined in `cmsisdsp.cg.scheduler`
They are defined in `cmsisdsp.cg.scheduler`.
There are 3 other classes that can be used to create new nodes from functions:
There are 3 other classes that can be used to create new nodes from functions. A function has no state and a C++ wrapper is not required. In this case, the tool is generating code for calling the function directly rather than using a C++ wrapper.
* `Unary`
* `Binary`
* `Dsp`
* `Unary` (unary operators like `negate`, `inverse` ...)
* `Binary` (binary operators like `add`, `mul` ...)
* `Dsp` (Some CMSIS-DSP function either binary or unary)
# 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.
When you define a new kind of node, it must inherit from one of those classes. Those classes are providing the methods `addInput` and/or `addOutput` to define new inputs / outputs.
A new kind of node is generally defined as:
@ -36,6 +36,20 @@ class ProcessingNode(GenericNode):
return "ProcessingNode"
```
The method `typeName` from the parent class must be overridden and provide the name of the `C++` wrapper to be used for this node.
The object constructor is defining the inputs / outputs : number of samples and datatype.
The object constructor is also defining the name used to identity this node in the generated code (so it must be a valid C variable name).
`GenericSink` is only providing the `addInput` function.
`GenericSource` is only providing the `addOutput` function
`GenericNode` is providing both.
You can use each function as much as you want to create several inputs and / or several outputs for a node.
See the [simple](../examples/simple/README.md) example for more explanation about how to define a new node.
## Methods
@ -46,7 +60,7 @@ The constructor of the node is using the `addInput` and/or `addOutput` to define
def addInput(self,name,theType,theLength):
```
* `name` is the name of the input. It will becomes a property of the Python object so it must not conflict with existing properties. If `name` is, for instance, "i" then it can be accessed with `node.i` in the code
* `name` is the name of the input. It will becomes a property of the Python object so it must not conflict with existing properties. If `name` is, for instance, `"i"` then it can be accessed with `node.i` in the code
* `theType` is the datatype of the IO. It must inherit from `CGStaticType` (see below for more details about defining the types)
* `theLength` is the amount of **samples** consumed by this IO at each execution of the node
* `name` is the name of the input. It will becomes a property of the Python object so it must not conflict with existing properties. If `name` is, for instance, "o" then it can be accessed with `node.o` in the code
* `name` is the name of the output. It will becomes a property of the Python object so it must not conflict with existing properties. If `name` is, for instance, `"o"` then it can be accessed with `node.o` in the code
* `theType` is the datatype of the IO. It must inherit from `CGStaticType` (see below for more details about defining the types)
* `theLength` is the amount of **samples** produced by this IO at each execution of the node
@ -70,9 +84,9 @@ This method defines the name of the C++ class implementing the wrapper for this
Datatypes for the IOs are inheriting from `CGStaticType`.
Currently there are 3 classes defined:
Currently there are 3 classes defined in the project:
* `CType` for the standard CMSIS-DSP types
* `CType` for the standard CMSIS-DSP types like `q15_t`, `float32_t` ...
* `CStructType` for a C struct
* `PythonClassType` to create structured datatype for the Python scheduler
* `size_in_bytes` is the size of the struct. It should take into account padding. It is used in case of buffer sharing since the datatype of the shared buffer is `int8_t`. The Python script must be able to compute the size of those buffers and needs to know the size of the structure including padding.
* `size_in_bytes` is the size of the C struct. It should take into account padding. It is used when the compute graph memory optimization is used since size of the datatype is needed.
## PythonClassType
@ -125,9 +139,17 @@ Most CMSIS-DSP functions have no state. The compute graph framework is providing
This feature is relying on the nodes:
* `Unary`
* To use an unary operator like `negate`, `inverse` ...
* `Binary`
* To use a binary operator like `add`, `mul` ...
* `Dsp`
* Should detect if the CMSIS-DSP operator is unary or binary and use the datatype to compute the name of the function. In practice, only a subset of CMSIS-DSP function is supported so you should use `Unary` or `Binary` nodes
* `Constant`
* Special node to be used **only** with function nodes when some arguments cannot be connected to a FIFO. For instance, with `arm_scale_f32` the scaling factor is a scalar value and a FIFO cannot be connected to this argument. The function is a binary operator but between a stream and a scalar.
All of this is explained in detail in the [simple example with CMSIS-DSP](../examples/simpledsp/README.md).
@ -13,13 +13,14 @@ Typically this method is used as:
```python
the_graph = Graph()
# Connect the source output to the processing node input
# Connect the source output to the processing node input and add this directed
# edge to the object the_graph
the_graph.connect(src.o,processing.i)
```
There are two optional arguments:
There are two optional arguments for the `connect` function:
* `fifoClass` : To use a different C++ class for implementing the connection between the two IOs. (it is also possible to change the FIFO class globally by setting an option on the graph. See below). Only the `FIFO` class is provided by default. Any new implementation must inherit from `FIFObase<T>`
* `fifoClass` : To use a different C++ class for implementing the connection between the two IOs. (it is also possible to change the FIFO class globally by setting an option on the graph. See below). The `FIFO` class is provided by default. Any new implementation must inherit from `FIFObase<T>`
* `fifoScale` : In asynchronous mode, it is a scaling factor to increase the length of the FIFO compared to what has been computed by the synchronous approximation. This setting can also be set globally using the scheduler options. `fifoScale` is overriding the global setting. It must be a `float` (not an `int`).
```python
@ -55,3 +56,7 @@ Class used for FIFO by default. Can also be customized for each connection (`con
Prefix used to generate the duplicate node classes like `Duplicate2`, `Duplicate3` ...
Those nodes are inserted automatically to implement one-to-many connections.
If you need to connect an output to more than 3 nodes, you'll have to create the `Duplicate` nodes.