jFoster
Posts: 57
Joined: Fri Jul 03, 2015 7:32 pm
Location: British Columbia, Canada
Contact: Website

Initializing RAM on Raspberry Pi 2

Mon Dec 21, 2015 10:59 pm

Hi All,

I have a question about the Raspberry Pi 2's RAM at a bare metal startup. Do I need to "set up" the RAM located after the .text and the .data sections of the code if I want to use it during the program? I have a bit of code to just read and write 32 bit values to RAM that just turns on the OK LED if the read/write returns the same value:

Code: Select all

/* The base address of the GPIO peripheral (ARM Physical Address) */
#ifdef RPI2
    #define GPIO_BASE       0x3F200000UL
#else
    #define GPIO_BASE       0x20200000UL
#endif

#if defined( RPIBPLUS ) || defined( RPI2 )
    #define LED_GPFSEL      GPIO_GPFSEL4
    #define LED_GPFBIT      21
    #define LED_GPSET       GPIO_GPSET1
    #define LED_GPCLR       GPIO_GPCLR1
    #define LED_GPIO_BIT    15
#else
    #define LED_GPFSEL      GPIO_GPFSEL1
    #define LED_GPFBIT      18
    #define LED_GPSET       GPIO_GPSET0
    #define LED_GPCLR       GPIO_GPCLR0
    #define LED_GPIO_BIT    16
#endif

#define GPIO_GPFSEL0    0
#define GPIO_GPFSEL1    1
#define GPIO_GPFSEL2    2
#define GPIO_GPFSEL3    3
#define GPIO_GPFSEL4    4
#define GPIO_GPFSEL5    5

#define GPIO_GPSET0     7
#define GPIO_GPSET1     8

#define GPIO_GPCLR0     10
#define GPIO_GPCLR1     11

#define GPIO_GPLEV0     13
#define GPIO_GPLEV1     14

#define GPIO_GPEDS0     16
#define GPIO_GPEDS1     17

#define GPIO_GPREN0     19
#define GPIO_GPREN1     20

#define GPIO_GPFEN0     22
#define GPIO_GPFEN1     23

#define GPIO_GPHEN0     25
#define GPIO_GPHEN1     26

#define GPIO_GPLEN0     28
#define GPIO_GPLEN1     29

#define GPIO_GPAREN0    31
#define GPIO_GPAREN1    32

#define GPIO_GPAFEN0    34
#define GPIO_GPAFEN1    35

#define GPIO_GPPUD      37
#define GPIO_GPPUDCLK0  38
#define GPIO_GPPUDCLK1  39

/** GPIO Register set */
volatile unsigned int* gpio;

void put32(unsigned long int addr, unsigned long int val) {
	volatile unsigned long int *a;
	a = addr;
	*a = val;
}
unsigned long int get32(unsigned long int addr) {
	volatile unsigned long int *a;
	a = addr;
	return *a;
}

/** Main function - we'll never return from here */
int main(void) {
    gpio = (unsigned int*)GPIO_BASE;
    gpio[LED_GPFSEL] |= (1 << LED_GPFBIT);
	volatile unsigned long int val;
	put32(&val, 0x2336);
	if(get32(&val) == 0x2336) gpio[LED_GPSET] = (1 << LED_GPIO_BIT);
	else gpio[LED_GPCLR] = (1 << LED_GPIO_BIT);
    while(1) {
    }
}
This code works great, the OK LED lights up and everything. The problem is when I replace val with something like 0x00FFFFFF (to access a different location in RAM), like this...

Code: Select all

put32(0x00FFFFFF, 0x2336);
if(get32(0x00FFFFFF) == 0x2336) gpio[LED_GPSET] = (1 << LED_GPIO_BIT);
else gpio[LED_GPCLR] = (1 << LED_GPIO_BIT);
...nothing works. To my understanding, shouldn't this be accessing a RAM location just like the first code is accessing the address of var? This led me to believe that the RAM needed to be initialized for some reason because it worked when I used the address of an already initialized variable.

Any help on this would be great, hope I'm not missing something too obvious :)

Judd

User avatar
Ultibo
Posts: 158
Joined: Wed Sep 30, 2015 10:29 am
Location: Australia
Contact: Website

Re: Initializing RAM on Raspberry Pi 2

Mon Dec 21, 2015 11:51 pm

jFoster wrote:Hi All,

