Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions library/std/src/sys/sync/condvar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cfg_select! {
all(target_family = "wasm", target_feature = "atomics"),
target_os = "hermit",
all(target_os = "wasi", target_env = "p3"),
target_os = "vexos",
) => {
mod futex;
pub use futex::Condvar;
Expand Down
4 changes: 4 additions & 0 deletions library/std/src/sys/sync/futex/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,9 @@ cfg_select! {
target_os = "motor" => {
pub use moto_rt::futex::*;
}
target_os = "vexos" => {
mod vexos;
pub use vexos::*;
}
_ => {}
}
55 changes: 55 additions & 0 deletions library/std/src/sys/sync/futex/vexos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use core::sync::atomic::Ordering;

use crate::sync::atomic::Atomic;
use crate::time::{Duration, Instant};

/// An atomic for use as a futex that is at least 32-bits but may be larger
pub type Futex = Atomic<Primitive>;
/// Must be the underlying type of Futex
pub type Primitive = u32;

/// An atomic for use as a futex that is at least 8-bits but may be larger.
pub type SmallFutex = Atomic<SmallPrimitive>;
/// Must be the underlying type of SmallFutex
pub type SmallPrimitive = u32;

/// Wait for a futex_wake operation to wake us.
///
/// Returns directly if the futex doesn't hold the expected value.
///
/// Returns false on timeout, and true in all other cases.
pub fn futex_wait(futex: &Atomic<u32>, expected: u32, timeout: Option<Duration>) -> bool {
if let Some(timeout) = timeout {
let begin = Instant::now();

while futex.load(Ordering::Acquire) == expected {
if begin.elapsed() >= timeout {
return false;
}

// Wait for an ISR or Simple Task to wake.
crate::thread::yield_now();
}
} else {
while futex.load(Ordering::Acquire) == expected {
crate::thread::yield_now();
}
}

true
}

/// Wakes up one thread that's blocked on `futex_wait` on this futex.
///
/// Returns true if this actually woke up such a thread,
/// or false if no thread was waiting on this futex.
pub fn futex_wake(_futex: &Atomic<u32>) -> bool {
// This matches the behavior of FreeBSD/DragonFlyBSD which also always return false here.
false
}

/// Wakes up all threads that are waiting on `futex_wait` on this futex.
pub fn futex_wake_all(_futex: &Atomic<u32>) {
// The futex_wait will wake itself up whenever the futex is modified, so this can
// stay a no-op.
}
1 change: 1 addition & 0 deletions library/std/src/sys/sync/mutex/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ cfg_select! {
all(target_family = "wasm", target_feature = "atomics"),
target_os = "hermit",
all(target_os = "wasi", target_env = "p3"),
target_os = "vexos",
) => {
mod futex;
pub use futex::Mutex;
Expand Down
1 change: 1 addition & 0 deletions library/std/src/sys/sync/once/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ cfg_select! {
target_os = "fuchsia",
target_os = "hermit",
all(target_os = "wasi", target_env = "p3"),
target_os = "vexos",
) => {
mod futex;
pub use futex::{Once, OnceState};
Expand Down
1 change: 1 addition & 0 deletions library/std/src/sys/sync/rwlock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cfg_select! {
target_os = "hermit",
target_os = "motor",
all(target_os = "wasi", target_env = "p3"),
target_os = "vexos",
) => {
mod futex;
pub use futex::RwLock;
Expand Down
1 change: 1 addition & 0 deletions library/std/src/sys/sync/thread_parking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cfg_select! {
target_os = "motor",
target_os = "hermit",
all(target_os = "wasi", target_env = "p3"),
target_os = "vexos",
) => {
mod futex;
pub use futex::Parker;
Expand Down
8 changes: 7 additions & 1 deletion library/std/src/sys/thread_local/no_threads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ use crate::cell::{Cell, UnsafeCell};
use crate::mem::MaybeUninit;
use crate::ptr;

#[cfg(target_has_threads)]
#[cfg(all(
target_has_threads,
// VEXos is "target_has_threads" because it allows user interrupt handlers which preempt the
// main thread, but they are forbidden from accessing TLS. Since there is only one context
// that's allowed to access thread locals, it's sound to use the no_threads implementation.
not(target_os = "vexos"),
))]
compile_error!("Using no_threads implementation on a target with threads");

#[doc(hidden)]
Expand Down
90 changes: 65 additions & 25 deletions src/doc/rustc/src/platform-support/thumbv7a-vex-v5.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,6 @@ This target is cross-compiled. Dynamic linking is unsupported.

`#![no_std]` crates can be built using `build-std` to build `core` and `panic_abort` and optionally `alloc`. Unwinding panics are not yet supported on this target.

`std` has only partial support due to platform limitations. Notably:

- `std::process` and `std::net` are unimplemented. `std::thread` only supports sleeping and yielding, as this is a single-threaded environment.
- `std::time` has full support for `Instant`, but no support for `SystemTime`.
- `std::io` has full support for `stdin`/`stdout`/`stderr`. `stdout` and `stderr` both write to USB channel 1 on this platform and are not differentiated.
- `std::fs` has limited support for reading or writing to files. The following features are unsupported:
- All directory operations (including `mkdir` and `readdir`), although reading directories is possible through [third-party crates](https://docs.rs/vex-sdk/latest/vex_sdk/file/fn.vexFileDirectoryGet.html)
- Deleting files and directories
- File metadata other than file size and type (that is, file vs. directory)
- Opening files with an uncommon combination of open options, such as read + write at the same time.
The supported modes for opening files are in read-only mode, append mode, or write mode (with or without truncation).
- A global allocator implemented on top of `dlmalloc` is provided.
- Modules that do not need to interact with the OS beyond allocation, such as `std::collections`, `std::hash`, `std::future`, `std::sync`, etc., are fully supported.
- Random number generation and hashing is insecure, as there is no reliable source of entropy on this platform.

When compiling for this target, the "C" calling convention maps to AAPCS with VFP registers (hard float ABI) and the "system" calling convention maps to AAPCS without VFP registers (softfp ABI).

This target generates binaries in the ELF format that may be uploaded to the brain with external tools.

### Platform SDKs

To use most platform-specific APIs, users must configure a supporting runtime SDK for `libstd` to link against. Official *VEXcode* SDKs from VEX can be downloaded and linked via the [`vex-sdk-vexcode`](https://crates.io/crates/vex-sdk-vexcode) crate, but they have a restrictive redistribution policy that might not be suitable for all projects. The suggested SDK for open-source projects is the community-supported [`vex-sdk-jumptable`](https://crates.io/crates/vex-sdk-jumptable) crate. SDK implementations are generally thin wrappers over system calls, so projects should not expect to see significant differences in behavior depending on which SDK they use.

Libraries may access symbols from the active VEX SDK without depending on a specific implementation by using the [`vex-sdk`](https://crates.io/crates/vex-sdk) crate.

## Building the target

You can build Rust with support for this target by adding it to the `target` list in `bootstrap.toml`, and then running `./x build --target thumbv7a-vex-v5 compiler`.
Expand Down Expand Up @@ -100,6 +75,71 @@ fn main() {
}
```

## Environment

The `main` function is entered on a Zynq 7000's CPU1 in the *System* processor mode in *Secure* state. Programs or runtimes should periodically call `std::thread::yield_now` to flush buffers and fetch the latest peripheral state.

Developers writing programs for this target should use a high-level runtime such as [vexide](https://vexide.dev) or a lower-level system access crate such as [`vex-sdk`](https://crates.io/crates/vex-sdk) to access peripherals such as motors and sensors.

When compiling for this target, the "C" calling convention maps to AAPCS with VFP registers (hard float ABI) and the "system" calling convention maps to AAPCS without VFP registers (softfp ABI).

This target generates binaries in the ELF format that may be uploaded to the brain with external tools.

See Also: [VEX V5 Environment](https://internals.vexide.dev/technical/environment).

### Platform SDKs

To use most platform-specific APIs, users must configure a supporting runtime SDK for `libstd` to link against. Official *VEXcode* SDKs from VEX can be downloaded and linked via the [`vex-sdk-vexcode`](https://crates.io/crates/vex-sdk-vexcode) crate, but they have a restrictive redistribution policy that might not be suitable for all projects. The suggested SDK for open-source projects is the community-supported [`vex-sdk-jumptable`](https://crates.io/crates/vex-sdk-jumptable) crate. SDK implementations are generally thin wrappers over system calls, so projects should not expect to see significant differences in behavior depending on which SDK they use.

Libraries may access symbols from the active VEX SDK without depending on a specific implementation by using the [`vex-sdk`](https://crates.io/crates/vex-sdk) crate.

### Standard Library Support

`std` has only partial support due to platform limitations. Notably:

- `std::process` and `std::net` are unimplemented. `std::thread` only supports sleeping, yielding, and parking.
- `std::time` has full support for `Instant`, but no support for `SystemTime`.
- `std::io` has full support for `stdin`/`stdout`/`stderr`. `stdout` and `stderr` both write to USB channel 1 on this platform and are not differentiated.
- `std::fs` has limited support for reading or writing to files. The following features are unsupported:
- All directory operations (including `mkdir` and `readdir`), although reading directories is possible through [third-party crates](https://docs.rs/vex-sdk/latest/vex_sdk/file/fn.vexFileDirectoryGet.html)
- Deleting files and directories
- File metadata other than file size and type (that is, file vs. directory)
- Opening files with an uncommon combination of open options, such as read + write at the same time.
The supported modes for opening files are in read-only mode, append mode, or write mode (with or without truncation).
- A global allocator implemented on top of `dlmalloc` is provided.
- `std::sync`'s synchronization primitives are implemented over simple spinlocks on `std::thread::yield_now`.
- Modules that do not need to interact with the OS beyond allocation, such as `std::collections`, `std::hash`, `std::future`, etc., are fully supported.
- Random number generation and hashing is insecure, as there is no reliable source of entropy on this platform.

### Thread Safety and Synchronization

*This section only applies to programs that use the `std` crate.*

When executing on this target, the `std` crate only supports one Rust execution context plus any user-installed Armv7-A exception handlers (configured using the `VBAR` register), provided they properly synchronize access to shared resources. `std` on this target is not aware of any execution inside a task scheduler, so `thread_local` globals will always have the same identity.

#### VEXos Task Scheduler

This target begins execution outside of the context of the builtin [VEXos task scheduler](https://internals.vexide.dev/sdk/tasks). Spawning VEXos Full Tasks through VEX's undocumented scheduler API (e.g. using the `vexTaskAdd` function) or using any other stack-switching scheduler (including FreeRTOS) is strictly forbidden on this target and will result in undefined behavior. Rust programs may assume that `vexTasksRun` and `std::thread::yield_now` will never switch stacks.

Code running inside a VEXos Simple Task context (for example, inside a user touchscreen callback) is forbidden from ticking the task scheduler reentrantly. Thus, it is invalid for task code to:

- call `vexTasksRun`, `thread::yield_now`, `thread::sleep`, `thread::park`, or derivatives,
- use blocking methods in `std::sync` including `LazyLock::deref` and `OnceLock::get_or_init`,
- or access standard I/O streams via `std::io`.

#### Exception Handlers

`std::sync` may be used to synchronize resources shared between ISRs and the main thread. However, ISRs should not use blocking functions such as `Mutex::lock`, `Condvar::wait`, `LazyLock::deref`, or `OnceLock::get_or_init` to prevent deadlocks and stack overflows (from ticking the scheduler on the small IRQ stack).

ARM exception handlers are forbidden from:

- accessing the default allocator or `std::fs`,
- accessing `thread_local!` values whatsoever,
- panicking, which indirectly accesses thread locals,
- or accessing standard I/O streams via `std::io`

since these features are not synchronized.

## Testing

Binaries built for this target can be run in an emulator (such as [vex-v5-qemu](https://github.com/vexide/vex-v5-qemu)), or uploaded to a physical device over a USB serial connection.
Expand Down
Loading