LFF
Posts: 6
Joined: Sun Jun 23, 2019 12:33 pm

Turning on ACT led on RPi 3 B+

Sun Jun 23, 2019 12:54 pm

Hi,

I am playing around with bare metal programming on a RPi 3 B+ and I am trying to turn on the ACT led right after booting to my kernel8.img.
This is the disassembly of my kernel8.img:

Code: Select all

.data:00000000	d2a7e400	mov x0, #0x3f200000
.data:00000004	d2800021	mov x1, #0x1
.data:00000008	d3659021	lsl x1, x1, #27
.data:0000000c	f9000401	str x1, [x0, #8]
.data:00000010	d2800021	mov x1, #0x1
.data:00000014	d3638821	lsl x1, x1, #29
.data:00000018	f801c001	str x1, [x0, #28]
.data:0000001c	14000000	b 0x0000001c

What I am doing:
- ACT led is supposed to be on GPIO 29, so I am setting the 27th bit of ARM physical address 0x3F200008 (to set GPIO 29 as an output);
- After that I am setting the 29th bit of ARM physical address 0x3F20001C (to actually set the GPIO 29);

This code is not working and I can't figure out why. Is it some index offset mistake?

LdB
Posts: 1207
Joined: Wed Dec 07, 2016 2:29 pm

Re: Turning on ACT led on RPi 3 B+

Sun Jun 23, 2019 4:06 pm

From memory the LED is sinking from the +ve rail so port high is off and port low is on .. hit the clear instead of the set and it will probably light.

LFF
Posts: 6
Joined: Sun Jun 23, 2019 12:33 pm

Re: Turning on ACT led on RPi 3 B+

Sun Jun 23, 2019 4:31 pm

I had tried clearing the pin with the following kernel8.img

Code: Select all

.data:00000000	d2a7e400	mov x0, #0x3f200000
.data:00000004	d2800021	mov x1, #0x1
.data:00000008	d3659021	lsl x1, x1, #27
.data:0000000c	f9000401	str x1, [x0, #8]
.data:00000010	d2800021	mov x1, #0x1
.data:00000014	d3638821	lsl x1, x1, #29
.data:00000018	f9001401	str x1, [x0, #40]
.data:0000001c	14000000	b 0x0000001c

What I am doing this time:
- Set GPIO 29 as an output;
- After that I am setting the 29th bit of ARM physical address 0x3F200028 (to clear the GPIO 29);

This code is still not working though.
I don't know if it matters, but my kernel.img is 32 bytes. Does it need to have a minimum size?

Also, this is my config.txt:

Code: Select all

kernel_old=1
disable_commandline_tags=1
arm_64bit=1
#disable_overscan=1
#framebuffer_swap=0

LdB
Posts: 1207
Joined: Wed Dec 07, 2016 2:29 pm

Re: Turning on ACT led on RPi 3 B+

Mon Jun 24, 2019 1:06 am

Kernel size is correct for your code last address is 0x20 which is 32 bytes
You will have all 4 cores coming in because of the kernel=old you will need to deadloop 3 they will all be clashing.
You also need to be aware you will have the core in EL3 mode and nothing initialized.
My assembler is rusty so take this with a grain of salt

Parking the cores would be something like

Code: Select all

mrs x0, mpidr_el1	
and x0, x0, #0x3	
cbz x0, 1f	 // Core0 continues on
wfi
b .
1:
Second problem I didn't even think about it before you are killing a pile ports because you are writing just the 1 rolled
value to the spot you need to read the port, clear only those bit and or the value in and then write it back

Code: Select all

mov x1, #0x1
mov x2, #0x07
lsl x1, x1, #27
lsl x2, x2, #27
ldr x3, [x0, #8]
bic x3, x3, x2
orr x3, x3, x1
str x3, [x0, #8]

LFF
Posts: 6
Joined: Sun Jun 23, 2019 12:33 pm

Re: Turning on ACT led on RPi 3 B+

Mon Jun 24, 2019 5:26 pm

Thank you for the feedback.

I didn't understand the second recommendation:
"Second problem I didn't even think about it before you are killing a pile ports because you are writing just the 1 rolled value to the spot you need to read the port, clear only those bit and or the value in and then write it back".

I've tried the following code, still with no success:

Code: Select all

.data:00000000	d53800a0	mrs x0, mpidr_el1
.data:00000004	f2400400	ands x0, x0, #0x3
.data:00000008	54000201	b.ne 0x00000048
.data:0000000c	d2a7e400	mov x0, #0x3f200000
.data:00000010	d2800021	mov x1, #0x1
.data:00000014	d28000e2	mov x2, #0x7
.data:00000018	d3659021	lsl x1, x1, #27
.data:0000001c	d3659042	lsl x2, x2, #27
.data:00000020	f9400403	ldr x3, [x0, #8]
.data:00000024	8a220063	bic x3, x3, x2
.data:00000028	aa010063	orr x3, x3, x1
.data:0000002c	f9000403	str x3, [x0, #8]
.data:00000030	d2800021	mov x1, #0x1
.data:00000034	d3659021	lsl x1, x1, #27
.data:00000038	f9000401	str x1, [x0, #8]
.data:0000003c	d2800021	mov x1, #0x1
.data:00000040	d3638821	lsl x1, x1, #29
.data:00000044	f9001401	str x1, [x0, #40]
.data:00000048	14000000	b 0x00000048

I've also tried setting the 29th bit instead of clearing it, but still no luck.

LdB
Posts: 1207
Joined: Wed Dec 07, 2016 2:29 pm

Re: Turning on ACT led on RPi 3 B+

Tue Jun 25, 2019 2:45 pm

You still have problems you are doing 64bit read/writes to 32 bit ports which I didn't pick up
This is a no no

Code: Select all

ldr x3, [x0, #8]
str x3, [x0, #8]
Needs to be a 32bit read/write

Code: Select all

ldr w3, [x0, #8]
str w3, [x0, #8]
Anyhow I did a simple roughly 1 sec flash code for you
https://github.com/LdB-ECM/Assembler/bl ... sm/start.S
The compiled version is here if you want to just test ... 108 bytes as little more complex than yours
https://github.com/LdB-ECM/Assembler/bl ... ernel8.img

Anyhow parks 3 cores down in hang, and core 0 flashes the led.

LFF
Posts: 6
Joined: Sun Jun 23, 2019 12:33 pm

Re: Turning on ACT led on RPi 3 B+

Tue Jun 25, 2019 6:06 pm

Thank you for your amazing help. With your guidance I was able to light the ACT led.

I've been playing with this and I was able to successfully light the ACT led with the following disassembled code:

Code: Select all

.data:00000000	d2a7e400	mov x0, #0x3f200000
.data:00000004	52800021	mov w1, #0x1
.data:00000008	53051021	lsl w1, w1, #27
.data:0000000c	b9000801	str w1, [x0, #8]
.data:00000010	52800021	mov w1, #0x1
.data:00000014	53030821	lsl w1, w1, #29
.data:00000018	b9001c01	str w1, [x0, #28]
.data:0000001c	14000000	b 0x0000001c

I also tested it and was able to light the led even if I set the GPIO first and then configured it as an output. This is very similar to my first attempt, it was just that I was using the X registers and I should be using the W registers. Now it's time to understand why it is like so.

Lessons learned:
- The ACT led is GPIO 29;
- To light the ACT led we should SET it;
- We need to transfer to the right physical addresses (addressed by X registers) the values in W registers.


Once again, thank you so much for your time!

LFF
Posts: 6
Joined: Sun Jun 23, 2019 12:33 pm

Re: Turning on ACT led on RPi 3 B+

Tue Jul 02, 2019 8:01 pm

I have a few questions that I don't think deserve a new thread on the forum. Particularly since they are related to the problems I've faced (and overcome!) during this thread. Nevertheless, if the community thinks otherwise I will happily create a new thread.

I was trying to store the value from a 64bit register in a physical address. It is not possible. I can only store in a physical address the value from 32bit registers. So my questions are:

- Are physical addresses 32bit wide?
- If so, is it all of them? Or are there some that are 64bit wide?
- From which datasheet can I get the width of the physical address?
- Assuming that all physical addresses are 32bit wide and the CPU in my RPi 3 B+ has a 64bit architecture, can I say that theoretically I could access up to 2^64 physical addresses? And since each one is 32bit wide, theoretically I could access up to (2^64)*32bits = 64 exabytes?

Did I get all of this right?

LdB
Posts: 1207
Joined: Wed Dec 07, 2016 2:29 pm

Re: Turning on ACT led on RPi 3 B+

Wed Jul 03, 2019 6:57 am

No you can store a 64 bit register directly in memory so anywhere outside the peripheral bus is fine.

The only things hard set 32bit is the peripheral bus specifically on the Pi3 anything between 0x3f000000-0x40010000
That is all the peripherals on this datasheet
https://www.raspberrypi.org/app/uploads ... herals.pdf
And all the QA7 multicore block
https://www.raspberrypi.org/documentati ... rev3.4.pdf

LFF
Posts: 6
Joined: Sun Jun 23, 2019 12:33 pm

Re: Turning on ACT led on RPi 3 B+

Thu Jul 04, 2019 9:24 pm

LdB you have been really helpful. However I believe my questions are far more basic/noob.
Nonetheless I also think that you have helped me answer them.

So let me present you some findings of my own, which, while obvious for most of you, may just be the required guidance for a few fellow newcomers to bare metal programming in the future.

What I found is that physical addresses are represented by 64 bit numbers. Inside each physical address, the hardware itself can only store 8 bits = 1 byte. But what if I want to store a 32bit number at some physical address X? Then the hardware "knows" it should store that number in the (32 bits / 8 bits per physical address =) 4 consecutive physical addresses starting from X.

A practical example: suppose I want to save 0x11223344 at address 0x0. Since, by default, the processor runs in little endian, then:
  • At address 0x0 the single byte value 0x44 will be stored;
  • At address 0x1 the single byte value 0x33 will be stored;
  • At address 0x2 the single byte value 0x22 will be stored;
  • (Yeah I guess you got the point) At address 0x3 the single byte value 0x11 will be stored;
One question that arouse was: what if I now load that value from memory to some 32bit register, will it be loaded as 0x44332211? Well, the processor is THAT smart and will load the value as expected: 0x11223344 to your register.

But if memory is just a collection of bytes, I should be able to access each one individually and be able to, for instance, load the value in memory location 0x2 to an 8bit register. That register should then be loaded with value 0x22. Well, if I am not wrong, and ignoring SIMD and floating point stuff, ARMv8 only has 32bit and 64bit general purpose registers. But that's okay. The engineers have designed the processor so that even if you load a single byte to a 32bit register, it will load the lsb (least significant byte) of said register and zero out all other upper bits. Brilliant!

As a proof of concept, and using what I've learned before, I've created a simple program:

Code: Select all

.data:00000000	d53800a0	mrs x0, mpidr_el1
.data:00000004	f2400400	ands x0, x0, #0x3
.data:00000008	54000141	b.ne 0x00000030
.data:0000000c	d2820000	mov x0, #0x1000
.data:00000010	d2820022	mov x2, #0x1001
.data:00000014	d2866881	mov x1, #0x3344
.data:00000018	f2a22441	movk x1, #0x1122, lsl #16
.data:0000001c	f9000001	str x1, [x0]
.data:00000020	39400041	ldrb w1, [x2]
.data:00000024	52800662	mov w2, #0x33
.data:00000028	6b02003f	cmp w1, w2
.data:0000002c	54000040	b.eq 0x00000034
.data:00000030	14000000	b 0x00000030
.data:00000034	d2a7e400	mov x0, #0x3f200000
.data:00000038	52800021	mov w1, #0x1
.data:0000003c	53051021	lsl w1, w1, #27
.data:00000040	b9000801	str w1, [x0, #8]
.data:00000044	52800021	mov w1, #0x1
.data:00000048	53030821	lsl w1, w1, #29
.data:0000004c	b9001c01	str w1, [x0, #28]
.data:00000050	17fffff8	b 0x00000030

You should recognize all but the section:

Code: Select all

.data:0000000c	d2820000	mov x0, #0x1000
.data:00000010	d2820022	mov x2, #0x1001
.data:00000014	d2866881	mov x1, #0x3344
.data:00000018	f2a22441	movk x1, #0x1122, lsl #16
.data:0000001c	f9000001	str x1, [x0]
.data:00000020	39400041	ldrb w1, [x2]
.data:00000024	52800662	mov w2, #0x33
.data:00000028	6b02003f	cmp w1, w2
.data:0000002c	54000040	b.eq 0x00000034

What I am doing is really simple:
  • I am storing the 32bit value 0x11223344 in memory location 0x1000;
  • Then I load a single byte starting at memory location 0x1001 (which should hold the single byte value 0x33) to the w1 register;
  • After all of this I simply assert that register w1 actually holds the single byte value 0x33 and if that's the case I jump to the start of the code section that turns on the ACT led.
But, what if I have loaded a halfword (2 bytes) starting at memory location 0x1001 to the w1 register? What would be the contents of that register? Well, since memory location 0x1001 holds the value 0x33 and memory location 0x1002 holds the value 0x22, w1 would hold the 2byte value 0x2233.

And this is it. It answers all of the questions I had before.
It created new ones though. It created so MANY new questions... Memory alignment, data structures...

LdB
Posts: 1207
Joined: Wed Dec 07, 2016 2:29 pm

Re: Turning on ACT led on RPi 3 B+

Fri Jul 05, 2019 1:26 am

The ARM8 prefers natural alignment

8 bits can be any byte aligned
16bits it wants 16 bit alignment ( 2 byte) ... half word opcodes
32 bits it wants 32 bit alignment
64 bit can be 32 but it prefers 64 for speed
Function arguments that are structs are padded to multiples of 8 bytes
It can store and load data in big or little endian but default startup is little endian
opcode execution is always little endian

By default it will throw an exception if you do unaligned access, but you can turn off the exception although half word opcodes on a byte alignment are unpredictable in result.

Stack Pointer (XSP AKA X31) must be aligned to 16-bytes (quad-word). This implies always push two registers to keep the alignment ..never push single registers.
All opcodes in AARCH64 are 32bit, hence why in 64bit it doesn't mind 32bit alignment.
The FPU is on as it is mandatory in AARCH64

Reference:
http://infocenter.arm.com/help/topic/co ... apcs64.pdf

Return to “Bare metal, Assembly language”