I have implemented a basic FIFO data structure with its related functions/subroutines in a FIFO.s file and want to use them in the main function inside a main.c file;
// FIFO.s
.syntax unified
.cpu cortex-m4
.fpu softvfp
.thumb
.equ FIFO_CAPACITY_POINTER_SIZE, 4
.equ FIFO_LENGTH_POINTER_SIZE, 4
.equ FIFO_PUT_POINTER_SIZE, 4
.equ FIFO_GET_POINTER_SIZE, 4
.equ FIFO_DATA_SIZE, 4
.equ FIFO_CAPACITY, 10
.equ FIFO_Size, FIFO_CAPACITY_POINTER_SIZE + FIFO_LENGTH_POINTER_SIZE + \
FIFO_PUT_POINTER_SIZE + FIFO_GET_POINTER_SIZE + \
(FIFO_CAPACITY * FIFO_DATA_SIZE)
.section .data
FIFO:
FIFO_CAPACITY_POINTER: .space FIFO_CAPACITY_POINTER_SIZE
FIFO_LENGTH_POINTER: .space FIFO_LENGTH_POINTER_SIZE
FIFO_PUT_POINTER: .space FIFO_PUT_POINTER_SIZE
FIFO_GET_POINTER: .space FIFO_GET_POINTER_SIZE
FIFO_DATA: .space (FIFO_CAPACITY * FIFO_DATA_SIZE)
.section .text
.globl FIFO_Init
.globl FIFO_Put
.globl FIFO_Get
FIFO_Init:
LDR R6, =FIFO_CAPACITY
LDR R7, =FIFO_CAPACITY_POINTER
STR R6, [R7]
MOV R6, #0
LDR R7, =FIFO_LENGTH_POINTER
STR R6, [R7]
LDR R6, =FIFO_DATA
LDR R7, =FIFO_PUT_POINTER
STR R6, [R7]
LDR R7, =FIFO_GET_POINTER
STR R6, [R7]
// Return.
BX LR
////////////////////////////////////////////////////////////////////////////////
FIFO_Put:
/*
If Length >= Capacity then FIFO is full and we return, doing nothing.
*/
LDR R7, =FIFO_LENGTH_POINTER
LDR R6, [R7]
LDR R5, =FIFO_CAPACITY_POINTER
LDR R5, [R5]
CMP R6, R5
IT GE
BXGE LR
/*
Add 1 to Length.
*/
ADD R6, #1
STR R6, [R7]
/*
Store the value passed to us in R0 where FIFO_PUT_POINTER is pointing.
*/
LDR R7, =FIFO_PUT_POINTER
LDR R6, [R7]
STR R0, [R6]
/*
If FIFO_PUT_POINTER is going out of range then reset it to FIFO_DATA;
Else increment it by FIFO_DATA_SIZE.
*/
ADD R6, #FIFO_DATA_SIZE
LDR R5, =(FIFO + FIFO_Size)
CMP R6, R5
IT GE
LDRGE R6, =FIFO_DATA
STR R6, [R7]
/*
Return.
*/
BX LR
////////////////////////////////////////////////////////////////////////////////
FIFO_Get:
/*
If Length = 0 then FIFO is empty and we return, doing nothing.
*/
LDR R7, =FIFO_LENGTH_POINTER
LDR R6, [R7]
CMP R6, #0
IT LE
BXLE LR
/*
Subtract 1 from Length.
*/
SUB R6, #1
STR R6, [R7]
/*
Put the return value pointed at by FIFO_GET_PTR in R0.
*/
LDR R7, =FIFO_GET_POINTER
LDR R6, [R7]
LDR R0, [R6]
/*
If FIFO_GET_POINTER is going out of range then reset it to FIFO_DATA;
Else increment it by FIFO_DATA_SIZE.
*/
ADD R6, #FIFO_DATA_SIZE
LDR R5, =(FIFO + FIFO_Size)
CMP R6, R5
IT GE
LDRGE R6, =FIFO_DATA
STR R6, [R7]
/*
Return.
*/
BX LR
////////////////////////////////////////////////////////////////////////////////
.align
.end
This data structure is stored in memory from 0x20000000 to 0x20000038.
My main.c file looks like this:
// main.c
#include <stdint.h>
void FIFO_Init(void);
void FIFO_Put(uint32_t data);
uint32_t FIFO_Get(void);
int main(void)
{
FIFO_Init();
FIFO_Put(1);
FIFO_Put(2);
FIFO_Put(3);
FIFO_Put(4);
FIFO_Put(5);
FIFO_Put(6);
FIFO_Put(7);
FIFO_Put(8);
FIFO_Put(9);
FIFO_Put(10);
// Will not be added to FIFO!
FIFO_Put(11);
FIFO_Put(12);
uint32_t sum = FIFO_Get(); // Error: FIFO_PUT_POINTER overwritten by "sum".
sum += FIFO_Get(); // Error: FIFO_PUT_POINTER overwritten by "sum".
sum += FIFO_Get(); // Error: FIFO_PUT_POINTER overwritten by "sum".
sum += FIFO_Get(); // Error: FIFO_PUT_POINTER overwritten by "sum".
sum += FIFO_Get(); // Error: FIFO_PUT_POINTER overwritten by "sum".
sum += FIFO_Get(); // Error: FIFO_PUT_POINTER overwritten by "sum".
sum += FIFO_Get(); // Error: FIFO_PUT_POINTER overwritten by "sum".
sum += FIFO_Get(); // Error: FIFO_PUT_POINTER overwritten by "sum".
sum += FIFO_Get(); // Error: FIFO_PUT_POINTER overwritten by "sum".
sum += FIFO_Get(); // Error: FIFO_PUT_POINTER overwritten by "sum".
FIFO_Put(sum); // Error: FIFO_PUT_POINTER has invalid address 0x00000037.
return 0;
}
Build commands that STM32CubeIDE version 1.19.0 uses (the Assembler and Linker seem to be version 2.42.0):
08:24:55 **** Build of configuration Debug for project 22_WorkingWithDataStructures ****
make -j4 all
arm-none-eabi-gcc -mcpu=cortex-m4 -g3 -DDEBUG -c -x assembler-with-cpp -MMD -MP -MF"Startup/startup_stm32f303retx.d" -MT"Startup/startup_stm32f303retx.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Startup/startup_stm32f303retx.o" "../Startup/startup_stm32f303retx.s"
arm-none-eabi-gcc -mcpu=cortex-m4 -g3 -DDEBUG -c -x assembler-with-cpp -MMD -MP -MF"Src/FIFO.d" -MT"Src/FIFO.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Src/FIFO.o" "../Src/FIFO.s"
arm-none-eabi-gcc "../Src/main.c" -mcpu=cortex-m4 -std=gnu11 -g3 -DDEBUG -DSTM32 -DSTM32F3 -DSTM32F303RETx -c -I../Inc -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -fcyclomatic-complexity -MMD -MP -MF"Src/main.d" -MT"Src/main.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Src/main.o"
arm-none-eabi-gcc "../Src/syscalls.c" -mcpu=cortex-m4 -std=gnu11 -g3 -DDEBUG -DSTM32 -DSTM32F3 -DSTM32F303RETx -c -I../Inc -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -fcyclomatic-complexity -MMD -MP -MF"Src/syscalls.d" -MT"Src/syscalls.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Src/syscalls.o"
arm-none-eabi-gcc "../Src/sysmem.c" -mcpu=cortex-m4 -std=gnu11 -g3 -DDEBUG -DSTM32 -DSTM32F3 -DSTM32F303RETx -c -I../Inc -O0 -ffunction-sections -fdata-sections -Wall -fstack-usage -fcyclomatic-complexity -MMD -MP -MF"Src/sysmem.d" -MT"Src/sysmem.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Src/sysmem.o"
arm-none-eabi-gcc -mcpu=cortex-m4 -g3 -DDEBUG -c -x assembler-with-cpp -MMD -MP -MF"Inc/STM32F303RE.d" -MT"Inc/STM32F303RE.o" --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -o "Inc/STM32F303RE.o" "../Inc/STM32F303RE.s"
arm-none-eabi-gcc -o "22_WorkingWithDataStructures.elf" @"objects.list" -mcpu=cortex-m4 -T"X:\XXXXXXXXXX\22_WorkingWithDataStructures\STM32F303RETX_FLASH.ld" --specs=nosys.specs -Wl,-Map="22_WorkingWithDataStructures.map" -Wl,--gc-sections -static --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -Wl,--start-group -lc -lm -Wl,--end-group
Finished building target: 22_WorkingWithDataStructures.elf
arm-none-eabi-size 22_WorkingWithDataStructures.elf
arm-none-eabi-objdump -h -S 22_WorkingWithDataStructures.elf > "22_WorkingWithDataStructures.list"
text data bss dec hex filename
1104 56 1568 2728 aa8 22_WorkingWithDataStructures.elf
Finished building: default.size.stdout
Finished building: 22_WorkingWithDataStructures.list
08:25:57 Build Finished. 0 errors, 0 warnings. (took 1m:2s.223ms)
The value of local variable number (24) will be stored in memory address 0x20000008 which was reserved in the FIFO.s file to hold the Put_Ptr value of the FIFO data structure! In other words, number and Put_ptr share the same memory location!
I've checked and debugged all my functions/subroutines and they don't have any problems (it's not FIFO_Get that's doing the overwriting).
If I declare number in the .data section of the FIFO.s file and import it to my main.c file then everything works! But when I declare it as a local variable in the main function of my main.c file, it doesn't work.
Why is this happening and how can it be fixed?
Edit: As @AndreyTurkin (my thanks to him) has said in the comments, The problem was that I was using register R7 in my subroutines in FIFO.s so I changed them to only work with registers R0-R3 and there seems to be no problems.
New version of FIFO.s:
.syntax unified
.cpu cortex-m4
.fpu softvfp
.thumb
.equ FIFO_CAPACITY_POINTER_SIZE, 4
.equ FIFO_LENGTH_POINTER_SIZE, 4
.equ FIFO_PUT_POINTER_SIZE, 4
.equ FIFO_GET_POINTER_SIZE, 4
.equ FIFO_DATA_SIZE, 4
.equ FIFO_CAPACITY, 10
.equ FIFO_SIZE, FIFO_CAPACITY_POINTER_SIZE + FIFO_LENGTH_POINTER_SIZE + \
FIFO_PUT_POINTER_SIZE + FIFO_GET_POINTER_SIZE + \
(FIFO_CAPACITY * FIFO_DATA_SIZE)
.section .data
FIFO:
FIFO_CAPACITY_POINTER: .space FIFO_CAPACITY_POINTER_SIZE
FIFO_LENGTH_POINTER: .space FIFO_LENGTH_POINTER_SIZE
FIFO_PUT_POINTER: .space FIFO_PUT_POINTER_SIZE
FIFO_GET_POINTER: .space FIFO_GET_POINTER_SIZE
FIFO_DATA: .space (FIFO_CAPACITY * FIFO_DATA_SIZE)
.section .text
.globl FIFO_Init
.globl FIFO_Put
.globl FIFO_Get
.globl FIFO_Capacity
.globl FIFO_Length
.globl FIFO_Size
FIFO_Init:
/*
FIFO_CAPACITY_POINTER will hold the value "FIFO_CAPACITY".
*/
LDR R0, =FIFO_CAPACITY
LDR R1, =FIFO_CAPACITY_POINTER
STR R0, [R1], #FIFO_CAPACITY_POINTER_SIZE
/*
FIFO_LENGTH_POINTER will hold the value 0.
*/
MOV R0, #0
STR R0, [R1], #FIFO_LENGTH_POINTER_SIZE
/*
Both FIFO_PUT_POINTER and FIFO_GET_POINTER will hold the starting
address of "FIFO_DATA".
*/
LDR R0, =FIFO_DATA
STR R0, [R1], #FIFO_PUT_POINTER_SIZE
STR R0, [R1]
/*
Return.
*/
BX LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Put:
/*
If Length >= Capacity then FIFO is full and we return 0 (failed).
*/
LDR R3, =FIFO_CAPACITY_POINTER
LDR R1, [R3], #FIFO_CAPACITY_POINTER_SIZE
LDR R2, [R3]
CMP R2, R1
ITT GE
MOVGE R0, #0
BXGE LR
/*
Add 1 to Length.
*/
ADD R2, #1
STR R2, [R3], #FIFO_LENGTH_POINTER_SIZE
/*
Store the value passed to us in R0 where FIFO_PUT_POINTER is pointing.
*/
LDR R1, [R3]
STR R0, [R1]
/*
If FIFO_PUT_POINTER is going out of range then reset it to FIFO_DATA;
Else increment it by FIFO_DATA_SIZE.
*/
ADD R1, #FIFO_DATA_SIZE
LDR R0, =(FIFO + FIFO_SIZE)
CMP R1, R0
IT GE
LDRGE R1, =FIFO_DATA
STR R1, [R3]
/*
Return with "return code" 1.
*/
MOV R0, #1
BX LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Get:
/*
If Length = 0 then FIFO is empty and we return, doing nothing.
*/
LDR R2, =FIFO_LENGTH_POINTER
LDR R1, [R2]
CMP R1, #0
IT LE
BXLE LR
/*
Subtract 1 from Length.
*/
SUB R1, #1
STR R1, [R2], #(FIFO_LENGTH_POINTER_SIZE + FIFO_PUT_POINTER_SIZE)
/*
Put return value in R0.
*/
LDR R1, [R2]
LDR R0, [R1]
/*
If FIFO_GET_POINTER is going out of range then reset it to FIFO_DATA;
Else increment it by FIFO_DATA_SIZE.
*/
ADD R1, #FIFO_DATA_SIZE
LDR R3, =(FIFO + FIFO_SIZE)
CMP R1, R3
IT GE
LDRGE R1, =FIFO_DATA
STR R1, [R2]
/*
Return.
*/
BX LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Capacity:
LDR R0, =FIFO_CAPACITY
/*
Return.
*/
BX LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Length:
LDR R0, =FIFO_LENGTH_POINTER
LDR R0, [R0]
/*
Return.
*/
BX LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Size:
LDR R0, =FIFO_SIZE
/*
Return.
*/
BX LR
///////////////////////////////////////////////////////////////////////////////
.align
.end
P.S: I'm using STM32CubeIDE version 1.19.0 that uses the ARM GNU Assembler to program a STM32 NUCLEO-F303RE micro-controller (if it's a necessary piece of information for answering this question).
As @AndreyTurkin has said in the comments (my thanks to him), in my subroutines in FIFO.s I used and modified register R7, which according to ARM's AAPCS ABI, is callee-preserved and is used in the main function of main.c as a Frame Pointer. That caused the memory corruption problem.
I changed my subroutines in FIFO.s to only use registers R0 through R3, and there seems to be no more problems.
New version of FIFO.s:
.syntax unified
.cpu cortex-m4
.fpu softvfp
.thumb
.equ FIFO_CAPACITY_POINTER_SIZE, 4
.equ FIFO_LENGTH_POINTER_SIZE, 4
.equ FIFO_PUT_POINTER_SIZE, 4
.equ FIFO_GET_POINTER_SIZE, 4
.equ FIFO_DATA_SIZE, 4
.equ FIFO_CAPACITY, 10
.equ FIFO_SIZE, FIFO_CAPACITY_POINTER_SIZE + FIFO_LENGTH_POINTER_SIZE + \
FIFO_PUT_POINTER_SIZE + FIFO_GET_POINTER_SIZE + \
(FIFO_CAPACITY * FIFO_DATA_SIZE)
.section .data
FIFO:
FIFO_CAPACITY_POINTER: .space FIFO_CAPACITY_POINTER_SIZE
FIFO_LENGTH_POINTER: .space FIFO_LENGTH_POINTER_SIZE
FIFO_PUT_POINTER: .space FIFO_PUT_POINTER_SIZE
FIFO_GET_POINTER: .space FIFO_GET_POINTER_SIZE
FIFO_DATA: .space (FIFO_CAPACITY * FIFO_DATA_SIZE)
.section .text
.globl FIFO_Init
.globl FIFO_Put
.globl FIFO_Get
.globl FIFO_Capacity
.globl FIFO_Length
.globl FIFO_Size
FIFO_Init:
/*
FIFO_CAPACITY_POINTER will hold the value "FIFO_CAPACITY".
*/
LDR R0, =FIFO_CAPACITY
LDR R1, =FIFO_CAPACITY_POINTER
STR R0, [R1], #FIFO_CAPACITY_POINTER_SIZE
/*
FIFO_LENGTH_POINTER will hold the value 0.
*/
MOV R0, #0
STR R0, [R1], #FIFO_LENGTH_POINTER_SIZE
/*
Both FIFO_PUT_POINTER and FIFO_GET_POINTER will hold the starting
address of "FIFO_DATA".
*/
LDR R0, =FIFO_DATA
STR R0, [R1], #FIFO_PUT_POINTER_SIZE
STR R0, [R1]
/*
Return.
*/
BX LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Put:
/*
If Length >= Capacity then FIFO is full and we return 0 (failed).
*/
LDR R3, =FIFO_CAPACITY_POINTER
LDR R1, [R3], #FIFO_CAPACITY_POINTER_SIZE
LDR R2, [R3]
CMP R2, R1
ITT GE
MOVGE R0, #0
BXGE LR
/*
Add 1 to Length.
*/
ADD R2, #1
STR R2, [R3], #FIFO_LENGTH_POINTER_SIZE
/*
Store the value passed to us in R0 where FIFO_PUT_POINTER is pointing.
*/
LDR R1, [R3]
STR R0, [R1]
/*
If FIFO_PUT_POINTER is going out of range then reset it to FIFO_DATA;
Else increment it by FIFO_DATA_SIZE.
*/
ADD R1, #FIFO_DATA_SIZE
LDR R0, =(FIFO + FIFO_SIZE)
CMP R1, R0
IT GE
LDRGE R1, =FIFO_DATA
STR R1, [R3]
/*
Return with "return code" 1.
*/
MOV R0, #1
BX LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Get:
/*
If Length = 0 then FIFO is empty and we return, doing nothing.
*/
LDR R2, =FIFO_LENGTH_POINTER
LDR R1, [R2]
CMP R1, #0
IT LE
BXLE LR
/*
Subtract 1 from Length.
*/
SUB R1, #1
STR R1, [R2], #(FIFO_LENGTH_POINTER_SIZE + FIFO_PUT_POINTER_SIZE)
/*
Put return value in R0.
*/
LDR R1, [R2]
LDR R0, [R1]
/*
If FIFO_GET_POINTER is going out of range then reset it to FIFO_DATA;
Else increment it by FIFO_DATA_SIZE.
*/
ADD R1, #FIFO_DATA_SIZE
LDR R3, =(FIFO + FIFO_SIZE)
CMP R1, R3
IT GE
LDRGE R1, =FIFO_DATA
STR R1, [R2]
/*
Return.
*/
BX LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Capacity:
LDR R0, =FIFO_CAPACITY
/*
Return.
*/
BX LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Length:
LDR R0, =FIFO_LENGTH_POINTER
LDR R0, [R0]
/*
Return.
*/
BX LR
///////////////////////////////////////////////////////////////////////////////
FIFO_Size:
LDR R0, =FIFO_SIZE
/*
Return.
*/
BX LR
///////////////////////////////////////////////////////////////////////////////
.align
.end