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.

reading C ptr from asm file (Or how to convert simple 4 line 28375D assembly into equivalent CLA assembly routine)

If I do something like:

extern "C" {

float * thePtr

}

in some .h file in the C program, then in a particular .asm file

.global _thePtr

now if in the C files somewhere there is a variable

float array[SOME_NUMBER];

and at some point we set thePtr = &array[0]

Can I in some fashion read each element of the array in the assembly file?

MOV32   SomeRegister, #_thePtr+0

MOV32   SomeRegiste4r, #_thePtr+2

etc?

The ultimate problem I am having is on the 28375D CLA you only have 2 address registers...

In a simple function that implements a simple filter, I would typically (say on the CPU which has 8 addressing registers) assign one to the input data, one to the output data, and one to the history data...

In this way I can write a loop that cycles through my elements, incrementing the address registers each time...

I looked and found one sample program where you show how say an IIR filter is implemented on the CLA, and you effectively 'unroll' the loop and manually access the data.

As such because you only have two address registers in the CLA I was thinking to still use the two address registers, say one for the history, and one for the output, but then I need to pass the input data in this global fashion so that I can 'unroll the loop' and directly index the incrementing sample.  however to keep the structure of the progam consistent I was thinking to do something like:

extern "C" {

extern void theASMFunction ( float * ptr1, float * ptr2, float * ptr3);

float * thePtr;

}

myFunction(params ...) {

     thePtr = myPTr3;

theASMFunction ( myPtr1, myPtr2, myPtr3);

}

now in the assembler code myPtr1 and myPtr2 will be loaded into MAR0, MAR1 on the CLA and myPtr3 on the stack... (not used, just maintained since on CPU would then load this into a 3rd addressing register)  now by 'passing' this 3rd pointer the hope is in the asm file I can write my loop as:

_theASMFunction:

series of assembler instructions constituting 1 iteration of loop using MAR0 and MAR1 and @_thePtr+# idea.

series of assembler instructions consisting of 2nd iteration of loop

...

series of assembler instructions consisting of nth iteration of loop

