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.
_startand found incrt0.o, but you can write your own. It should set up any registers needed, and the callmain, and if thatmainreturns this assembly code should terminate the process/simulation. Sometimes_startalso passesargc,argv, andenvptomain.