Creating Blocks for C6Flo 1.0

From Texas Instruments Embedded Processors Wiki

(Redirected from Creating Blocks for C6Flo)
Jump to: navigation, search
Translate this page to   

^ Up to C6Flo 1.0 Main Page ^

This article is part of a series describing the C6Flo graphical development tool. Click the link above to return to the main page.

Contents

Introduction

This page explains how to create blocks for C6Flo, a graphical development tool for C6000 DSPs. The block creation process consists of five basic steps:

  1. Create a new folder for your block
  2. Create JavaScript package and module files that describe your block to C6Flo
  3. Write C-language source template files that manifest your block in generated application code
  4. Build your block's package and module using the XDC command line tool
  5. (Experimental release only) Create an XML and ZIP file so your block will work with legacy GUI components

Steps 2, 3, and 5 can be completed in any order, but certain parts of each must agree with each other. For instance, to define and use a user-controlled parameter, such as a filter tap, that parameter must be defined and used consistently in the JavaScript module, C source templates, and XML file.

The XML and ZIP files in step #5 must be created using the Component Publisher utility that comes with early releases of C6Flo. These files are only required by certain legacy components of the C6Flo GUI and will deprecated as that GUI is updated in subsequent releases (target date: Q4 2010).

Creating a New Folder

The first step to create a new block is to create a folder to contain it. This folder should be placed in the Blocks folder within the C6Flo installation. A typical block uses the following folder location:

<C6Flo Installation>/Blocks/ti/c6flo/<category>/<name>

For instance, the math.gain block uses math and gain for category and name, respectively. The folder's location will ultimately determine how the block is listed in the component publisher.

Note: Blocks not made by TI can use their own vendor name to further modify the suggested folder path.
Note: The block palette category is also explicitly set in the legacy XML file for preliminary releases of C6Flo.

Creating the JavaScript Package and Module Files

Once the (empty) block folder has been created, you must create three simple JavaScript files to define a package and module for your block. Each block has its own package and module definition. The package consists of two files, package.xdc and package.bld, while the module consists of only one: <Name>.xdc. By convention, the module file is named using CamelCase (as in Gain.xdc or FirFilter.xdc).

The package file contents are very simple. In practice, package.bld is the same for all blocks and package.xdc contains only two items: the path to the block's folder (relative to Blocks) and the name of the block's JavaScript module. The following examples show the contents of each file for the math.gain block that comes with C6Flo.

package.xdc
package ti.c6flo.math.gain
{
    module Gain;
}
package.bld
Pkg.attrs.exportAll = true;

Defining your block's module is more involved. The file must be created as <Module>.xdc, matching the module name given on line 3 of package.xdc. The following shows the contents of Gain.xdc, from the math.gain block. We will explain each element of this file below.

Gain.xdc
/*!
 *  ======== Gain ========
 *  Scale buffer samples by specified gain
 */
metaonly module Gain inherits ti.c6flo.IBlock
{
    override config int allowed_inputs[] = [1];
    override config int allowed_outputs[] = [1];
 
    override config String critical_params_proc[] = ["c6flo_input_type"];
 
instance:
    /*!
     *  ======== gain ========
     *  Gain factor
     */
    config float gain = 1.0;
}

There are several elements that can appear in this file:

  1. Module definition with inheritance (line 5)
  2. Override module parameters (lines 7-10)
  3. Override instance parameters (none in this example)
  4. New instance parameters (lines 13-19)
  5. New or override instance functions (none in this example)

The module definition occupies line 5 of our example file; note that the module name must be the same as in package.xdc. Note also that the block inherits from the interface called IBlock. This is basically a parent module that contains common parameters that all blocks must have. The interface has its own module definition file located at Blocks/ti/c6flo/IBlock.xdc.

The remaining contents of the Module.xdc file will be described in the following sub-sections.

Override Module Parameters

You can override IBlock parameters in your own module file if you want to use different default values for your block. This is done (basically) by copying a relevant line from IBlock.xdc, adding the keyword override to the beginning of the line, and assigning a different value. This is typically used to change how blocks are treated by the C6Flo GUI. For instance, changing the variable allowed_inputs controls how many inputs the user can select for this block within their block diagram.