Any suggestions as to how I can address the 3rd vector?  (input is an array of N floats, output is an array of N floats, and history is an array of N floats

--Input-->+------------>+----> output

               |                 ^

            z^-1              |

               |                 |

            history --------+

Ideally I would just like a 3rd addressing register, but I don't want to play flip flop as it takes time to load and unload the addressing registers.  Since we are optimizing for execution speed,

On the 28375D CPU its a simple:

MOV32 R0H, *XAR6++; load next input sample

MOV32 R1H, *XAR5; load next history sample for subtraction

RPTB end, numberOfElements

SUBF32 R2H, R0H, R1H  Perform subtraction (ON CPU takes 2 cycles to become available to store)

|| MOV32 *XAR5++, R0H; Store the history

MOV32 R0H, *XAR6++; load next input sample

MOV32 *XAR4++, R2H;Store the Result

MOV32 R1H, *XAR5;Load Next history value for subtraction

end:

SUBF32 R2H, R0H, R1H

|| MOV32 *XAR5, R0H

NOP

MOV32 *XAR4, R2H

in this case XAR4 = result, XAR5 = history, XAR6 = new data

The question is how can I write this simple 4 line (SubF32 and 4 mov32s...) in CLA assembly?  without the 3rd addressing register... it's starting to look very icky.

  • I think this should work. The trick to this is to club the equal sized input and output arrays into one, where the output array starts halfway down.

        .def        _theASMFunction
        .ref        _thePtr
        .sect       "Cla1Prog:_theASMFunction"
        .align  2
    __theASMFunction_sp    .usect  ".scratchpad:Cla1Prog:_theASMFunction",4,0,1  
    
    ;; MAR0      -> ptr1 points to input and output
    ;; MAR1      -> ptr2 points to the history
    ;; scratch+2 -> loop counter
    _theASMFunction:
        .asmfunc
        .asg    __theASMFunction_sp + 0, _save_MR3
        .asg    __theASMFunction_sp + 2, _loop_counter
        .asg    __theASMFunction_sp + 4, _const_one
        .asg    100,  SIZEARRAYWORDS ; array is 50 entries, first 25 is input, 
                                     ; second half 25 is the output
        .asg    48,    OUTOFF        ; output starts halfway down the combined 
                                     ; array minus two words
    ; Context Save
        MMOV32     @_save_MR3, MR3
        <load up the loop counter here>
        <load const 32-bit int value of 1 to _const_one>
        MMOV32      MR0, *MAR0[2]++      ; load first input sample, inc ptr
        MMOV32      MR1, *+MAR1[0]       ; load first hitory
    
        MMOV32      MR3, @_loop_counter ;    will set ZF if 0, used for branching
    _loop:
        MSUBF32     MR2, MR0, MR1       ;  | single cycle sub
     || MMOV32      *MAR1[2]++, MR0     ;  | store history, increment ptr
        MMOV32      *+MAR0[OUTOFF], MR2 ;-3| store result at output offset
        MMOV32      MR1, MR0            ;-2| load next histor value
        MMOV32      MR0, @_const_one    ;-1| MR0 = 1 (integer)
        MBCNDD      _loop, NEQ          ; *| branch if loop_count != 0
        MSUB32      MR3, MR3, MR0       ;+1| MR3-- (decrement loop counter)
        MMOV32      @_loop_counter, MR3 ;+2| save to loop_counter
        MMOV32      MR0, *MAR0[2]++     ;+3| load next input
    _loop_end:
    
        MSUBF32     MR2, MR0, MR1       ;  | single cycle sub
     || MMOV32      *+MAR1[0], MR0      ;  | store last history
        MMOV32      *+MAR0[OUTOFF], MR2 ;  | store last result
    ; Context Restore
        MMOV32      MR3,@_save_MR3 
        .unasg  _save_MR3
        .unasg  _loop_counter
        .unasg  _const_one
        .endasmfunc
        

  • I forgot to add, this function only takes two points, ptr1 points to the combined input/output array, ptr2 to the history.
  • Thanks.

    This is probably the best approach, however there are additional blocks I didn't present as they are slightly more complicated.  However this approach should still work, but I don't have the luxury of just declaring my variables as is done in your example, or I could but it would mess up the organization of the code.

    As such the question is:  Can I create a linker cmd file that will place the variables so they have this known offset for me:  and have a dual configuration if it compiles for CPU which will execute differently.

    block 1 is what I showed, I will not show block 2 as it is sligtly more complex, but it is just a filter of a similar sort, with a history, some coefficients, and is feedback not feedforward but it has a real input and a complex output so the input output can not be paired, so I will pair the complex history with the complex output.

    If the organization of the code I have the variables declared at various points as

    Variables for BLOCK 1:

    (Both Real)
    float inputVariable[LENGTH];
    float intermediateVariable1[LENGTH];


    Variables for BLOCK 2:

    (Both Complex so 2 elements per output element)
    float outputVariable[LENGTH*2];
    float intermediateHistoryVariable[LENGTH*2];

    So if the above is the 'chosen' paired variables that will be represented by a single addressing register, then I need to control their actual spacing in memory so that I KNOW the offset.

    Now if I want the code to compile on both the CPU and the CLA using the below I was thinking to use named sections to seperate these variables so the linker cmd file can place them specifically with a known offset, when calling the CLA code but not do so when the CPU is compiling the code for execution on the CPU.


    #if defined(__TMS320C28XX_CLA__)

    Variables for BLOCK 1:

    #pragma SET_DATA_SECTION("CombFilterInputData")
    float inputVariable[LENGTH];
    #pragma SET_DATA_SECTION()
    #pragma SET_DATA_SECTION("CombFilterOutputData")
    float intermediateVariable1[LENGTH];
    #pragma SET_DATA_SECTION()

    #pragma SET_DATA_SECTION("ResonatorHistoryData");
    float intermediateHistoryVariable[LENGTH*2];
    #pragma SET_DATA_SECTION()
    #pragma SET_DATA_SECTION("ResonatorOutputData");
    float outputVariable[LENGTH*2];
    #pragma SET_DATA_SECTION()


    #elif defined(__TMS320C28XX__)
    float inputVariable[LENGTH];
    float intermediateVariable1[LENGTH];
    #endif


    The above would be scattered throughout the code where the variables are actually declared, but I put them all together here for clairity.

    As such I would ideally like to have the linker cmd file now place these variables so that we have the known offset for the CLA version of the assembly file, that will only be called if it's being executed on the CLA, but not use it if its being compiled for the CPU.

    #if defined(__TMS320C28XX_CLA__)
     CLAVersionASMFUnct(params);
    #elif defined(__TMS320C28XX__)
     CPUVersionASMFunction(params);
    #endif

    So the question is:

    Is there any way to have one linker CMD file that will work for both? or do I need to make two linker cmd files one for each configuration, and if so is there an automatic way like the compiler directives to automatically switch linker command files.  In this way both the sections, and the MEMORY would have to be set up so that the named section goes into the specified memory.

    Unless you can think of a better way to control where I place the variables, for example, is there a

    #pragma PUT_DATA_EXACTLY_HERE("0xb000")

    float mydata[LENGTH];

    #pragma PUT_DATA_EXACTLY_HERE()

    #pragma PUT_DATA_EXACTLY_HERE("0xb800")

    flaot mySecondDataKnownOFFSETFromFirst[LENGTH];

    #pragma PUT_DATA_EXACTLY_HERE()

    Thanks.

     I suppose another way one could look at it is: is there a way to extract the offset between two variables, so instead of me constraining where they are at, can I extract the offset between them . Probably not since it needs to know at compile time what the offset is, but it's worth asking.

    It looks to me like If I did something like this:
    #define BLOCK1_INPUT 0x8800
    #define BLOCK1_OUTPUT   0x9000
    #define BLOCK2_HISTORY  0x9080
    #define BLOCK2_OUTPUT   0x9200


    MEMORY
    {
    ...

       RAMLS1      : origin = 0x008800, length = 0x000800
       RAMLS2      : origin = 0x009000, length = 0x000800
       RAMLS3      : origin = 0x009800, length = 0x000800
       RAMLS4      : origin = 0x00A000, length = 0x000800
       RAMLS5      : origin = 0x00A800, length = 0x000800

        ...
    }

    SECTIONS
    {
     ...
     CombFilterIntputData : > BLOCK1_INPUT
     CombFilterOutputData : > BLOCK1_OUTPUT
     ResonatorHistoryData : > BLOCK2_HISTORY
     ResonatorOutputData  : > BLOCK2_OUTPUT

     CLAMemory : >> RAMLS1 | RAMLS2 | RAMLS3 |RAMLS4 | RAMLS5, PAGE = 1

        ...
    }

    This should treat the 4 variables (named sections) and allocate them to a specific memory address (done first by linker)

    Then CLAMemory section will contain all other variables assigned to the CLA, and 'FIT' them in around these assigned variables, correct?


    Q:  If in the CPU build I do not assign anything to the 4 named sections of CombFilterInputData, CombFilterOutputData, etc. will the linker simply allocate nothing to these addresses?

    In so doing this would leave CLAMemory free to contain anything without restriction correct?


    I'm thinking this would solve the problem with one linker CMD file, as long as it interprets the SPECIFIC memory named sections only when they are declared, and they don't cause an issue if they are not declared in the code.

    Does that make sense?  can you see anything wrong with this?  In this way I should be able to control the offset between these variables, and meet my other constraints.

    Thanks.

    Q:  Is there a way to pass the OFFSETS from the CODE to the LINKER?  So if I defined the above as BASE then BASE + LENGTHVar1 then BASE+LENGTHVar1 + LENGTHVar2... is there a way for me to

    #define LENGTHVar1 in the C/assembler code and pass that SAME definition to the linker CMD file so I can define BASE + LENGTHVar1 for the offset, as opposed to having to type it into the project file likner defines...  This creates a SAME Variable in two places problem...  Since the compiler (assembler) needs the OFFSET to compile, and the linker needs the offset to place the variables apart by this value, how can I share it?

  • Rob Barton said:
    Is there any way to have one linker CMD file that will work for both? or do I need to make two linker cmd files one for each configuration, and if so is there an automatic way like the compiler directives to automatically switch linker command files.  In this way both the sections, and the MEMORY would have to be set up so that the named section goes into the specified memory.

    You can have a unified linker command file. for the CLA build, you would have to define __TMS320C28XX_CLA__ in the linker properties. It might already be defined when --cla_support is turned on (doc SPRU513, table 8-10) but in case it isnt you can manually enter it as shown.

    You can then use it in the linker command file as you would a C file and conditionally link sections when this macro is defined.

    Rob Barton said:
     I suppose another way one could look at it is: is there a way to extract the offset between two variables, so instead of me constraining where they are at, can I extract the offset between them . Probably not since it needs to know at compile time what the offset is, but it's worth asking.

    you could extract the offset by assigning each variable to its own named section, attaching a LOAD_START (doc:SPRU513, 8.5.11.6)  symbol to both and then calculating the difference between them at run time. But you couldn't use this info in offset addressing in the CLA.

    Rob Barton said:
    Q:  If in the CPU build I do not assign anything to the 4 named sections of CombFilterInputData, CombFilterOutputData, etc. will the linker simply allocate nothing to these addresses?

    Yeah im pretty sure they will not be allocated.

    Rob Barton said:
    In so doing this would leave CLAMemory free to contain anything without restriction correct?

    It should. There will be memory holes at those locations and the linker will try to fill them.

    Rob Barton said:
    Q:  Is there a way to pass the OFFSETS from the CODE to the LINKER?

    I don't know of a way to do that. You might want to post this specific question to the compiler forum.