Issue
In the last months I have implemented ad RV32I CPU on an FPGA. Until now I just tested it using some assembly code. This week I decided to try out a simple test program in C on it. The source code is as follows :
int main(){
register unsigned int a = 1;
register unsigned int b = 1;
while(1){
a = a << b;
if(a == 0x80000000){
a = 1;
}
}
return 0;
}
This is all plain and simple, so that I can see a walking one pattern in whichever register "a" gets assigned.
Doing an objdump I am still quite puzzled by the position in which the stack has been placed :
test.elf: file format elf32-littleriscv
Disassembly of section .text:
00000000 <main>:
0: ff010113 add sp,sp,-16
4: 00112623 sw ra,12(sp)
8: 00812423 sw s0,8(sp)
c: 00912223 sw s1,4(sp)
10: 01010413 add s0,sp,16
14: 00100093 li ra,1
18: 00100493 li s1,1
1c: 009090b3 sll ra,ra,s1
20: 800007b7 lui a5,0x80000
24: fef09ce3 bne ra,a5,1c <main+0x1c>
28: 00100093 li ra,1
2c: ff1ff06f j 1c <main+0x1c>
Given that I say in the linker-script where my memory is, I am not understanding why the compiler uses the highest possible address below the 4GB limit as the stack base address. This can be seen in the first four instructions, where we are saving values to the stack. My linker script is the following :
ENTRY(main)
BRAM_SIZE = 1024;
MEMORY{
INSTR(RX) : ORIGIN =0x00000000 , LENGTH = BRAM_SIZE
DATA(RWX) : ORIGIN =0x01000000 , LENGTH = BRAM_SIZE
}
STACK_SIZE = 0x100;
/* Section Definitions */
SECTIONS
{
.text :
{
KEEP(*(.vectors .vectors.*))
*(.text*)
*(.rodata*)
} > INSTR
/* .bss section which is used for uninitialized data */
.bss (NOLOAD) :
{
*(.bss*)
*(COMMON)
} > DATA
.data :
{
*(.data*);
} > DATA AT >INSTR
/* stack section */
.stack (NOLOAD):
{
. = ALIGN(8);
. = . + STACK_SIZE;
. = ALIGN(8);
} > DATA
_end = . ;
}
I am compiling with the following commands and toolchains :
riscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -ffreestanding -T linkerscript_reduced.ld -o test.elf test.c
What am I doing wrong? Thanks in advance and sorry for the noob question.
Solution
Since you add the option "-nostdlib -ffreestanding", you need to provide each and any detail yourself. This includes the setup of the C environment as promised by the standard, and anything your specific target expects.
This says the documentation of the link options (emphasized by me):
-nostdlib
Do not use the standard system startup files or libraries when linking. No startup files and only the libraries you specify are passed to the linker, and options specifying linkage of the system libraries, such as
-static-libgcc
or-shared-libgcc
, are ignored.
And this says the documentation of the C dialect options:
-ffreestanding
Assert that compilation targets a freestanding environment. This implies
-fno-builtin
. A freestanding environment is one in which the standard library may not exist, and program startup may not necessarily be atmain
.
Therefore, you need to add code that initializes the stack pointer to the value you want it to have, zeroes .bss
, copies initialized data (INSTR
memory) to the .data
section (DATA
memory), and finally calls main()
. This is commonly written in assembler, but with a grain of inline assembler and in simple cases like yours, you can write a C source as well. Don't forget to think about what should happen, if main()
returns.
Answered By - the busybee Answer Checked By - Timothy Miller (WPSolving Admin)