I have a question about the Raspberry Pi 2's RAM at a bare metal startup. Do I need to "set up" the RAM located after the .text and the .data sections of the code if I want to use it during the program? I have a bit of code to just read and write 32 bit values to RAM that just turns on the OK LED if the read/write returns the same value:

Code: Select all

/* The base address of the GPIO peripheral (ARM Physical Address) */
#ifdef RPI2
    #define GPIO_BASE       0x3F200000UL
#else
    #define GPIO_BASE       0x20200000UL
#endif

#if defined( RPIBPLUS ) || defined( RPI2 )
    #define LED_GPFSEL      GPIO_GPFSEL4
    #define LED_GPFBIT      21
    #define LED_GPSET       GPIO_GPSET1
    #define LED_GPCLR       GPIO_GPCLR1
    #define LED_GPIO_BIT    15
#else
    #define LED_GPFSEL      GPIO_GPFSEL1
    #define LED_GPFBIT      18
    #define LED_GPSET       GPIO_GPSET0
    #define LED_GPCLR       GPIO_GPCLR0
    #define LED_GPIO_BIT    16
#endif

#define GPIO_GPFSEL0    0
#define GPIO_GPFSEL1    1
#define GPIO_GPFSEL2    2
#define GPIO_GPFSEL3    3
#define GPIO_GPFSEL4    4
#define GPIO_GPFSEL5    5

#define GPIO_GPSET0     7
#define GPIO_GPSET1     8

#define GPIO_GPCLR0     10
#define GPIO_GPCLR1     11

#define GPIO_GPLEV0     13
#define GPIO_GPLEV1     14

#define GPIO_GPEDS0     16
#define GPIO_GPEDS1     17

#define GPIO_GPREN0     19
#define GPIO_GPREN1     20

#define GPIO_GPFEN0     22
#define GPIO_GPFEN1     23

#define GPIO_GPHEN0     25
#define GPIO_GPHEN1     26

#define GPIO_GPLEN0     28
#define GPIO_GPLEN1     29

#define GPIO_GPAREN0    31
#define GPIO_GPAREN1    32

#define GPIO_GPAFEN0    34
#define GPIO_GPAFEN1    35

#define GPIO_GPPUD      37
#define GPIO_GPPUDCLK0  38
#define GPIO_GPPUDCLK1  39

/** GPIO Register set */
volatile unsigned int* gpio;

void put32(unsigned long int addr, unsigned long int val) {
	volatile unsigned long int *a;
	a = addr;
	*a = val;
}
unsigned long int get32(unsigned long int addr) {
	volatile unsigned long int *a;
	a = addr;
	return *a;
}

/** Main function - we'll never return from here */
int main(void) {
    gpio = (unsigned int*)GPIO_BASE;
    gpio[LED_GPFSEL] |= (1 << LED_GPFBIT);
	volatile unsigned long int val;
	put32(&val, 0x2336);
	if(get32(&val) == 0x2336) gpio[LED_GPSET] = (1 << LED_GPIO_BIT);
	else gpio[LED_GPCLR] = (1 << LED_GPIO_BIT);
    while(1) {
    }
}
This code works great, the OK LED lights up and everything. The problem is when I replace val with something like 0x00FFFFFF (to access a different location in RAM), like this...

Code: Select all

put32(0x00FFFFFF, 0x2336);
if(get32(0x00FFFFFF) == 0x2336) gpio[LED_GPSET] = (1 << LED_GPIO_BIT);
else gpio[LED_GPCLR] = (1 << LED_GPIO_BIT);
...nothing works. To my understanding, shouldn't this be accessing a RAM location just like the first code is accessing the address of var? This led me to believe that the RAM needed to be initialized for some reason because it worked when I used the address of an already initialized variable.

Any help on this would be great, hope I'm not missing something too obvious :)

Judd
Hi,

There is nothing special you need to do to initialize the RAM on the RPi2 from bare metal, the GPU has already done the low level initialization and you can just read and write to it in any way you like.

The one thing that stands out in your code is the address in the second example (0x00FFFFFF) which is not word (4 byte) aligned and might be the cause of your problem. The ARM is fussy about alignment for data access so if you used 0x00FFFFF0 or 0x01000000 then that might solve the issue.

Ultibo
Ultibo.org | Make something amazing
https://ultibo.org

Threads, multi-core, OpenGL, Camera, FAT, NTFS, TCP/IP, USB and more in 3MB with 2 second boot!

