Scheintod
Scheintod

Reputation: 8105

armv7-m bare metal ldr/str symbolic memory

so I know questions for ldr/str on arm are countless. Perhaps this is another twist (not probable) or I'm just missing something (more probable.)

So this is bare metal and I want to load/store some variable in memory. And because I insist I want to give it a name. Naively I could write:

.section .bss
var: .word 0

.section .text
str r0, var 

(having a custom linker script which puts .bss in ram and .text in flash)

Of cause this doesn't work because instructions are 32bit and only have place for some smaller immediate. And the instructions I'm talking about live in flash which is 0x8000000+x and the variable is to be stored in memory which is somewhere in the 0x20000000+y.

Manually I know quite some ways to solve this:

Every of these variants work but neither of these variants let me use the label which I really want to use.

So what am I missing here. Is my idea for the linker script and labels bogus? Is there some assembler feature I didn't see? Something completely different?

Upvotes: 2

Views: 655

Answers (1)

fuz
fuz

Reputation: 92966

One way you could use symbolic names for variables in static storage is to define a structure for your variables. This allows you to load the base address of your structure into a register and then access structure members using symbolic names relative to the base address. For example, you could do:

        .struct 0          @ start a new structure
foo:    .skip 4            @ length of foo
bar:    .skip 4            @ length of bar
baz:    .skip 4            @ length of baz
len:                       @ total length of the structure

        .section .bss      @ switch to the BSS (uninitialised data) section
        .balign 4          @ align to 4 bytes
variables:
        .space len         @ reserve space for your variables

        .section .text     @ switch to the text (code) section

        ...
        ldr r0, =variables @ load r0 with the base address of your variables
        ldr r1, [r0+#foo]  @ access foo
        str r2, [r0+#bar]  @ access bar
        ldr r3, [r0+#baz]  @ access baz

This is pretty much the closest you can get to symbolic names for variables in static storage. If the variables are on the stack, you can use a similar approach using the frame pointer (or stack pointer) as the base address. The operand to .struct is the base address of the structure for which you can chose any value you like.

As for movw and movt. These offer a tiny performance advantage on some microarchitectures over ldr ..., =... as they do not require data fetches to the text section. This makes no difference on armv7-m targets as far as I know; also, movw and movt consume two extra bytes versus ldr with an = operand. I thus recommend you to stick with ldr and an = operand. The usage of movw and movt is like this:

        movw r0, :lower16:foo  @ load lower 16 bit of foo's address into r0
        movt r0, :upper16:foo  @ or higher 16 bit of foo's address into r0

These two have to be issued in this specific order as movw clears the upper 16 bit. The prefixes :lower16: and :upper16: select appropriate relocation types referring to just the lower and upper 16 bit of the symbol's address. You can make a macro to make this easier to type:

        .macro addr reg, sym
        movw \reg, :lower16:\sym
        movt \reg, :upper16:\sym
        .endm

This allows you to write

        addr r0, foo

to generate the aforementioned movw and movt pair.

Upvotes: 2

Related Questions