Skip to content

Conversation

mikesmitty
Copy link
Contributor

@mikesmitty mikesmitty commented Sep 10, 2025

As it turns out, the RP2350 has hardware spinlocks that can be unlocked by writes to nearby addresses, the lower spinlocks currently in use in TinyGo happen to be unlocked by writes to the doorbell interrupt registers used to signal between cores, very possibly leading to some unexpected unlocks. This was not corrected in the A3 or A4 steppings and instead software spinlocks are used by default on RP2350 in pico-sdk:

RP2350 Warning. Due to erratum RP2350-E2, writes to new SIO registers above an offset of +0x180 alias the spinlocks, causing spurious lock releases. This SDK by default use atomic memory accesses to implement the hardware_sync_spin_lock API, as a workaround on RP2350 A2.

https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#group_hardware_sync

Another thing I noticed in that section, they also always disable interrupts when the spinlocks are being held, we may want to do the same:

[...] the default spinlock related methods here (e.g. spin_lock_blocking) always disable interrupts while the lock is held as use by IRQ handlers and user code is common/desirable, and spin locks are only expected to be held for brief periods.

These are the software spinlock macros ported over:
https://github.com/raspberrypi/pico-sdk/blob/2.2.0/src/rp2_common/hardware_sync_spin_lock/include/hardware/sync/spin_lock.h#L112
https://github.com/raspberrypi/pico-sdk/blob/2.2.0/src/rp2_common/hardware_sync_spin_lock/include/hardware/sync/spin_lock.h#L197