jFoster
Posts: 57
Joined: Fri Jul 03, 2015 7:32 pm
Location: British Columbia, Canada
Contact: Website

Re: Initializing RAM on Raspberry Pi 2

Tue Dec 22, 2015 12:22 am

Thanks for that, solved the problem really easily. Just to be clear on word alignment, if I make a new function like this:

Code: Select all

void put8(unsigned long int addr, char val) {
	volatile char *a;
	a = addr;
	*a = val;
}
then I should be able to write to an odd address like 0x00FFFFFF?

User avatar
Ultibo
Posts: 158
Joined: Wed Sep 30, 2015 10:29 am
Location: Australia
Contact: Website

Re: Initializing RAM on Raspberry Pi 2

Tue Dec 22, 2015 1:29 am

jFoster wrote:Thanks for that, solved the problem really easily. Just to be clear on word alignment, if I make a new function like this:

Code: Select all

void put8(unsigned long int addr, char val) {
	volatile char *a;
	a = addr;
	*a = val;
}
then I should be able to write to an odd address like 0x00FFFFFF?
It depends on what instructions the compiler produces from that, what you have written looks like it should work, but I'm not an all day everyday C programmer so maybe someone else who knows what the compiler would produce can confirm or deny that.
Ultibo.org | Make something amazing
https://ultibo.org

Threads, multi-core, OpenGL, Camera, FAT, NTFS, TCP/IP, USB and more in 3MB with 2 second boot!

mic_s
Posts: 92
Joined: Sun Oct 26, 2014 4:15 pm

Re: Initializing RAM on Raspberry Pi 2

Tue Dec 22, 2015 8:56 am

Code: Select all

void put8(unsigned long int addr, char val){
  *(volatile char*)addr = val;
}
Compiler output — ARM gcc 4.8.2 :

