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.

How to access 'C' structure attributes with in-line assembly



hello,

I'm trying to write some inline assembler code that accesses the attributes of a structure that is passed to it. A previous post ( http://e2e.ti.com/support/development_tools/compiler/f/343/p/100304/773310.aspx#773310 ) said you could just add an '_' to the front of the type, but I've had no luck in getting this working. I've also looked at the generated .asm file and still couldn't see anything that helped.

In my simple example below I want to use +XAR4[_MyStruct.b] instead of +XAR4[1]. I don’t want to hard code the attribute's offset in the assembler, I want to be able to get the attributes offset from the structure. Can somebody please show me how to achieve this.


typedef struct MyStruct
{
    int a;
    int b;
    int r;
}MyStruct;

void Add (MyStruct * Ptr)
{
#if 0
    Ptr->r = Ptr->a + Ptr->b;
#else
    asm(
    "  MOV  AL, *+XAR4[0]\n" //*+XAR4[_MyStruct.a]
    "  ADD  AL, *+XAR4[1]\n" //*+XAR4[_MyStruct.b]
    "  MOV  *+XAR4[2], AL\n" //*+XAR4[_MyStruct.r]
    );
#endif
}

void main(void)
{
    MyStruct x;
    x.a = 4;
    x.b = 2;
    Add(&x);
}

My final goal is to write a inline 2p2z controller that is called from "C" code. It needs to be assembler because there aren't the required intrinsic commands to do this. Also to avoid the calling overhead it has to be inline. And to enable me to debug the rest of my code I don’t want to turn on full program level optimization.

cheers

