Modernize rpi-5 build
Updates the rpi-5-blink example to link using SwiftPM and use a toolset.json.
@iCMDdev can you verify that this example still works with the build changes?
Hi, sure! I'll test later today, or in the worst case, tomorrow, and let you know.
Sorry for the delay. I tried to test it since yesterday but something didn't work out (likely on my end). I'll test again tomorrow and let you know how it goes.
Unfortunately I think something isn't right on the Pi 5 (LED doesn't blink); I'll try to compare the resulted binaries. Currently the old build fails for me, so I'll have to retry with an older Swift dev build.
Looking at the output of objdump -D Application it seems that the entrypoint / address of main is 00000000000800a0 and I'm not really sure if that's all right or not (looking at the linker script, entrypoint should be 0x80000). As I said, I wanted to compare with the previous working build and I currently don't have it, but I'll try do this asap.
Unfortunately I think something isn't right on the Pi 5 (LED doesn't blink); I'll try to compare the resulted binaries. Currently the old build fails for me, so I'll have to retry with an older Swift dev build.
Looking at the output of
objdump -D Applicationit seems that the entrypoint / address ofmainis00000000000800a0and I'm not really sure if that's all right or not (looking at the linker script, entrypoint should be0x80000). As I said, I wanted to compare with the previous working build and I currently don't have it, but I'll try do this asap.
Thanks so much for taking a look; would you be interesting in trying to fix/land this change? we're trying to move all the SwiftPM sample to avoid manually linking
Unfortunately I think something isn't right on the Pi 5 (LED doesn't blink); I'll try to compare the resulted binaries. Currently the old build fails for me, so I'll have to retry with an older Swift dev build. Looking at the output of
objdump -D Applicationit seems that the entrypoint / address ofmainis00000000000800a0and I'm not really sure if that's all right or not (looking at the linker script, entrypoint should be0x80000). As I said, I wanted to compare with the previous working build and I currently don't have it, but I'll try do this asap.Thanks so much for taking a look; would you be interesting in trying to fix/land this change? we're trying to move all the SwiftPM sample to avoid manually linking
Yes, surely! I was actually looking into it right now.
Looking at the old (main branch) resulted build files, I'm pretty sure that indeed, the problem is related to the entrypoint. Here's the old elf file, with entrypoint at 0x80000:
cmd@ ~/Downloads/rpi-5-blink-binary % objdump -D kernel8.elf
kernel8.elf: file format elf64-littleaarch64
Disassembly of section .text:
0000000000080000 <_start>:
80000: d53800a1 mrs x1, MPIDR_EL1
80004: 92400421 and x1, x1, #0x3
80008: b4000061 cbz x1, 0x80014 <_start+0x14>
8000c: d503205f wfe
80010: 17ffffff b 0x8000c <_start+0xc>
80014: 58000161 ldr x1, 0x80040 <_start+0x40>
80018: 9100003f mov sp, x1
8001c: 58000161 ldr x1, 0x80048 <_start+0x48>
80020: 18000182 ldr w2, 0x80050 <_start+0x50>
80024: 34000082 cbz w2, 0x80034 <_start+0x34>
80028: f800843f str xzr, [x1], #0x8
8002c: 51000442 sub w2, w2, #0x1
80030: 35ffffa2 cbnz w2, 0x80024 <_start+0x24>
80034: 940003ee bl 0x80fec <main>
80038: 17fffff5 b 0x8000c <_start+0xc>
8003c: 00000000 udf #0x0
... (there's more)
And here is the current, new build result:
cmd@ ~/Downloads/rpi-5-blink % objdump -D Application
Application: file format elf64-littleaarch64
Disassembly of section .swift_modhash:
0000000000080000 <$d>:
80000: b8 37 7d b5 .word 0xb57d37b8
80004: 35 04 4e b2 .word 0xb24e0435
80008: 8d 6c 19 ca .word 0xca196c8d
8000c: cf a6 20 4d .word 0x4d20a6cf
... (more .words)
Disassembly of section .text:
00000000000800a0 <main>:
800a0: d28f8088 mov x8, #0x7c04 // =31748
800a4: f2afaa28 movk x8, #0x7d51, lsl #16
800a8: f2c00208 movk x8, #0x10, lsl #32
800ac: b9400509 ldr w9, [x8, #0x4]
800b0: 12167929 and w9, w9, #0xfffffdff
800b4: b9000509 str w9, [x8, #0x4]
800b8: b9400109 ldr w9, [x8]
800bc: 32170129 orr w9, w9, #0x200
800c0: b9000109 str w9, [x8]
800c4: b9400109 ldr w9, [x8]
800c8: 12167929 and w9, w9, #0xfffffdff
800cc: b9000109 str w9, [x8]
800d0: 17fffffa b 0x800b8 <main+0x18>
So yes, as I suspected, the entrypoint is likely the issue. This is likely caused by the linking step, and I'll look into it. It's a bit strange, since the same linker script is used and this shouldn't have happened.
Also: In the new build, there's no _start symbol in the resulted ELF file, so that might also be part of the culprit.
If you have any ideas why this could happen, I'm open to hearing them!
@iCMDdev I think I fixed those issues!
- I forgot to link
SupportintoApplication🤦 - I updated the linker map to discard
.swift_modhash
Great! I'll test again shortly.
I think we're definitely on the right track and these changes are good, but from my testing it stil doesn't blink. Here's what I've noticed:
- Both builds (old and new) now have the
_startfunction. And it is identical. This is great, the previous updated version did not have it. - I noticed the new
.textsection is significantly shoter, containing the_startandmainsymbols, while the original build included many other symbols such asMMIO-related ones, or embedded-specific symbols such as_swift_embedded_set_heap_object_metadata_pointer(random example that I picked from the original build). This leads me to believe that a module or dependency issue might be the root cause of the problem. Here's the current.textin the new build:
% objdump -D Application
Disassembly of section .text:
0000000000080000 <_start>:
80000: d53800a1 mrs x1, MPIDR_EL1
80004: 92400421 and x1, x1, #0x3
80008: b4000061 cbz x1, 0x80014 <_start+0x14>
8000c: d503205f wfe
80010: 17ffffff b 0x8000c <_start+0xc>
80014: 58000161 ldr x1, 0x80040 <_start+0x40>
80018: 9100003f mov sp, x1
8001c: 58000161 ldr x1, 0x80048 <_start+0x48>
80020: 18000182 ldr w2, 0x80050 <_start+0x50>
80024: 34000082 cbz w2, 0x80034 <_start+0x34>
80028: f800843f str xzr, [x1], #0x8
8002c: 51000442 sub w2, w2, #0x1
80030: 35ffffa2 cbnz w2, 0x80024 <_start+0x24>
80034: 94000008 bl 0x80054 <main>
80038: 17fffff5 b 0x8000c <_start+0xc>
8003c: 00000000 udf #0x0
0000000000080040 <$d>:
80040: 00 00 08 00 .word 0x00080000
80044: 00 00 00 00 .word 0x00000000
80048: 90 00 08 00 .word 0x00080090
8004c: 00 00 00 00 .word 0x00000000
80050: 00 00 00 00 .word 0x00000000
0000000000080054 <main>:
80054: d28f8088 mov x8, #0x7c04 // =31748
80058: f2afaa28 movk x8, #0x7d51, lsl #16
8005c: f2c00208 movk x8, #0x10, lsl #32
80060: b9400509 ldr w9, [x8, #0x4]
80064: 12167929 and w9, w9, #0xfffffdff
80068: b9000509 str w9, [x8, #0x4]
8006c: b9400109 ldr w9, [x8]
80070: 32170129 orr w9, w9, #0x200
80074: b9000109 str w9, [x8]
80078: b9400109 ldr w9, [x8]
8007c: 12167929 and w9, w9, #0xfffffdff
80080: b9000109 str w9, [x8]
80084: 17fffffa b 0x8006c <main+0x18>
(That's all of it. No mentions of MMIO, compared to the original build, which had symbols such as <$e4MMIO18ContiguousBitFieldPAAE8bitWidthSivgZ7MainApp7GIODATAV5VALUEO_Ttgq5+0x40>).
- I assume
.swift_modhashare related to module hashes. I'm not sure if they are necessary at runtime; if they are, I think we could relocate this section after.text.
@iCMDdev can you try the old version but in a release build? It would be good to know if that version still blinks. I'd expect the .text section of the old version in release to be similar to the new version.
I do really appreciate the time and help here!
@iCMDdev can you try the old version but in a
releasebuild? It would be good to know if that version still blinks. I'd expect the.textsection of the old version inreleaseto be similar to the new version.I do really appreciate the time and help here!
Sure!
It looks like indeed, the old build doesn't work either when compiled in release mode (with .swift_modhash removed as well; since building in release also made that section appear at 0x80000 before .text).
While the old build .elf file, compiled in release mode, still includes some extra symbols such as swift_initStackObject, the assembly instrctions within _start and main are nearly, if not identical, so I suppose the old build didn't strip out the unnecessary symbols generated by the standard libraries. Which is fine for the new build to do.
I think I figured out something by looking at the assembly. I was previously using the following as a simple workaround for a sleep() function:
for _ in 1..<100000 {}
But that gets optimized in the production assembly:
main:
// set x8 to the MMIO address = 0x00000010_7d51_7c04
mov x8, #0x7c04
movk x8, #0x7d51, lsl #16
movk x8, #0x10, lsl #32
// x8 is now set to GPIO base address + 4 = GIODATA
// x8 + 4 = GPIOIODIR (GPIO direction register)
ldr w9, [x8, #0x4] // load current value of GIOIODIR
and w9, w9, #0xfffffdff // clear bit 9, which is for GPIO9 (value 0 = output)
str w9, [x8, #0x4] // store result back to GIOIODIR
// x8 + 0 = GPIOIODATA (GPIO data register)
// set GPIO9 (onboard green ACT LED) high
ldr w9, [x8] // load current value of GIODATA
<main+0x18>:
orr w9, w9, #0x200 // set bit 9, which is for GPIO9 (value 1 = high)
str w9, [x8] // store result back to GIODATA
// NOTICE: There's no delay here!!
// x8 + 0 = GPIOIODATA (GPIO data register)
// set GPIO9 (onboard green ACT LED) low
ldr w9, [x8] // load current value of GIODATA
and w9, w9, #0xfffffdff // clear bit 9, which is for GPIO9 (value 0 = low)
str w9, [x8] // store result back to GIODATA
b 0x8006c <main+0x18> // loop back; set GPIO9 high again
And, to be honest, I forgot to rewrite it in a more graceful manner. So, @rauhul, do you have any tips on how to write such a sleep() function in a better way (and, perhaps, keep it slightly simple - I guess we shouldn't use timers, etc.)?
TL;DR I suppose the code is actually running but it blinks really fast, without a delay; unfortunately I can't find my SWD debugger to attach to my Pi and confirm it that way (edit: reading this, I guess I could just turn the LED off without turning it back on and confirm it like that, but I'll try the hack mentioned below).
I've seen that the STM32 example defines a nop() function in the Bridging Header, which is then used in Swift like this:
for _ in 0..<10_000 * milliseconds {
nop()
}
So I assume that would work.
@iCMDdev our hack solution in the other examples is to use inline asm from a c header to expose nop as a function you can call in loop.
@iCMDdev our hack solution in the other examples is to use inline asm from a c header to expose
nopas a function you can call in loop.
Yeah, just noticed that (also sent my comment above right before seeing this). I'll try to test that.
@rauhul Great news: I confirmed it works!!! I made the follwing changes (for the Pi 5 example, Pi 4 should be similar):
// Support.h
#pragma once
static inline __attribute((always_inline)) void nop() {
asm volatile("nop");
}
// Application.swift
import MMIO
import Support // for nop()
@Register(bitWidth: 32)
struct GIOIODIR {
@ReadWrite(bits: 9..<10, as: Bool.self)
var direction: DIRECTION
}
@Register(bitWidth: 32)
struct GIODATA {
@ReadWrite(bits: 9..<10, as: Bool.self)
var value: VALUE
}
@RegisterBlock
struct GPIO {
@RegisterBlock(offset: 0x00008)
var gioiodir: Register<GIOIODIR>
@RegisterBlock(offset: 0x00004)
var giodata: Register<GIODATA>
}
let gpio = GPIO(unsafeAddress: 0x10_7d51_7c00)
func setLedOutput() {
gpio.gioiodir.modify {
$0.direction = false // 0 is output, 1 is input
}
}
func ledOn() {
gpio.giodata.modify {
$0.value = true // pin on
}
}
func ledOff() {
gpio.giodata.modify {
$0.value = false // pin off
}
}
@main
struct Application {
static func main() {
setLedOutput()
while true {
ledOn()
for _ in 1..<100000 { nop() } // added nop and increased delay
ledOff()
for _ in 1..<100000 { nop() } // added nop and increased delay
}
}
}