Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support per-core state using #[thread_local] #794

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions memory.x
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,37 @@ SECTIONS {
KEEP(*(.boot2));
} > BOOT2
} INSERT BEFORE .text;

/* Per-core (thread) data into flash */
SECTIONS {
.tdata : ALIGN(4)
{
. = ALIGN(4);
PROVIDE(__tdata_start = .);
*(.tdata .tdata.*);
. = ALIGN(4);
PROVIDE(__tdata_end = .);
} > FLASH
PROVIDE(__tdata_len = __tdata_end - __tdata_start);
} INSERT AFTER .data;

/* Size per-core state and allocate bss space for each core */
SECTIONS {
.tbss (NOLOAD) : ALIGN(4)
{
. = ALIGN(4);
PROVIDE(__tbss_start = .);
*(.tbss .tbss.*);
*(.tcommon);
. = ALIGN(4);
PROVIDE(__tbss_end = .);
} > RAM
PROVIDE(__tbss_len = __tbss_end - __tbss_start);

.tls_state (NOLOAD) : ALIGN(4) {
PROVIDE(TLS_CORE_0 = ALIGN(4));
. += __tdata_len + __tbss_len;
PROVIDE(TLS_CORE_1 = ALIGN(4));
. += __tdata_len + __tbss_len;
} > RAM
} INSERT AFTER .bss;
3 changes: 3 additions & 0 deletions rp2040-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ rtic-monotonic = ["dep:rtic-monotonic"]
# Implement `i2c-write-iter` traits
i2c-write-iter = ["dep:i2c-write-iter"]

# Enable use of thread-local variables for multicore state
thread_local = []

[[example]]
# irq example uses cortex-m-rt::interrupt, need rt feature for that
name = "gpio_irq_example"
Expand Down
106 changes: 106 additions & 0 deletions rp2040-hal/src/multicore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,47 @@
//! For inter-processor communications, see [`crate::sio::SioFifo`] and [`crate::sio::Spinlock0`]
//!
//! For a detailed example, see [examples/multicore_fifo_blink.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/multicore_fifo_blink.rs)
//!
//! ## Per-core static data
//!
//! Both cores share the same memory, so a `static` variable will be accessible
//! and shared by both, requiring the same care as it would in a multi-threaded
//! program.
//!
//! With the `thread_local` feature enabled, this module supports the use of the
//! ([unstable](https://github.com/rust-lang/rust/issues/29594))
//! `#[thread_local]` attribute to make these per-core variables. This allows
//! the same code to run on both cores but with its own core-specific static
//! state, such maintaining program state, or for things like DMA buffers.
//!
//! For example:
//! ```rust,ignore
//! #![feature(thread_local)]
//! # use core::cell::RefCell;
//!
//! #[thread_local]
//! static MY_COUNTER: RefCell<usize> = RefCell::new(0);
//!
//! fn next_id() -> usize {
//! MY_COUNTER.replace_with(|c| *c + 1)
//! }
//! ```
//!
//! Each core will get its own instance of the `MY_COUNTER` variable. Since
//! these are not shared, they do not need atomic operations to update.
//!
//! These core-local variables are initialized on program startup and retain
//! their value from there on, even between invocations of [`Core::spawn`].
//!
//! Note that this requires some setup in the linker script to allocate space
//! for the static data. See memory.x for details.
//!
//! If the variables are zero-initialized then they will be reserved space in
//! the `.tbss` section in the executable, and then space in `.bss` for each
//! core. Similarly, variables initialized with non-zero constants will be in
//! the executable's `.tdata` section, and have space reserved in `.bss`; the
//! initial values are copied at program startup. Note that this uses the
//! `__pre_init` hook to do this, so it won't be available for other uses.

use core::mem::ManuallyDrop;
use core::sync::atomic::compiler_fence;
Expand Down Expand Up @@ -290,3 +331,68 @@ impl<'p> Core<'p> {
}
}
}

#[cfg(all(target_arch = "arm", feature = "thread_local"))]
mod thread_local {
use core::arch::global_asm;
use core::ptr::addr_of;

extern "C" {
static TLS_CORE_0: u8;
static TLS_CORE_1: u8;
}
// Not really a const pointer, but we reform it into mut in the asm
static mut TLS_STATE: [*const u8; 2] = [
// Point to linker-allocated space in .bss
unsafe { addr_of!(TLS_CORE_0) },
unsafe { addr_of!(TLS_CORE_1) },
];
ithinuel marked this conversation as resolved.
Show resolved Hide resolved

// Define `__aeabi_read_tp` called by the compiler to get access to
// thread-local storage.
global_asm! {
".pushsection .text.__aeabi_read_tp",
".align 4",
".p2align 4,,15",
".global __aeabi_read_tp",
".type __aeabi_read_tp,%function",

"__aeabi_read_tp:",
" push {{r1, lr}}",
" ldr r1, =0xd0000000", // Load SIO CPUID addr
" ldr r1, [r1]", // Get current CPUID
" lsls r1, r1, #2", // Scale by 4
" ldr r0, ={tls_state}", // Load TLS_STATE base addr
" ldr r0, [r0, r1]", // Load CPU per-thread
" pop {{r1, pc}}",
ithinuel marked this conversation as resolved.
Show resolved Hide resolved

".popsection",
tls_state = sym TLS_STATE,
}

// Intercept __pre_init to hook into the startup code to copy the tdata into
// TLS_CORE_[01].
global_asm! {
".pushsection .text.__pre_init",
".align 4",
".p2align 4,,15",
".global __pre_init",
".type __pre_init,%function",

"__pre_init:",
" push {{lr}}",
" ldr r0, ={tls_core_0}",
" ldr r1, =__tdata_start",
" ldr r2, =__tdata_len",
" bl __aeabi_memcpy",
" ldr r0, ={tls_core_1}",
" ldr r1, =__tdata_start",
" ldr r2, =__tdata_len",
" bl __aeabi_memcpy",
" pop {{pc}}",

".popsection",
tls_core_0 = sym TLS_CORE_0,
tls_core_1 = sym TLS_CORE_1,
}
ithinuel marked this conversation as resolved.
Show resolved Hide resolved
}
Loading