Module-level parameter overrides must be placed above the instance: label within the module file.

New Module Parameters

Blocks should not define new module-level parameters. Only standard parameters will be recognized by the C6Flo GUI. All user-configurable parameters are applied on a per-instance basis, and thus are defined as instance-level parameters.

Override Instance Parameters

Any of the instance parameters defined in IBlock.xdc can be overridden. The process is similar to module-level parameters, but instance-level overrides must be placed below the instance: label within the module file.

New Instance Parameters

Many blocks allow the user to configure instance parameters in the GUI. For this to work, those parameters must be listed as instance-level parameters in the module file. These definitions must be placed below the instance: label within the module file. In the above example, we define a single parameter, gain, as a floating point number.

Instance Functions (advanced)

It is possible (but absolutely not required) to create or override instance functions in the JavaScript domain. Functions declared in this way must be defined in a separate file, <Module>.xs, which is placed in same folder as <Module>.xdc. These functions can then be called from the block's C source template files as this.<function name>(<args>). This is an advanced feature that is not used by most blocks.

These functions exist only in the JavaScript domain. They have no relation to the standard C functions implemented by each block.

Writing the Source Template Files

Ultimately, your block will be used to generate part of the C-language source code for any C6flo system that uses it. For this to happen, you must provide six (6) source template files. These files are summarized in the following table.

File name Description
typedef.ht Defines the format of instance structs for the block; placed in application header file
struct.ct Lays out the instance struct for a block instance; placed in application _blocks.c source file
create.ct Implements the standard create function; placed in application _blocks.c source file
init.ct Implements the standard initialization function; placed in application _blocks.c source file
proc.ct Implements the standard processing function; placed in application _blocks.c source file
ctrl.ct Implements the standard control function; placed in application _blocks.c source file

During code generation, C6Flo applies a sophisticated JavaScript-based template engine to each of these files to generate pure C-language code fragments. The template engine can be used extensively to adapt blocks to efficiently account for changing parameters, or it can be used sparingly to create very simple source template files. The following rules summarize the capabilities of the template engine.

  1. By default, text is copied as-is from the template file into the generated code
  2. Lines beginning with % are interpreted as JavaScript and are not copied to the generated code
    • Lines between JavaScript control braces are conditionally copied to the generated code (see example below)
  3. Lines between %%{ and %%} are interpreted as JavaScript and are not copied to the generated code
  4. Elements within `back apostrophes` are evaluated as JavaScript and are copied to the generated code
    • `Back apostrophes` can only be used in a line that is not already interpreted as JavaScript

The following example shows how these various rules may be used in a template file.

Template file contents
% // this is some JavaScript code
% var my_string = "// hello world";
% var display_string = true;
% if (display_string) {
`my_string`
% } else {
// (didn't display my_string)
% }
%%{
// more JavaScript
// ...
%%}
Generated code contents
// hello world

Four Standard Functions

One of our primary goals when creating a block is to implement the four standard functions required by C6Flo: create, initialize, process, and control. This section explains the purpose of each function and describes its arguments and return values. Each function has a standard prototype that is automatically generated by C6Flo. This prototype is inserted in your source template using this.c6flo_cg_<name>_fxn_prototype, where name is an abbreviated name for the function in question: create, init, proc, or ctrl. You should not attempt to type in the function prototype manually.

Note: Always use `this.c6flo_cg_<name>_fxn_prototype` to insert the function prototype in your templates.

All standard functions should return their status using the standard values defined in the following table.

Status Macro Description
C6Flo_EOK Status OK; continue normal thread operation
C6Flo_ERESET Reset thread; call initialize functions again before next processing loop
C6Flo_EGENERIC Generic error condition; abort thread
C6Flo_EID ID error condition; abort thread
C6Flo_ERANGE Range error condition; abort thread
C6Flo_EALLOC Allocation error condition; abort thread

Create

Prototype
int ti_c6flo_<name>_v1_create(ti_c6flo_<name>_v1_hdl blockp);

Initialize

Prototype
int ti_c6flo_<name>_v1_init(ti_c6flo_<name>_v1_hdl blockp);

Process