sago35 and others added 4 commits September 2, 2025 07:14
Writing to the UART takes time and that may not be a good idea inside an
interrupt, but it is essential for debugging sometimes (especially since
USB-CDC typically doesn't work inside an interrupt).

This fixes UART support in interrupts for the RP2040 at least. You can
test it with `-serial=uart` and connecting a USB-UART adapter to the
right pins.
…he first error is returned

In some cases, e.g nothing connected on the bus, repeated resume-stop sequences can lead to the bus never reaching the stop state, hanging Tx.
This change ensures the resume-stop sequence is submitted once on error. It also moves the error code read to before the sequence to ensure it's valid.

Fixes: tinygo-org#4998
@mikesmitty
Copy link
Contributor Author

Oh, whoops. My go fmt extension has been flaking out on me. Will have the missing rp2040 imports updated in a moment. Here's the lock/unlock disassembled output with inlining disabled:

   0x10001178 <(*runtime.spinLock).Lock+0>:     cbz     r0, 0x10001192 <(*runtime.spinLock).Lock+26>
   0x1000117a <(*runtime.spinLock).Lock+2>:     ldaexb  r2, [r0]
   0x1000117e <(*runtime.spinLock).Lock+6>:     movs    r1, #1
   0x10001180 <(*runtime.spinLock).Lock+8>:     cmp     r2, #0
   0x10001182 <(*runtime.spinLock).Lock+10>:    bne.n   0x1000117a <(*runtime.spinLock).Lock+2>
   0x10001184 <(*runtime.spinLock).Lock+12>:    strexb  r2, r1, [r0]
   0x10001188 <(*runtime.spinLock).Lock+16>:    cmp     r2, #0
   0x1000118a <(*runtime.spinLock).Lock+18>:    bne.n   0x1000117a <(*runtime.spinLock).Lock+2>
   0x1000118c <(*runtime.spinLock).Lock+20>:    dmb     sy
   0x10001190 <(*runtime.spinLock).Lock+24>:    bx      lr
   0x10001192 <(*runtime.spinLock).Lock+26>:    bl      0x100013f4 <runtime.nilPanic>
   0x100013e4 <(*runtime.spinLock).Unlock+0>:   cbz     r0, 0x100013ee <(*runtime.spinLock).Unlock+10>
   0x100013e6 <(*runtime.spinLock).Unlock+2>:   movs    r1, #0
   0x100013e8 <(*runtime.spinLock).Unlock+4>:   stlb    r1, [r0]
   0x100013ec <(*runtime.spinLock).Unlock+8>:   bx      lr
   0x100013ee <(*runtime.spinLock).Unlock+10>:  bl      0x100013f4 <runtime.nilPanic>

Copy link
Contributor

@eliasnaur eliasnaur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I support the switch to atomic instructions, but if you need something that works right away, RP2350-E2 mentions that some spinlocks are not affected:

The following SIO spinlocks can be used normally because they don’t alias with writable registers: 5, 6, 7,
10, 11, and 18 through 31. Some of the other lock addresses may be used safely depending on which of
the high-addressed SIO registers are in use.
Locks 18 through 24 alias with some read-only TMDS encoder registers, which is safe as only writes are
mis-decoded.

Copy link
Member

@aykevl aykevl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment below.

Also,

Another thing I noticed in that section, they also always disable interrupts when the spinlocks are being held, we may want to do the same: [...]

The runtime does this in various places if needed. The spinlock implementation doesn't need to disable interrupts too.

@mikesmitty
Copy link
Contributor Author

I support the switch to atomic instructions, but if you need something that works right away, RP2350-E2 mentions that some spinlocks are not affected:

The following SIO spinlocks can be used normally because they don’t alias with writable registers: 5, 6, 7,
10, 11, and 18 through 31. Some of the other lock addresses may be used safely depending on which of
the high-addressed SIO registers are in use.
Locks 18 through 24 alias with some read-only TMDS encoder registers, which is safe as only writes are
mis-decoded.

Thank you for tracking those down. I figured there would probably be some, but I couldn't find them

deadprogram and others added 11 commits September 11, 2025 08:34
…tests

that use Chromium headless browser to avoid use of older incompatible version.

Signed-off-by: deadprogram <ron@hybridgroup.com>
…wser used for running

the wasm tests.

Also add favicon link to avoid extra fetches during test runs.

Signed-off-by: deadprogram <ron@hybridgroup.com>
With these flags, the TinyGo binary gets 18.8MB (11.6%) smaller. That
seems like a quite useful win for such a small change!

This is only for Linux for now. MacOS and Windows can be tested later,
the flags for those probably need to be modified.

Originally inspired by:
https://discourse.llvm.org/t/state-of-the-art-for-reducing-executable-size-with-heavily-optimized-program/87952/18
There are some other flags like -Wl,--pack-dyn-relocs=relr that did not
shrink binary size in my testing, so I've left them out.

This also switches the linker to prefer mold or lld over the default
linker, since the system linker is usually ld.bfd which is very slow.
(Also, for some reason mold produces smaller binaries than lld).
The revive command seems to have had a syntax error in the file
input glob. It appears to have been broken in a way that did not
result in a return code being set. This change uses 'find' to
build the input to the linter.

Note that it is expected to fail the CI script, because it is
uncovering some existing lint issues that were not being caught.
Signed-off-by: deadprogram <ron@hybridgroup.com>
This switches the Espressif fork from LLVM 19 to LLVM 20, so we can use
the improvements made between those LLVM versions. It also better aligns
with the system-LLVM build method, which currently also defaults to LLVM
20.

Note that this disables the machine outliner for RISC-V. It appears
there's a bug in there somewhere, with the machine outliner enabled the
crypto/elliptic package tests fail with -target=riscv-qemu.
This should ideally be investigated and reported upstream.
Copy link
Contributor

@eliasnaur eliasnaur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than a nit, this looks good to me. @aykevl WDYT?

printLock = spinLock{id: 0}
schedulerLock = spinLock{id: 1}
atomicsLock = spinLock{id: 2}
futexLock = spinLock{id: 3}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id is an implementation detail of rp2040 spinlocks, whereas on rp2350 ids have some meaning but are unusued. I suggest moving the spinlock variables to the rpXXXX.go files and avoid the id field on rp2350.


type spinLock struct {
atomic.Uint32
id uint8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete field.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That one's still needed for compatibility with how the rp2040 hardware spinlocks are initialized here:

printLock = spinLock{id: 20}
schedulerLock = spinLock{id: 21}
atomicsLock = spinLock{id: 22}
futexLock = spinLock{id: 23}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. Above, I suggested moving that var () block to the runtime_rp2xxx.go files to get rid of that dependency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yep, I missed that

@mikesmitty
Copy link
Contributor Author

After doing some testing, I think this might not actually be locking properly. Going to do some more thorough testing

deadprogram and others added 5 commits September 18, 2025 08:24
- do not install cmake, instead use the version already installed
- add macOS 15 to the CI builds
- update a could of GH actions to latest release
- update Go version being use to Go 1.25.1

Signed-off-by: deadprogram <ron@hybridgroup.com>
GitHub org. The git server for git.musl-libc.org is having troubles,
and it also seems like a safer bet to have our own mirror just in case.

Signed-off-by: deadprogram <ron@hybridgroup.com>
…d by

the test-macos-homebrew job, and it conflicts with the actual build
that we want, which is macOS 14 for backwards-compatibility.

Signed-off-by: deadprogram <ron@hybridgroup.com>
@aykevl
Copy link
Member

aykevl commented Sep 23, 2025

Yes, this looks good. @mikesmitty can you apply the changes proposed by @eliasnaur?

Also, you may want to rebase on the dev branch to hopefully fix the assert-test-linux CI failure.

@mikesmitty
Copy link
Contributor Author

Sure, here's the rebase. I haven't had time to test or diagnose it yet, been stuck working on a project that's consumed all my free time, but I'm pretty sure it's not functioning properly as confirmed by that CI failure

@mikesmitty
Copy link
Contributor Author

Hmm, no I guess it is actually locking. I'm not sure what's happening with that CI test though

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.