Chris

  • You cannot use asm statements to directly interface with the C environment.  I call your attention to this quote from the C2000 compiler manual:

    ... asm statements that attempt to interface with the C/C++ environment or access C/C++ variables can have unexpected results.

    Rather than asm statements, I recommend you write your implement your key operations in C.  Perhaps multi-line macros ...

    #define BIG_MACRO(arg1, arg2, arg3)   \
    do {                                  \
       statement1;                        \
       statement2;                        \
       ...                                \
    } while (0)

    Or, if you prefer functions, supply them in header files with the modifier "static inline".  Using that method means you always have to build with optimization (--opt_level=-0 or higher).

    Thanks and regards,

    -George

  • hi,
    thank you for your comments. Writing a 2p2z controller in asm is more efficient than using "C", and I wanted to avoid writing a asm function and incurring the overhead of calling it.

    I created the following 3 test functions which all use CNTRL_2p2zDataFloat as a common "C" structure.

    opt=disabled    opt=0     (tested on a 60MHz F28069)
    2440ns         1190ns        inline "C" 2p2z function - CNTRL_2p2zFloatInline
    1150ns         1130ns        inline asm 2p2z function - 2p2zFloatAsmInline
    1070ns         1050ns        asm 2p2z function - CNTRL_2p2zFloatAsm

    The difference between the "C" function and asm function was only 9 instructions, which isn't bad, but it's still a waste of 140ns.

    The inline asm performed worst than the asm function, because I had to use the intrinsic __sat() command to get the "C" pointer in to a CPU register (I couldn't find any inline asm method of extracting a "C" pointer value) and I had to save all CPU and FPU registers I used. Also I had to hard code my "C" structure attributes offset.

    The asm function provided the best performance (and allowed interaction with my "C" structure), because I didn't need to save all CPU and FPU registers (see TMS320C28x Optimizing C/C++ Compiler, Section "TMS320C28x Register Use and Preservation").

    Is there some way to force the asm function to be in-lined? That should save 2(entry and exit call overhead) x 9 (pipeline) x 16.66ns (sys clock) = 300ns?

    cheers

    Chris


    struct CNTRL_2p2zDataFloat
    {
        float   m_Ref;        //0
        float   m_Fdbk;        //2
        float   m_Out;        //4
        float   m_B2;        //6
        float   m_B1;        //8
        float   m_B0;        //10
        float   m_A2;        //12
        float   m_A1;        //14
        float   m_Max;        //16
        float   m_Min;        //18
        float   m_U[2];        //20, 22
        float   m_E[3];        //24
    };

    static inline void CNTRL_2p2zFloatInline( CNTRL_2p2zDataFloat* x )
    {
        (x)->m_E[2] = (x)->m_E[1];
        (x)->m_E[1] = (x)->m_E[0];
        (x)->m_E[0] = (x)->m_Ref-(x)->m_Fdbk;

        (x)->m_U[1] = (x)->m_U[0];
        (x)->m_U[0] = (x)->m_Out;

        (x)->m_Out = ((x)->m_A1*(x)->m_U[0]) + ((x)->m_A2*(x)->m_U[1])
            + ((x)->m_B0*(x)->m_E[0]) + ((x)->m_B1*(x)->m_E[1]) + ((x)->m_B2*(x)->m_E[2]);

        (x)->m_Out = __fmin( (x)->m_Out , (x)->m_Max );
        (x)->m_Out = __fmax( (x)->m_Out , (x)->m_Min );
    }

    #define CNTRL_2p2zFloatAsmInline( x ) \
    {\
        volatile long ignore = __sat((long)x);\
        asm( "        MOV32    *SP++,R4H"\
        "\n        PUSH    AR0"\
        "\n        PUSH    XAR4"\
        "\n        PUSH    XAR5"\
        "\n        PUSH    XAR6"\
        "\nm_Ref    .set    0    ; CNTRL_2p2zDataFloat.m_Ref"\
        "\nm_Fdbk    .set    2    ; CNTRL_2p2zDataFloat.m_Fdbk"\
        "\nm_Out    .set    4    ; CNTRL_2p2zDataFloat.m_Out"\
        "\nm_U        .set     20    ; CNTRL_2p2zDataFloat.m_U"\
        "\nm_B2    .set     6   ;CNTRL_2p2zDataFloat.m_B2"\
        \
        "\n            MOVL    XAR4, ACC"\
        \
        "\n        ; calculate error (Ref - Fdbk)"\
        "\n        MOV32    R0H, *+XAR4[m_Ref]                        ; R0H = Ref"\
        "\n        MOV32    R1H, *+XAR4[m_Fdbk]                        ; R1H = Fdbk"\
        "\n        SUBF32    R0H,R0H,R1H                        ; R0H = e(n) =Ref -Fdbk"\
        \
        "\n        ; set up address pointers"\
        "\n        MOVL    ACC,XAR4      ; AR4 = address of &Cntrl2p2z"\
        "\n        ADD    ACC, #m_B2"\
        "\n        MOVL    XAR5, ACC"\
        "\n        ADD    ACC, #m_U-m_B2"\
        "\n        MOVL    XAR6, ACC"\
        \
        "\n        ;store error in DBUFF"\
        "\n        MOV32    *+XAR6[4], R0H                    ; e(n) =  error Q{30}"\
        \
        "\n        ; compute 2P2Z filter"\
        "\n        MOV        AR0,#8"\
        "\n        MOV32    R0H, *+XAR6[AR0]                ;     R0H  = e(n-2)"\
        "\n        MOV32    R1H, *XAR5++                    ;    R1H  = B2"\
        "\n        MPYF32    R2H,R0H,R1H                     ;    R2H = e(n-2)*B(2), R1H=B1"\
        "\n        ||  MOV32     R1H,*XAR5++"\
        \
        "\n        MOVD32    R0H,*+XAR6[6]                    ;    R0H = e(n-1) and e(n-2)=e(n-1)"\
        "\n        MPYF32    R3H,R0H,R1H                        ;    R3H = e(n-1)*B1"\
        "\n        ||  MOV32 R1H,*XAR5++                        ;     R1H=B0"\
        \
        "\n        MOVD32    R0H,*+XAR6[4]                    ;    R0H=e(n) and e(n-1)=e(n)"\
        "\n        MPYF32    R4H,R0H,R1H                        ;    R4H =e(n)*B0 , R2H = e(n-2)*B2+e(n-1)*B1"\
        "\n        ||    ADDF32  R2H,R2H,R3H"\
        \
        "\n        MOV32    R0H,*+XAR6[2]                    ;    R0H=u(n-2)"\
        "\n        MOV32    R1H,*XAR5++                        ;     R1H=A2"\
        "\n        MPYF32    R3H,R0H,R1H                        ;     R3H=u(n-2)*A2, R2H = e(n-2)*B2+e(n-1)*B1+e(n)*B0"\
        "\n        ||    ADDF32 R2H,R2H,R4H"\
        \
        "\n        MOVD32    R0H,*+XAR6[0]                    ;     R0H=u(n-1), u(n-1)=u(n-2)"\
        "\n        MOV32    R1H,*XAR5++                        ;     R1H=A1"\
        "\n        MPYF32    R4H,R0H,R1H                        ;   R4H=u(n-1)*A1 , R2H = e(n-2)*B2+e(n-1)*B1+e(n)*B0+u(n-2)*A2"\
        "\n        ||    ADDF32 R2H,R2H,R3H"\
        "\n        MOV32    R0H,*XAR5++                        ;    R0H = Maximum Value"\
        \
        "\n        ADDF32    R2H,R2H,R4H                        ;    R2H = e(n-2)*B2+e(n-1)*B1+e(n)*B0+u(n-2)*A2+u(n-1)*A1"\
        "\n        MOV32    R1H,*XAR5++                        ;    R1H    = Minimum Value"\
        \
        "\n        MINF32  R2H,R0H"\
        "\n        MAXF32    R2H,R1H"\
        \
        "\n        MOV32    *+XAR4[m_Out],R2H"\
        "\n        MOV32    *+XAR6[0],R2H"\
        \
        "\n        POP        AR0"\
        "\n        POP        XAR4"\
        "\n        POP        XAR5"\
        "\n        POP        XAR6"\
        "\n        MOV32   R4H, *--SP"\
        \
        );\
    }


        .cdecls C,LIST,"dpwr.h"
        .text
        .def         _CNTRL_2p2zFloatAsm
        .global        _CNTRL_2p2zFloatAsm
    _CNTRL_2p2zFloatAsm:
            MOV32    *SP++,R4H
    m_Ref    .set    CNTRL_2p2zDataFloat.m_Ref
    m_Fdbk    .set    CNTRL_2p2zDataFloat.m_Fdbk
    m_Out    .set    CNTRL_2p2zDataFloat.m_Out
    m_U        .set     CNTRL_2p2zDataFloat.m_U
    m_B2    .set     CNTRL_2p2zDataFloat.m_B2


    ; calculate error (Ref - Fdbk)
            MOV32    R0H, *+XAR4[m_Ref]                        ; R0H = Ref
            MOV32    R1H, *+XAR4[m_Fdbk]                        ; R1H = Fdbk
            SUBF32    R0H,R0H,R1H                        ; R0H = e(n) =Ref -Fdbk

    ; set up address pointers
            MOVL    ACC,XAR4      ; AR4 = address of &Cntrl2p2z
            ADD    ACC, #m_B2
            MOVL    XAR5, ACC
            ADD    ACC, #m_U-m_B2
            MOVL    XAR6, ACC

    ;store error in DBUFF
            MOV32    *+XAR6[4], R0H                    ; e(n) =  error Q{30}


    ; compute 2P2Z filter
            MOV        AR0,#8
            MOV32    R0H, *+XAR6[AR0]                ;     R0H  = e(n-2)
            MOV32    R1H, *XAR5++                    ;    R1H  = B2
            MPYF32    R2H,R0H,R1H                     ;    R2H = e(n-2)*B(2), R1H=B1
        ||  MOV32     R1H,*XAR5++


            MOVD32    R0H,*+XAR6[6]                    ;    R0H = e(n-1) and e(n-2)=e(n-1)
            MPYF32    R3H,R0H,R1H                        ;    R3H = e(n-1)*B1
        ||  MOV32 R1H,*XAR5++                        ;     R1H=B0


            MOVD32    R0H,*+XAR6[4]                    ;    R0H=e(n) and e(n-1)=e(n)
            MPYF32    R4H,R0H,R1H                        ;    R4H =e(n)*B0 , R2H = e(n-2)*B2+e(n-1)*B1
        ||    ADDF32  R2H,R2H,R3H


            MOV32    R0H,*+XAR6[2]                    ;    R0H=u(n-2)
            MOV32    R1H,*XAR5++                        ;     R1H=A2
            MPYF32    R3H,R0H,R1H                        ;     R3H=u(n-2)*A2, R2H = e(n-2)*B2+e(n-1)*B1+e(n)*B0
        ||    ADDF32 R2H,R2H,R4H

            MOVD32    R0H,*+XAR6[0]                    ;     R0H=u(n-1), u(n-1)=u(n-2)
            MOV32    R1H,*XAR5++                        ;     R1H=A1
            MPYF32    R4H,R0H,R1H                        ;   R4H=u(n-1)*A1 , R2H = e(n-2)*B2+e(n-1)*B1+e(n)*B0+u(n-2)*A2
        ||    ADDF32 R2H,R2H,R3H
            MOV32    R0H,*XAR5++                        ;    R0H = Maximum Value

            ADDF32    R2H,R2H,R4H                        ;    R2H = e(n-2)*B2+e(n-1)*B1+e(n)*B0+u(n-2)*A2+u(n-1)*A1
            MOV32    R1H,*XAR5++                        ;    R1H    = Minimum Value

            MINF32  R2H,R0H
            MAXF32    R2H,R1H

            MOV32    *+XAR4[m_Out],R2H
            MOV32    *+XAR6[0],R2H

            MOV32   R4H, *--SP

        LRETR

  • Christopher Hossack said:
    Is there some way to force the asm function to be in-lined?

    No.  Sorry.  -George

  • hi George,

    Thank you for your comments.

    I guess what I really want is to be able to write my own intrinsic function. I guess when you write an intrinsic function you need to inform the compiler which registers your code will use, so it's can preserve just the registers it's interest in.

    So is there any chance of been able to write our own intrinsic functions or to be able to include a .asm in a "C" file and mark the asm function as in-line?

    cheers

    Chris

  • Christopher Hossack said:
    is there any chance of been able to write our own intrinsic functions or to be able to include a .asm in a "C" file and mark the asm function as in-line?

    No.  Sorry.  -George

  • It is an appetizing thought to be enabled to enhance the intrinsic functions of a compiler - at least for those special hardwares like DSPs. But the intrinsic functions are not actually at a same level with library functions, but in-built into the compiler's tools suite. And they usually are used at all if compiled with optimization - and if the compiler while optimization decides to do so. Also the inline keyword does not lead obligatory to inlining, also to decide that is left to the compiler.

    Anyway, your thoughts are not strange to me, although the typical argument often heard then is that the compiler optimization was very complex and in most cases would deliver better results than from hand-optimization. In most cases... So probably the only way will be to write bigger functions in assembler, so that the overhead of calling doesn't hurt too much. A pity. I'd like it more if for (inline) C++ functions it might be possible to declare the kind of parameter delivering (and not leave that to the opimizers decision) to have a sure access to the parameters from inside asm too.

    Regards,
    Joern.

  • Yes, you can use asm statements to access C variables. You just have to be careful with the optimization effects.

    The manual encourages checking the compiled code.

    In this case it's pointer parameter, so it just can't be optimized away.

    You might want to take a look into chapter 12.2.13 Structures and Unions

    in http://www.ti.com/lit/ug/spru513e/spru513e.pdf

  • Hi turboscrew,

    I've managed to access C global variables, but not function arguments, or attributes of a C structure when using inline asm statements.

    I've read chapter 12.2.13 and I think that just applies to asm files, since I can get asm files working as expected.

    It's when I used "C" files with inline asm statements I'm having a problem

    I would be very grateful if you could give me an example of inline asm within "C" files to access function arguments and attributes of a "C" structure.

    cheers

    Chris