Prototype
int ti_c6flo_<name>_v1_proc(ti_c6flo_<name>_v1_hdl blockp, C6Flo_buf_hdl *inputs, C6Flo_buf_hdl *outputs);
Macro Description
C6Flo_get_buf_size(a) Gets size (in bytes) of buffer a (ex. a = inputs[0])
C6Flo_set_buf_size(a, b) Sets size of buffer a to value b
C6Flo_get_buf_length(a) Gets length (in elements) of buffer a
C6Flo_set_buf_length(a, b) Sets length of buffer a to value b
C6Flo_get_buf_ptr(a) Gets buffer data pointer of buffer a (return as void *)
C6Flo_set_buf_ptr(a, b) Sets buffer data pointer of buffer a to value b

Control

Prototype
int ti_c6flo_<name>_v1_ctrl(ti_c6flo_<name>_v1_hdl blockp, C6Flo_HOST_msg_hdl incoming_msg, C6Flo_HOST_msg_hdl outgoing_msg);
Message struct definition
typedef struct {
    Uint16 block_id;
    Uint16 arg_count;
    void *arg_buffer;
} C6Flo_HOST_msg_obj, *C6Flo_HOST_msg_hdl;

Accessing Block Parameters

Block instance parameters are accessible in the source template files. This includes standard instance parameters defined in IBlock.xdc as well as "extra" instance parameters defined in <Block>.xdc. It does not include module-level parameters defined above the instance: label in IBlock.xdc.

The block instance is passed to the template file as this during code generation. Thus, to access a parameter, simply use this.<parameter name> in a JavaScript context:

Two methods to access block instance parameters
% var input_count = this.c6flo_input_count;
float my_gain = `this.gain`;

In general, you should use parameters directly in struct.ct. Parameters that are used directly in a function are called critical parameters for that function. If a function has no critical parameters, it is only generated once and can be shared by any number of block instances. Critical parameters can thus increase code size in the generated application, and should only exist when they provide a clear benefit in terms of performance or code structure.

Critical Parameters

An instance parameter is called a critical parameter of a particular function if it appears directly in the source template for that function. In general, instance parameters are only used in the struct template. This allows function code to be re-used between different instances of the same block, reducing code size and resulting in cleaner application code. Critical parameters allow you to change how a function looks depending on parameters that the use sets when designing their system. This is a trade off that allows greater flexibility and (sometimes) more elegant code at the cost of code size. For example, this is the processing function template for the math.sum block:

math/sum/proc.ct
`this.c6flo_cg_proc_fxn_prototype`
{
    % var type_string = this.get_input_type_string();
    `type_string` *x[`this.c6flo_input_count`], *y;
    int count, i;
 
    // get buffer count (NOTE: assume all buffers same size)
    count = C6Flo_get_buf_length(outputs[0]);
 
    // get buffer data pointers
    for (i = 0; i < `this.c6flo_input_count`; i++)
        x[i] = C6Flo_get_buf_ptr(inputs[i]);
    y = C6Flo_get_buf_ptr(outputs[0]);
 
    // calculate sum of inputs
    for (i = 0; i < count; i++)
        % var x_string = "x[0][i] + x[1][i]";
        % for (var k = 2; k < this.c6flo_input_count; k++)
        %     x_string += " + x[" + k + "][i]";
        y[i] = `x_string`;
 
    return C6Flo_EOK;
}

There are two critical parameters in this function, c6flo_input_type and c6flo_input_count. The input type parameter is used to cast the input and output data pointers to the appropriate C datatype. (The helper function, get_input_type_string(), is a JavaScript-domain function implemented in IBlock.xs that uses c6flo_input_type for this purpose.) This allows the generated code to cleanly add the input signals using the appropriate addition opcodes.

The input count parameter is used to produce a cleaner summation expression and maximize efficiency. The alternative here is to use a more complicated structure in the source template, as in the following counter-example:

math/sum/proc.ct (alternate)
if (blockp->std.input_count == 2)
    for (i = 0; i < count; i++)
        y[i] = x[0][i] + x[1][i];
else if (blockp->std.input_count == 3)
    for (i = 0; i < count; i++)
        y[i] = x[0][i] + x[1][i] + x[2][i];