Code: Select all

        ...
        ldr     r3, [r7, #4]      -- load addr to r3
        ldrb   r2, [r7, #3]      -- load Byte val to r2  
        strb    r2, [r3]           -- store byte 
        ...
BTW .:

http://gcc.godbolt.org/ is a free interactive tool. You can select the compiler/options and see the generated assembler output. Nice and easy to use – when you are interested in how the C-compilers do there translation.
.

jFoster
Posts: 57
Joined: Fri Jul 03, 2015 7:32 pm
Location: British Columbia, Canada
Contact: Website

Re: Initializing RAM on Raspberry Pi 2

Tue Dec 22, 2015 5:17 pm

http://gcc.godbolt.org/ is a free interactive tool. You can select the compiler/options and see the generated assembler output. Nice and easy to use – when you are interested in how the C-compilers do there translation.
.
Thank you so much, that's great, definitely beats having to compile, assemble, link and then disassemble the elf file just to read the output! Now I have another question about this, even though the raspberry pi is already executing from 0x00008000 in RAM, is it able to jump into a place like 0x01000000 in RAM and start executing with a simple instruction? I tried using the put32() and get32() functions and writing hex files directly into the RAM and then jumping to it, but it doesn't seem to be working:

Code: Select all

//turn on OK LED
   put32(0x01000000, 0x3F04A0E3);		// asm volatile("mov r0,#0x3F000000");
	put32(0x01000004, 0x020680E3);		// asm volatile("orr r0,#0x200000");
	put32(0x01000008, 0x0120A0E3);		// asm volatile("mov r2,#1");
	put32(0x0100000C, 0x8227A0E1);		// asm volatile("mov r2,r2,lsl #15");
	put32(0x01000010, 0x202080E5);		// asm volatile("str r2,[r0,#0x20]");
	put32(0x01000014, 0xFEFFFFEA);      // asm volatile("loop: b loop");
	
	asm volatile("mov r0, #0x01000000");
	asm volatile("bx r0"); // jump to the code we just made
I also tried asm volatile("mov pc, 0x01000000"); but that doesn't work either.

Thanks again!

User avatar
Paeryn
Posts: 2708
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Initializing RAM on Raspberry Pi 2

Tue Dec 22, 2015 7:16 pm

Are you putting the integer into memory the correct way around? If you just load the 32bit value straight to memory then the bytes are in the wrong order, you need to reverse them

Code: Select all

//turn on OK LED
   put32(0x01000000, 0xE3A0043F);      // asm volatile("mov r0,#0x3F000000");
   put32(0x01000004, 0xE3800602);      // asm volatile("orr r0,#0x200000");
   put32(0x01000008, 0x0E3A0201);      // asm volatile("mov r2,#1");
   put32(0x0100000C, 0xE1A02782);      // asm volatile("mov r2,r2,lsl #15");
   put32(0x01000010, 0xE5802020);      // asm volatile("str r2,[r0,#0x20]");
   put32(0x01000014, 0xEAFFFFFE);      // asm volatile("loop: b loop");
   
   asm volatile("mov r0, #0x01000000");
   asm volatile("bx r0"); // jump to the code we just made
Last edited by Paeryn on Tue Dec 22, 2015 7:31 pm, edited 1 time in total.
She who travels light — forgot something.

jFoster
Posts: 57
Joined: Fri Jul 03, 2015 7:32 pm
Location: British Columbia, Canada
Contact: Website

Re: Initializing RAM on Raspberry Pi 2

Tue Dec 22, 2015 7:31 pm

Oops, putting them backwards works :oops: , I had read something about the bits being in reversed order before but since I was viewing the file in a hex editor I assumed they were put the right way around because that file would be loaded straight into the ARM. So putting it like this:
0x020680E3
instead of
0xE3800602
works.
Thanks for all of your help, glad it was something this easy!

User avatar
Paeryn
Posts: 2708
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Initializing RAM on Raspberry Pi 2

Tue Dec 22, 2015 7:56 pm

Values are stored in memory with the least significant byte first. You'll get used to it after a while ;-)
She who travels light — forgot something.

dwelch67
Posts: 955
Joined: Sat May 26, 2012 5:32 pm

Re: Initializing RAM on Raspberry Pi 2

Wed Dec 23, 2015 6:49 am

There is no reason for you to need to byteswap those instructions, what is your put32 doing, seems broken if you need to byteswap.

Code: Select all

.globl PUT32
PUT32:
    str r1,[r0]
    bx lr
a position independent raspberry pi 2 blinker (both leds)

Code: Select all

.globl _start
_start:
    mov r0,#0x3F000000
    orr r0,r0,#0x00200000
    ldr r1,[r0,#0x10]
    bic r1,r1,#0x00E00000
    orr r1,r1,#0x00200000
    str r1,[r0,#0x10]
    ldr r1,[r0,#0x0C]
    bic r1,r1,#0x00038000
    orr r1,r1,#0x00008000
    str r1,[r0,#0x0C]
    mov r2,#0x00008000
    mov r3,#0x00000008

mainloop:
    str r2,[r0,#0x20]
    str r3,[r0,#0x2C]
    mov r4,#0x100000
one:
    subs r4,r4,#1
    bne one
    str r2,[r0,#0x2C]
    str r3,[r0,#0x20]
    mov r4,#0x100000
two:
    subs r4,r4,#1
    bne two
    b mainloop
arm-none-eabi-as blinker.s -o blinker.o
arm-none-eabi-objdump -D blinker.o
shows us our instructions

Code: Select all

00000000 <_start>:
   0:	e3a0043f 	mov	r0, #1056964608	; 0x3f000000
   4:	e3800602 	orr	r0, r0, #2097152	; 0x200000
   8:	e5901010 	ldr	r1, [r0, #16]
   c:	e3c1160e 	bic	r1, r1, #14680064	; 0xe00000
  10:	e3811602 	orr	r1, r1, #2097152	; 0x200000
  14:	e5801010 	str	r1, [r0, #16]
  18:	e590100c 	ldr	r1, [r0, #12]
  1c:	e3c1190e 	bic	r1, r1, #229376	; 0x38000
  20:	e3811902 	orr	r1, r1, #32768	; 0x8000
  24:	e580100c 	str	r1, [r0, #12]
  28:	e3a02902 	mov	r2, #32768	; 0x8000
  2c:	e3a03008 	mov	r3, #8

00000030 <mainloop>:
  30:	e5802020 	str	r2, [r0, #32]
  34:	e580302c 	str	r3, [r0, #44]	; 0x2c
  38:	e3a04601 	mov	r4, #1048576	; 0x100000

0000003c <one>:
  3c:	e2544001 	subs	r4, r4, #1
  40:	1afffffd 	bne	3c <one>
  44:	e580202c 	str	r2, [r0, #44]	; 0x2c
  48:	e5803020 	str	r3, [r0, #32]
  4c:	e3a04601 	mov	r4, #1048576	; 0x100000

00000050 <two>:
  50:	e2544001 	subs	r4, r4, #1
  54:	1afffffd 	bne	50 <two>
  58:	eafffff4 	b	30 <mainloop>
which we turn into this

Code: Select all

int notmain ( void )
{
    unsigned int ra;

    ra=0x10000;

    PUT32(ra,0xe3a0043f); ra+=4;
    PUT32(ra,0xe3800602); ra+=4;
    PUT32(ra,0xe5901010); ra+=4;
    PUT32(ra,0xe3c1160e); ra+=4;
    PUT32(ra,0xe3811602); ra+=4;
    PUT32(ra,0xe5801010); ra+=4;
    PUT32(ra,0xe590100c); ra+=4;
    PUT32(ra,0xe3c1190e); ra+=4;
    PUT32(ra,0xe3811902); ra+=4;
    PUT32(ra,0xe580100c); ra+=4;
    PUT32(ra,0xe3a02902); ra+=4;
    PUT32(ra,0xe3a03008); ra+=4;
    PUT32(ra,0xe5802020); ra+=4;
    PUT32(ra,0xe580302c); ra+=4;
    PUT32(ra,0xe3a04601); ra+=4;
    PUT32(ra,0xe2544001); ra+=4;
    PUT32(ra,0x1afffffd); ra+=4;
    PUT32(ra,0xe580202c); ra+=4;
    PUT32(ra,0xe5803020); ra+=4;
    PUT32(ra,0xe3a04601); ra+=4;
    PUT32(ra,0xe2544001); ra+=4;
    PUT32(ra,0x1afffffd); ra+=4;
    PUT32(ra,0xeafffff4); ra+=4;

    HOP(0x10000);
    return(0);
}
using the PUT32 above and this for HOP

Code: Select all

.globl HOP
HOP:
    bx r0
and it blinks quite nicely...

I just posted the code for this here

https://github.com/dwelch67/raspberrypi ... 01/pi2/asm

dwelch67
Posts: 955
Joined: Sat May 26, 2012 5:32 pm

Re: Initializing RAM on Raspberry Pi 2

Wed Dec 23, 2015 7:17 am

so adding this in

Code: Select all

void put32(unsigned long int addr, unsigned long int val) {
   volatile unsigned long int *a;
   a = addr;
   *a = val;
}

Code: Select all

    PUT32(ra,0xe3a02902); ra+=4;
    put32(ra,0xe3a03008); ra+=4;
    PUT32(ra,0xe5802020); ra+=4;
    PUT32(ra,0xe580302c); ra+=4;
you of course get a warning
blinker01.c:12:6: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
a = addr;
^
despite that it does what we really wanted, stores the 32 bit value to an address using the 32 bit store

Code: Select all

0000800c <PUT32>:
    800c:	e5801000 	str	r1, [r0]
    8010:	e12fff1e 	bx	lr

00008014 <GET32>:
    8014:	e5900000 	ldr	r0, [r0]
    8018:	e12fff1e 	bx	lr

0000801c <dummy>:
    801c:	e12fff1e 	bx	lr

00008020 <HOP>:
    8020:	e12fff10 	bx	r0

00008024 <put32>:
    8024:	e5801000 	str	r1, [r0]
    8028:	e12fff1e 	bx	lr
so byte swapping would mess up the data in this case. what is your compiler producing for your put32?

Even way back on old arm cores, but certainly today alignment fault checking is disabled by default so you can write to an unaligned address with a single store, and it should operate as desired (starting with maybe the armv6 I think, armv4 definitely rotated the bytes around within a word rather than spilling over to the next word which was cool if you wanted to some simple shifty things, dont remember what the armv5 does). This is NOT working for me on the raspberry pi 2 even though the fault checking is disabled. so more investigation is required, it could be the broadcom logic wont let it happen, I dont yet know. not relevant for writing instructions to ram then branching to them though those have to be aligned.

User avatar
Paeryn
Posts: 2708
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Initializing RAM on Raspberry Pi 2

Wed Dec 23, 2015 12:44 pm

dwelch67 wrote:There is no reason for you to need to byteswap those instructions, what is your put32 doing, seems broken if you need to byteswap.
The problem jFoster had was that he was trying to use the values in the order that he was seeing them in a hex dump (or some such). Looking at a 32bit word in memory byte by byte you see the least significant byte first, he was assuming that was the order to use so when he wrote out the 32 bit constant for each instruction he wrote it out in the reversed order, so when he saw the four individual bytes of the mov instruction as 3F 04 A0 E3 he assumed that as a 32 bit value he needed to write 3F04A0E3 rather than the correct E3A0043F.
She who travels light — forgot something.

jFoster
Posts: 57
Joined: Fri Jul 03, 2015 7:32 pm
Location: British Columbia, Canada
Contact: Website

Re: Initializing RAM on Raspberry Pi 2

Wed Dec 23, 2015 4:44 pm

Here are my put32() and get32() functions from the disassembler:

Code: Select all

0000800c <put32>:
    800c:	e52db004 	push	{fp}		; (str fp, [sp, #-4]!)
    8010:	e28db000 	add	fp, sp, #0
    8014:	e24dd014 	sub	sp, sp, #20
    8018:	e50b0010 	str	r0, [fp, #-16]
    801c:	e50b1014 	str	r1, [fp, #-20]
    8020:	e51b3010 	ldr	r3, [fp, #-16]
    8024:	e2033003 	and	r3, r3, #3
    8028:	e3530000 	cmp	r3, #0
    802c:	0a000000 	beq	8034 <put32+0x28>
    8030:	ea000004 	b	8048 <put32+0x3c>
    8034:	e51b3010 	ldr	r3, [fp, #-16]
    8038:	e50b3008 	str	r3, [fp, #-8]
    803c:	e51b3008 	ldr	r3, [fp, #-8]
    8040:	e51b2014 	ldr	r2, [fp, #-20]
    8044:	e5832000 	str	r2, [r3]
    8048:	e24bd000 	sub	sp, fp, #0
    804c:	e49db004 	pop	{fp}		; (ldr fp, [sp], #4)
    8050:	e12fff1e 	bx	lr

00008054 <get32>:
    8054:	e52db004 	push	{fp}		; (str fp, [sp, #-4]!)
    8058:	e28db000 	add	fp, sp, #0
    805c:	e24dd014 	sub	sp, sp, #20
    8060:	e50b0010 	str	r0, [fp, #-16]
    8064:	e51b3010 	ldr	r3, [fp, #-16]
    8068:	e2033003 	and	r3, r3, #3
    806c:	e3530000 	cmp	r3, #0
    8070:	0a000001 	beq	807c <get32+0x28>
    8074:	e1a00000 	nop			; (mov r0, r0)
    8078:	ea000003 	b	808c <get32+0x38>
    807c:	e51b3010 	ldr	r3, [fp, #-16]
    8080:	e50b3008 	str	r3, [fp, #-8]
    8084:	e51b3008 	ldr	r3, [fp, #-8]
    8088:	e5933000 	ldr	r3, [r3]
    808c:	e1a00003 	mov	r0, r3
    8090:	e24bd000 	sub	sp, fp, #0
    8094:	e49db004 	pop	{fp}		; (ldr fp, [sp], #4)
    8098:	e12fff1e 	bx	lr
I know even less in assembler than I know C, so I don't exactly know what's going on here!
Paeryn wrote:The problem jFoster had was that he was trying to use the values in the order that he was seeing them in a hex dump (or some such)
Yeah, I was just reading it in the wrong order and assuming the hex editor would load that file straight into RAM.

Just a question for dwelch67, I saw your put32() and get32() functions before I wrote my own. If I'm reading your assembly correctly, does it just store the value of r1 in the address that r0 is pointing to?

Code: Select all

00008024 <put32>:
    8024:   e5801000    str   r1, [r0]
    8028:   e12fff1e    bx   lr
How does it know that when this function is called that r0 and r1 will always hold the correct values from the C code, I couldn't find anywhere in your examples that you had explicitly initialized r0 and r1 before calling put32() and get32().

User avatar
Paeryn
Posts: 2708
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Initializing RAM on Raspberry Pi 2

Wed Dec 23, 2015 5:03 pm

By convention the first parameter passed to a function is in R0, the second in R1 etc. up until R3, after that subsequent parameters get pushed on to the stack. The code your function generates behaves the same except there's a ton of pushing values on/off of the stack too, gcc does this when there's no optimisation as it tries to keep all variables in memory at all times (and not just in registers).
She who travels light — forgot something.

dwelch67
Posts: 955
Joined: Sat May 26, 2012 5:32 pm

Re: Initializing RAM on Raspberry Pi 2

Wed Dec 23, 2015 8:03 pm

as just answered there is a calling convention that dictates rules as to which registers or stack locations are used to pass parameters and results.

most of the arm instructions are sane like the intel formatted x86 you can replace the first comma with an equals

add r0,r1,r2

r0 = r1 + r2

ldr and str though the data moves to registers or from keep the same ordering

Code: Select all

str r0,[r1]
ldr r2,[r3]
ldr makes sense r2 = contents of r3. but str does not r0 is not getting the contents of r1, one could argue it should have been formatted str [r1],r0 to be consistent but that is not what they did.

anyway asked and answered by a prior post I know that compilers have to operate on a rule/convention to even work both sides of a function call (caller and calee) have to agree on a way to pass data. you can either look for the spec, or you can just try it and disassemble

Code: Select all

unsigned int fun ( unsigned int a, unsigned int b )
{
return(a+b);
}

Code: Select all

extern unsigned int fun ( unsigned int a, unsigned int b );
unsigned int test ( void )
{
   return(fun(7,5));
}

compile and disassemble those separately from each other (in their own separate files not same file) and disassemble and you will see that 7 should go into r0, 5 into r1, and then on the fun() side it adds r0=r0+r1 and returns that or maybe if not optimized it adds r0 and r1 to some destination then copies that destination to r0 because r0 is the return.

if you are on a new to you platform or compiler that uses a different convention or whatever you can create simple test functions like this (so long as you can avoid compiler optimizing out what you needed to figure out separate files in this case) and figure out how the compiler generates the code, you might get it wrong but if your project doesnt work then you can look within your project how the compiler generated the call to your assembly and adjust accordingly. basically you can read the spec and hope the compiler conforms or do experiments and hope there is not a nuance to the compiler or spec that changes things. for arm for example generally they use r0,r1,r2,r3 in that order then use the stack, but there are certain exceptions to that where they might skip one of those registers.

jFoster
Posts: 57
Joined: Fri Jul 03, 2015 7:32 pm
Location: British Columbia, Canada
Contact: Website

Re: Initializing RAM on Raspberry Pi 2

Thu Dec 24, 2015 4:07 pm

Thanks guys, that's really helpful. Just so I'm clear though, r0 will always store the 1st parameter of the function when it is called, r1 = 2nd, r2 = 3rd, and r3 = 4th. After that, it goes on the stack right? Even if the compiler is optimized, will this always be the case at least at the start of the function before values start being modified? I just want to make sure that

Code: Select all

00008024 <put32>:
    8024:   e5801000    str   r1, [r0]
    8028:   e12fff1e    bx   lr
will always be a reliable function even when the compiler is optimized. Also, what happens if the stack pointer ever runs out of room? I have it placed at 0x8000 so it progresses downwards and so that it won't start eating into the code, but what happens if there are ever 0x8000 bytes put on the stack. If I try to write to it one more time, where will that byte be stored in memory?

User avatar
Paeryn
Posts: 2708
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Initializing RAM on Raspberry Pi 2

Thu Dec 24, 2015 5:50 pm

Yes, gcc will adhere to the standard regardless of the optimisation level used. There are times when a register has to be skipped if you use 64 bit types (e.g. long long) due to register pairs having to always be even/odd. So if you have a function

Code: Select all

void func(int intvar, long long longvar);
intvar will be assigned to R0 but longvar will be assigned to R2-R3, R1 will not be used as you can't use R1-R2 as a register pair. R1 won't get used for a subsequent parameter even though it's free - it will be permanently skipped.

The stack has to be 8 byte aligned at each function call and 8 byte parameter types have to be on 8 byte aligned addresses.

Also if you are using hard-float then float/double parameters will be passed in the fp registers, only first 16 single precision registers can be used (doubles takes an even/odd pair). Although if a float register is skipped due to a double, it will be used for a following float unlike the integer case, so

Code: Select all

void func(float p1, double p2, float p3);
will assign p1 to S0, p2 to D1 (S2-S3) and p3 to S1.

If you want, the full aapcs (Arm Architecture Procedure Call Standard) can be found here http://infocenter.arm.com/help/topic/co ... _aapcs.pdf

If the stack runs off the bottom of memory the stack pointer will wrap to a value that is likely to not be a valid address. And usually the bottom of memory contains the interrupt vector table so you'll have overwritten them. If you are worried about using too much stack space then check it at the start of each function to make sure there is space for the next call.
She who travels light — forgot something.

Return to “Bare metal, Assembly language”