This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

c6600 complex types: big picture?

I’m confused regarding TI’s support for complex float data.  Two main issues:

(1)    Order of real/imaginary in the data structure?

(2)    Correctly and efificiently casting between the various complex types: c99, TI types  is non-trivial.

Issue 1)

Should the order be real/imaginary or imaginary/real?   What type should be used?  The C/C++ type or the compiler type float2_t?  It wouldn’t matter except that the compiler does not support casting between the two very well (see end of this post).

(For the moment, unless stated otherwise, assume little endian for ensuing discussion).

Consider little endian.  The traditional method of representing complex data is real/imaginary, namely an array (or an array of structs with the equivalent layout) where A[2*i] is the real component and A[2*i+1] is the imaginary component.  This is what is used by the ISO C99 float complex supported by cl6x  beginning with cl6x 7.4.  I believe that this is also what is used by c++98 complex class complex<float>.

Although I cannot find any documentation in dsplib that states the expected input/output format for DSPF_sp_fftSPxSP() (or the double precision version in the c66 dsplib), it appears that the real/imaginary format (as opposed o to the reverse imag/real format)  is also what is expected by this routine in both the c674x and c66x dislib routines.  Please confirm this!

However, per  spabg7, the following structure (namely the imaginary/real format) is recommended:

#ifdef _LITTLE_ENDIAN

typedef struct _CPLXF

{

float imag;

float real;

} cplxf_t;

#else

typedef struct _CPLXF

{

float real;

float imag;

} cplxf_t;

#endif

There is a discussion of using the compiler intrinsics _complex_mpy(), which translates to the pair assembly instructions CMPYSP and DADDSP.  Base on my non-expert calculations with little endian, the imaginary/real format is indeed needed to use this compiler instrinsic and compute a complex add with minimal assembly instructions.  To use the traditional representation real/imaginary, takes three machine instructions: DMPYSP and the pair (FADDSP || FSUBSP).  Same time (assuming FADDSP can be executed in parallel on the next instruction – I did not check the machine constraints on this) but one extra instruction.  There does not appear to be a compiler intrinsic for this case.

Clearly, continually converting back and forth between real/imag and imag/real is not efficient and presumably not the intended use mode. 

Am I missing something?  How is complex float intended to be used? 

 Issue 2)

The other issue with complex float is that different types are needed for different operations.  The fft routine wants an array of floats, the builtin c99 functions want “complex float”, the TI intrinsics want __float2_t and the app note sprabg7 recommends a structure.   In theory, ignoring the issue of real/imaginary order for a moment, casting between  these complex types should be simple.  They all store data in memory as two consecutive floats, 64 bits total.  However, when I try to cast between “complex float” and __float2_t, the compiler  generates an SPDP do the conversion.  For example, consider the following code:

#include <complex.h>

#include <c6x.h>

#include<stdio.h>

 

main()

{

  complex float c = 1.1 + 9.9 * __I__;

  __float2_t f2;

 

 

  printf("re=%f, im=%f\n", creal(c), cimag(c));

 

  f2 = (__float2_t) c;

 

  printf("re=%f, im=%f\n", _lof2(f2), _hif2(f2));

}

 

I get the following results:

 

re=1.100000, im=9.900000

re=-0.000000, im=1.887500

 

Again, I am wondering if I am missing something?  Is this my error or a bug? 

 

The only alternative that I am aware of is: 

 

complex float c;

__float2_t f = _ftof2(cimag(c), creal(c));

 

This method of casting (which should take 0 cycles) takes 39 cycles + two function calls to bulitin functions creal() and cimag().  This is prohibitively expensive so I did not even test the correctness of this.

To port my application to the c6678, I need to be able to use fft functions, complex trigonometric functions, reference real and imaginary fields  and do basic addition, multiplication and division.

Some guidance on how to do this correctly, efficiently and cleanly would be greatly appreciated!!!

  • I'm also very interested in the replies of this.

    In a previous application for C66x, we used complex float data represented as real-imaginary (little endian).
    We had to use the intrinsic _complex_mpy() which uses imaginary-real.

    It was a pain to get things right !

  • Lani,

    Currently, converting/casting between c99 complex types and float2_t is not supported.  The c99 complex types were added to provide out-of-the-box compatibility for existing code and at this time, should not be used when trying to obtain maximum performance.

    I'm not sure I can provide more useful insight on the performance/ordering/type storage issue.  A colleague who can provide more detail on this topic is currently out of the office for several days and I'll get them to comment when they return.

    Regards,

    -Todd

  • Internally the device's complex instructions allways have the real part in the upper half and the imaginary part in the lower half, of a double size unit, be it fixed or single precision floating point.

    The intrinsics;-

    _complex_mpysp()

    _complex_conjugate_mpysp

    _daddsp

    _dsubsp

    should then be used. for complex maths.

    In big-endian mode this is consistant with C99 oredering hower in little endian mode the complex and imaginary parts are transposed between the DSP and C99.

    If your code is little endian and talks to other C99 devices then the swap needs to be managed somewhere.