else if (blockp->std.input_count == 4)
    // etc

Clearly the original template is an improvement over this messy implementation. However, using an instance parameter directly in a function is not always the right thing to do. This is the processing function template for the math.gain block.

math/gain/proc.ct
`this.c6flo_cg_proc_fxn_prototype`
{
    % var type_string = this.get_input_type_string();
    `type_string` *data;
    int count, i;
 
    // get element count and data pointer
    count = C6Flo_get_buf_length(inputs[0]);
    data = C6Flo_get_buf_ptr(inputs[0]);
 
    // apply gain (in place)
    for (i = 0; i < count; i++)
        data[i] = (`type_string`)(data[i] * blockp->gain);
 
    return C6Flo_EOK;
}

Note that, while data type is again used as a critical parameter, the gain parameter is included as blockp->gain. This is because creating multiple nearly-identical copies of the function for different gain values is inefficient and would generate sloppy, repetitive code. Wherever possible, instance parameters should be left in the instance struct and not used directly in functions.

The names of critical parameters must be listed in the module-level parameter critical_params_<name>[], where name is the abbreviated name of the function. For instance, note line 8 of Sum.xdc:

math/sum/Sum.xdc
metaonly module Sum inherits ti.c6flo.IBlock
{
    override config int allowed_inputs[] = [2, 3, 4, 5, 6, 7, 8];
    override config int allowed_outputs[] = [1];
 
    override config Bool is_symmetric = false;
 
    override config String critical_params_proc[] = ["c6flo_input_count", "c6flo_input_type"];
 
instance:
}

Note that this function has critical parameters only for the processing function. By default, each function has no critical parameters.

Using Helper Functions in the JavaScript Domain

The IBlock interface provides utility functions in the JavaScript domain to perform common template tasks. The following table lists these functions.

Function name Description
get_type_string(data_type) Returns a string containing the C-standard name for the specified data type. The following data_type strings are supported: Float32, Float64, Int8, Int16, Int32, UInt8, UInt16, UInt32
get_input_type_string() equivalent to get_type_string(this.c6flo_input_type)
get_input_type_string() equivalent to get_type_string(this.c6flo_output_type)

Building the Block Package

Once all of the package and module files have been created, you are ready to build your block's JavaScript package. This is a simple matter of using the xdc command line tool. Navigate to your block's folder in a terminal window and use the following command:

xdc all

Note that there are a few simple prerequisites here:

  1. Install the XDC tools and make sure that the XDCROOT environment variable is set properly
  2. Add the absolute path of your Blocks folder to the XDCPATH environment variable

Also, if you want to distribute your block to other people, you may want to use the following syntax:

xdc release

This will build the package and place all of the relevant files into a ZIP file that can be easily shared.

Creating the XML File (temporary)

While C6Flo will ultimately parse blocks' module files to understand each block's parameters, that functionality does not exist in the preliminary GUI used in early releases of the C6Flo software. For blocks to work with this GUI, they must also provide an XML file that it can understand. The contents of this XML file are much more complicated than the other files we've created, but they can be generated by the legacy ComponentPublisher tool instead of being written by hand. While this tool is somewhat complicated and initially confusing, you don't need to worry about most of its tabs and controls. Only two tabs are relevant when creating blocks for C6Flo: Main and Design Properties.

Main Tab

The Main tab contains several fields that determine how the block appears in the palette and block diagram editor within the legacy GUI. The following table lists these fields and how they should be set.

Field Name Description
Component name Lower-case name of block (module name only: gain, biquad, etc.)
Component description Optional string describing block
Vendor name For internally-developed blocks, this should be set to "ti"
Version number Version string; generally set to "v1"
Palette name String indicating which palette in the legacy GUI the block appears in. Typically set to the block folder within Blocks/ti/c6flo (ex. Math for math.gain)
Display name Capitalized form of Component name
Number of input channels Default number of input channels; should match c6flo_input_count in JavaScript module
Input channels supported Allowed numbers of input channels; should match allowed_inputs in JavaScript module
Number of output channels Default number of output channels; should match c6flo_output_count in JavaScript module
Output channels supported Allowed numbers of output channels; should match allowed_outputs in JavaScript module
Fill color Color used to fill block in diagram; typically set to light blue (for framework or host control blocks), light green (for input blocks), coral (for output blocks), or beige (for other blocks)
Graphic Small (16x16) icon used in the block diagram; optional
Large icon Large (32x32) icon used in the GUI palette; required
Singleton category Field used to identify Framework and Host Control blocks. Must be left blank for other blocks.

