Teensy 3.1 assembly language use of LDR/STR ???

Status
Not open for further replies.

RichardFerraro

Well-known member
I can't get past TeensyDuino assembler errors with LDR or STR. The following should copy a 16 bit inval to a 32-bit outval. I get:

Error: cannot represent T32_OFFSET_IMM relocation in this object file format

Any guidance here?

#include <stdint.h>

static inline uint32_t CopyInt32(uint16_t inVal) __attribute__((always_inline, unused));
static inline uint32_t CopyInt32(uint16_t inVal)
{
uint32_t outVal;
asm(
"mov r0, %[value] \n\t"
"str %[result], r0 \n\t"
: [result]"=r" (outVal) // outputs
: [value]"r" (inVal) // inputs
: "r0" // clobbers
);
return outVal;
}

void setup()
{
Serial.begin(9600);
}

void loop()
{
uint32_t outValInt32 = CopyInt32(0x1234);
Serial.println(outValInt32,HEX);
delay(100);

}
 
If you really want to write to memory, you need to restructure all this stuff.

First, outValInt32 would need to be an actual memory location. There are a couple ways to do this. The most explicit way is making it a static or global variable, so the compiler has to put it into memory. For example:

Code:
  static uint32_t outValInt32=0;

The implicit way would be taking the address and passing a pointer to a function. But if the function is inline or static (in the same file), the compiler can be quite crafty about optimizing away implicit pointers and keeping the variable in a register the whole time. If it's not static or global scope, there's no strict requirement for it to be in memory if the compiler is able to figure out what you're actually doing with that pointer.

Even if outValInt32 is allocated in RAM, "CopyInt32" is returning a value, and the ARM ABI says return values are in register r0 (which the compiler will pick automatically, if you let it). So CopyInt32 should not be writing to memory as you've defined it.

For a write to memory version, you'd define the function like this:

Code:
static inline void CopyInt32(uint32_t *addr, uint16_t inVal)

The other bug here incorrect ARM assembly syntax. The STR instruction syntax is this:

Code:
  STR r0, [r1]   // r0 = value to store, r1 = address to write

Your code has the args backwards, and it lacks the square brackets which the assembler expects. This is ARM's assembly syntax, which differs from AVR's syntax.

There's also no reason to use r0 as a temporary variable. ARM is able to store any register, using any other register as the address.

Here's a copy with all the problems fixed. I ran this on a Teensy 3.1 just now. It works. :)

Code:
#include <stdint.h>

static inline void CopyInt32(uint32_t *addr, uint16_t inVal) __attribute__((always_inline, unused));
static inline void CopyInt32(uint32_t *addr, uint16_t inVal)
{
asm(
"str %[value], [%[result]]\n\t"
:  // outputs
: [result]"r" (addr), [value]"r" (inVal) // inputs
:  // clobbers
);
}

void setup()
{
Serial.begin(9600);
}

uint32_t outValInt32;

void loop()
{
CopyInt32(&outValInt32, 0x1234);
Serial.println(outValInt32,HEX);
delay(100);

}
 
I just tried this with outValInt32 as a local variable, and I'm happy to report it works.

Code:
void loop()
{
uint32_t outValInt32;
CopyInt32(&outValInt32, 0xABCD);
Serial.println(outValInt32,HEX);
delay(100);
}

Here's the generated code: (with comments added in green)

Code:
000004dc <loop>:
     4dc:       b513            push    {r0, r1, r4, lr}      [COLOR="#008000"]r0 is pushed merely to create space on the stack[/COLOR]
     4de:       f64a 32cd       movw    r2, #43981      ; 0xabcd
     4e2:       ab01            add     r3, sp, #4
     4e4:       601a            str     r2, [r3, #0]          [COLOR="#008000"]here's the ASM from CopyInt32()[/COLOR]
     4e6:       4c07            ldr     r4, [pc, #28]   ; (504 <loop+0x28>)
     4e8:       9901            ldr     r1, [sp, #4]          [COLOR="#008000"]compiler need to read outValInt32 from memory[/COLOR]
     4ea:       2210            movs    r2, #16               [COLOR="#008000"]r2 is "HEX" constant[/COLOR]
     4ec:       2300            movs    r3, #0
     4ee:       4620            mov     r0, r4
     4f0:       f001 fe78       bl      21e4 <_ZN5Print11printNumberEmhh>
     4f4:       4620            mov     r0, r4
     4f6:       f001 fe67       bl      21c8 <_ZN5Print7printlnEv>
     4fa:       2064            movs    r0, #100        ; 0x64
     4fc:       f000 f9a6       bl      84c <delay>
     500:       bd1c            pop     {r2, r3, r4, pc}
     502:       bf00            nop
     504:       1fff901c        .word   0x1fff901c            [COLOR="#008000"]this is the instance "Serial"[/COLOR]
 
Another way to force something to be in memory instead of a register, is to use the "m" constraint which says the argument needs to be a general memory adddress, instead of "r" which says to use a general register. If you need specific registers or memory types, you will need to dive into the documentation for the ARM portion of the GCC to see what other constraint letters are used.
 
Thanks Paul, your code helped my understanding.

As Michael intuits, I need to assign to specific registers. I need an assembly routine that will encode an entire frame buffer (32-bit pixels) into the OctoWS2811 display buffer (each byte being a bit of a pixel for each of 8 strips). This routine will take as input two pointers to memory (frame buffer and display buffer). The C version is too slow for my needs.

I have sketched out the assembly language and I need all 12 registers (one for a pointer to the frame buffer memory, one for a pointer to the display buffer memory output, eight for one pixel from each strip, one input shift mask and one output shift mask. Thus, I need a single routine that, when running, will receive two memory pointers and perform the encoding utilizing each of the 12 available registers.

I was hoping that I could hire someone to write this routine for me but, based on a previous post, there were no takers. Any help in this regard would be greatly appreciated.

thanks,

Richard Ferraro
 
Status
Not open for further replies.
Back
Top