Search code examples
cassemblystructstm32

How to access a C struct member in ASM


I am using a mixture of C and ASM code to program an STM32H745 in STCubeIDE.

I have the following struct to ensure strict ordering of variables that are shared among the two cores CM4 and CM7:

struct miscSharedMemoryStruct {
   volatile uint32_t triggerPtr;
   volatile uint32_t bufferHead;
}
__attribute__((section(".sram3"))) struct miscSharedMemoryStruct miscSharedMemory;

The section .sram3 is defined in the linker script and accessing my variables in C code for both cores works well. So far, so good. However...

To enable the access of entries of miscSharedMemory, I set the variable as global in my ASM code:

.global miscSharedMemory

To be able for the assembler to recognize the right address, I added the following define statement to the header, in which the above struct is defined:

#define BUFFER_HEAD_ADR  ((uintptr_t)(&miscSharedMemory.bufferHead))

In the end, the idea is to get data from the adress above into a register in my ASM code:

ldr R1, =BUFFER_HEAD_ADR

However, when compiling my code, I get a

undefined reference to 'BUFFER_HEAD_ADR'

What is the correct way to make the assembler recognize the right address to my variable in the struct? I probably don't need to do the .global statement, as long as the address stored in BUFFER_HEAD_ADR is correct and recognized by the assembler, but I just don't know how to get it to work.


Solution

  • Address Calculation Solution for Assembly in STM32H745

    In tackling the challenge of dynamically calculating addresses in assembly code for an STM32H745 microcontroller, I've crafted a solution that prioritizes flexibility and transparency without resorting to script extraction or hardcoded values.

    1. Initial Problem: The objective was to load data from a specific address in assembly without hardcoding it, considering the evolving nature of the code and the necessity for predictability when accessing shared memory across different cores.

    2. Use of struct for Ordering: To maintain strict ordering of variables shared among cores, a struct named miscSharedMemoryStruct was introduced. This struct, residing in the .sram3 section, contains variables like triggerPtr and bufferHead.

    3. Dynamic Address Storage: The challenge lay in dynamically calculating the address of miscSharedMemory.bufferHead during compile time, as the actual address is unknown until the linker defines it.

    4. Register-Relative Load Solution: The core idea involves:

    1. The use of a register-relative load instead of a PC-relative load as used in my initial post
    2. The storage of the address in the .data section of the assembly code.

    5. C Code:

    struct miscSharedMemoryStruct {
       volatile uint32_t triggerPtr;
       volatile uint32_t bufferHead;
    } __attribute__((section(".sram3"))) struct miscSharedMemoryStruct miscSharedMemory;
    
    extern volatile uint32_t *bufferHeadAdr;
    bufferHeadAdr = (uint32_t *) &miscSharedMemory.bufferHead;
    

    6. ASM Code:

    .global bufferHeadAdr
    
    
    .data
    
    CBAdr:
    variableA:     .word 0
    variableB:     .word 0
    .
    .
    bufferHeadAdr: .word 0
    .
    .
    
    
    .text
    
    LDR R0, =CBAdr
    LDR R1, [R0, #bufferHeadAdr - CBAdr]
    

    In the assembly code, bufferHeadAdr is declared global, and its address is stored in the .data section. The actual address is then loaded into register R1 using register-relative addressing.

    Note that the utilization of LDR R0, =CBAdr incurs no runtime impact in my specific case. I declare R0 anyways and keep it unchanged throughout my code, facilitating extensive access to other data in my .data section.