This screenshot shows the settings used for the math.gain block.

C6flo component publisher main.png

Design Properties Tab

The Design Properties tab is used to list the block's parameters in a way that ther preliminary GUI can understand. Since this GUI was (mostly) written before the other pieces of C6Flo, there are several parameters that must be defined in this tab. The following table lists these required parameters.

File name Description
c6flo_cg_temp_rtsc_package_name The full JavaScript module name including package path (i.e. ti.c6flo.<category>.<block>.<Block>)
c6flo_cg_temp_version Version number string (typically 1)
c6flo_cg_temp_critical_params_create List of critical parameters for create function (one combined string separated by semicolons); must match critical_params_create[] in JavaScript module
c6flo_cg_temp_critical_params_init List of critical parameters for init function (one combined string separated by semicolons); must match critical_params_init[] in JavaScript module
c6flo_cg_temp_critical_params_proc List of critical parameters for proc function (one combined string separated by semicolons); must match critical_params_proc[] in JavaScript module
c6flo_cg_temp_critical_params_ctrl List of critical parameters for ctrl function (one combined string separated by semicolons); must match critical_params_ctrl[] in JavaScript module
c6flo_cg_temp_resource_lib List of required C library files (one combined string separated by semicolons); must match resource_lib in JavaScript module
c6flo_cg_temp_resource_tci List of required DSP/BIOS TCI files (one combined string separated by semicolons); must match resource_tci in JavaScript module
c6flo_cg_temp_resource_h List of required C header files (one combined string separated by semicolons); must match resource_h in JavaScript module
c6flo_input_type Corresponds to c6flo_input_type in JavaScript module; must be an enumeration with appropriate string options (Float32, Float63, Int8, Int16, etc.) (integer values of enum are not used)
c6flo_output_type Corresponds to c6flo_output_type in JavaScript module; must be an enumeration with appropriate string options (Float32, Float63, Int8, Int16, etc.) (integer values of enum are not used)
c6flo_id Corresponds to c6flo_id in JavaScript module; must default to 0

In addition to these required values, and extra parameters used by your block should be defined on this tab. These definitions should use the same name, data type, and default value that they use in your JavaScript module and source template files. The following screenshot shows the Design Properties tab for the math.gain block. Note that the gain parameter is defined on this tab as a floating point value that defaults to 1.0.

C6flo component publisher design.PNG

Saving and Publishing

When you have finished setting all of the relevant fields in the Main and Design Properties tabs, you are ready to save and publish the XML file. First, use the File->Save XML command to save your XML file. The XML file should be saved in the same folder as your block's other files.

Next, you must publish your XML file to a special folder used by the preliminary GUI. Click the Publish button in the lower right corner of the ComponentPublisher, and select the <C6Flo Installation>/ComponentLibrary folder. Afterward, you will find a ZIP file containing your XML file (and any icon files it uses) in that location.

Miscellaneous Tips

  • When creating a new XML file, it is best to copy an existing XML file to your new block's folder, then rename that copy to the desired file name. The Component Publisher sometimes behaves strangely if you open an XML file and save it to a different location using File->Save XML.
  • If you change your XML file, you must save and re-publish it before your changes will appear in the legacy GUI. If the GUI is already open when you publish your modified block, the changes will not appear until you close and re-open the GUI.
  • The JavaScript package and module files follow the RTSC standard. You don't need to know anything about RTSC to create a block for C6Flo, but more information is available at RTSC-Pedia (see below).

See Also

Leave a Comment

Comments

Comments on Creating Blocks for C6Flo


Drew said ...

May want to remove References to PurePath Studio in the screen captures in order to make things less confusing.

--Drew 16:13, 9 November 2010 (CST)

Personal tools
Namespaces
Variants
Actions
Navigation
Print/export
Toolbox