1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-09-16 09:36:41 +02:00

docs: improve language in bare-metal section (#2891)

I asked Gemini to review the English for inconsistencies and grammar
mistakes. This is the result and I hope it's useful!

As a non-native speaker, it is hard for me to evaluate the finer
details, so let me know if you would like to see changes (or even
better: make them directly in the PR with the suggestion function).

---------

Co-authored-by: Dmitri Gribenko <gribozavr@gmail.com>
This commit is contained in:
Martin Geisler
2025-09-06 20:43:01 +02:00
committed by GitHub
parent 16c960d690
commit d7dc934891
17 changed files with 46 additions and 47 deletions

View File

@@ -1,8 +1,8 @@
# A better UART driver
The PL011 actually has [a bunch more registers][1], and adding offsets to
construct pointers to access them is error-prone and hard to read. Plus, some of
them are bit fields which would be nice to access in a structured way.
The PL011 actually has [more registers][1], and adding offsets to construct
pointers to access them is error-prone and hard to read. Additionally, some of
them are bit fields, which would be nice to access in a structured way.
| Offset | Register name | Width |
| ------ | ------------- | ----- |
@@ -23,7 +23,7 @@ them are bit fields which would be nice to access in a structured way.
<details>
- There are also some ID registers which have been omitted for brevity.
- There are also some ID registers that have been omitted for brevity.
</details>

View File

@@ -1,6 +1,6 @@
# Getting Ready to Rust
Before we can start running Rust code, we need to do some initialisation.
Before we can start running Rust code, we need to do some initialization.
```armasm
{{#include examples/src/entry.S:entry}}
@@ -12,21 +12,21 @@ This code is in `src/bare-metal/aps/examples/src/entry.S`. It's not necessary to
understand this in detail -- the takeaway is that typically some low-level setup
is needed to meet Rust's expectations of the system.
- This is the same as it would be for C: initialising the processor state,
- This is the same as it would be for C: initializing the processor state,
zeroing the BSS, and setting up the stack pointer.
- The BSS (block starting symbol, for historical reasons) is the part of the
object file which containing statically allocated variables which are
initialised to zero. They are omitted from the image, to avoid wasting space
object file that contains statically allocated variables that are
initialized to zero. They are omitted from the image, to avoid wasting space
on zeroes. The compiler assumes that the loader will take care of zeroing
them.
- The BSS may already be zeroed, depending on how memory is initialised and the
- The BSS may already be zeroed, depending on how memory is initialized and the
image is loaded, but we zero it to be sure.
- We need to enable the MMU and cache before reading or writing any memory. If
we don't:
- Unaligned accesses will fault. We build the Rust code for the
`aarch64-unknown-none` target which sets `+strict-align` to prevent the
compiler generating unaligned accesses, so it should be fine in this case,
but this is not necessarily the case in general.
`aarch64-unknown-none` target that sets `+strict-align` to prevent the
compiler from generating unaligned accesses, so it should be fine in this
case, but this is not necessarily the case in general.
- If it were running in a VM, this can lead to cache coherency issues. The
problem is that the VM is accessing memory directly with the cache disabled,
while the host has cacheable aliases to the same memory. Even if the host
@@ -34,14 +34,14 @@ is needed to meet Rust's expectations of the system.
fills, and then changes from one or the other will get lost when the cache
is cleaned or the VM enables the cache. (Cache is keyed by physical address,
not VA or IPA.)
- For simplicity, we just use a hardcoded pagetable (see `idmap.S`) which
- For simplicity, we just use a hardcoded pagetable (see `idmap.S`) that
identity maps the first 1 GiB of address space for devices, the next 1 GiB for
DRAM, and another 1 GiB higher up for more devices. This matches the memory
layout that QEMU uses.
- We also set up the exception vector (`vbar_el1`), which we'll see more about
later.
- All examples this afternoon assume we will be running at exception level 1
(EL1). If you need to run at a different exception level you'll need to modify
`entry.S` accordingly.
(EL1). If you need to run at a different exception level, you'll need to
modify `entry.S` accordingly.
</details>

View File

@@ -16,7 +16,7 @@ for all these functions.)
- PSCI is the Arm Power State Coordination Interface, a standard set of
functions to manage system and CPU power states, among other things. It is
implemented by EL3 firmware and hypervisors on many systems.
- The `0 => _` syntax means initialise the register to 0 before running the
- The `0 => _` syntax means initialize the register to 0 before running the
inline assembly code, and ignore its contents afterwards. We need to use
`inout` rather than `in` because the call could potentially clobber the
contents of the registers.
@@ -24,7 +24,7 @@ for all these functions.)
because it is called from our entry point in `entry.S`.
- Just `#[no_mangle]` would be sufficient but
[RFC3325](https://rust-lang.github.io/rfcs/3325-unsafe-attributes.html) uses
this notation to draw reviewer attention to attributes which might cause
this notation to draw reviewer attention to attributes that might cause
undefined behavior if used incorrectly.
- `_x0`–`_x3` are the values of registers `x0`–`x3`, which are conventionally
used by the bootloader to pass things like a pointer to the device tree.

View File

@@ -9,7 +9,7 @@ We can do this by implementing the `Log` trait.
<details>
- The first unwrap in `log` will succeed because we initialise `LOGGER` before
- The first unwrap in `log` will succeed because we initialize `LOGGER` before
calling `set_logger`. The second will succeed because `Uart::write_str` always
returns `Ok`.

View File

@@ -28,7 +28,7 @@ unsafe {
compiler may assume that the value read is the same as the value just
written, and not bother actually reading memory.
- Some existing crates for volatile access to hardware do hold references, but
this is unsound. Whenever a reference exist, the compiler may choose to
this is unsound. Whenever a reference exists, the compiler may choose to
dereference it.
- Use `&raw` to get struct field pointers from a pointer to the struct.
- For compatibility with old versions of Rust you can use the [`addr_of!`] macro

View File

@@ -5,9 +5,9 @@
- Supports x86, aarch64 and RISC-V.
- Relies on LinuxBoot rather than having many drivers itself.
- [Rust RaspberryPi OS tutorial](https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials)
- Initialisation, UART driver, simple bootloader, JTAG, exception levels,
- Initialization, UART driver, simple bootloader, JTAG, exception levels,
exception handling, page tables.
- Some dodginess around cache maintenance and initialisation in Rust, not
- Some caveats around cache maintenance and initialization in Rust, not
necessarily a good example to copy for production code.
- [`cargo-call-stack`](https://crates.io/crates/cargo-call-stack)
- Static analysis to determine maximum stack usage.

View File

@@ -17,14 +17,14 @@ Now let's use the new `Registers` struct in our driver.
- These MMIO accesses are generally a wrapper around `read_volatile` and
`write_volatile`, though on aarch64 they are instead implemented in assembly
to work around a bug where the compiler can emit instructions that prevent
MMIO virtualisation.
MMIO virtualization.
- The `field!` and `field_shared!` macros internally use `&raw mut` and
`&raw const` to get pointers to individual fields without creating an
intermediate reference, which would be unsound.
- `field!` needs a mutable reference to a `UniqueMmioPointer`, and returns a
`UniqueMmioPointer` which allows reads with side effects and writes.
`UniqueMmioPointer` that allows reads with side effects and writes.
- `field_shared!` works with a shared reference to either a `UniqueMmioPointer`
or a `SharedMmioPointer`. It returns a `SharedMmioPointer` which only allows
or a `SharedMmioPointer`. It returns a `SharedMmioPointer` that only allows
pure reads.
</details>

View File

@@ -1,6 +1,6 @@
# safe-mmio
The [`safe-mmio`] crate provides types to wrap registers which can be read or
The [`safe-mmio`] crate provides types to wrap registers that can be read or
written safely.
| | Can't read | Read has no side-effects | Read has side-effects |
@@ -23,7 +23,7 @@ written safely.
operations; we recommend the `safe-mmio` crate.
- The difference between `ReadPure` or `ReadOnly` (and likewise between
`ReadPureWrite` and `ReadWrite`) is whether reading a register can have
side-effects which change the state of the device. E.g. reading the data
side-effects that change the state of the device, e.g., reading the data
register pops a byte from the receive FIFO. `ReadPure` means that reads have
no side-effects, they are purely reading data.

View File

@@ -12,7 +12,7 @@ for convenience.
<details>
- In this case the board support crate is just providing more useful names, and
a bit of initialisation.
a bit of initialization.
- The crate may also include drivers for some on-board devices outside of the
microcontroller itself.
- `microbit-v2` includes a simple driver for the LED matrix.

View File

@@ -16,8 +16,8 @@ accelerometer driver might need an I2C or SPI device instance.
<details>
- The traits cover using the peripherals but not initialising or configuring
them, as initialisation and configuration is usually highly platform-specific.
- The traits cover using the peripherals but not initializing or configuring
them, as initialization and configuration is usually highly platform-specific.
- There are implementations for many microcontrollers, as well as other
platforms such as Linux on Raspberry Pi.
- [`embedded-hal-async`] provides async versions of the traits.

View File

@@ -11,12 +11,11 @@ wrappers for memory-mapped peripherals from
<details>
- SVD (System View Description) files are XML files typically provided by
silicon vendors which describe the memory map of the device.
- They are organised by peripheral, register, field and value, with names,
silicon vendors that describe the memory map of the device.
- They are organized by peripheral, register, field and value, with names,
descriptions, addresses and so on.
- SVD files are often buggy and incomplete, so there are various projects
which patch the mistakes, add missing details, and publish the generated
crates.
- SVD files are often buggy and incomplete, so there are various projects that
patch the mistakes, add missing details, and publish the generated crates.
- `cortex-m-rt` provides the vector table, among other things.
- If you `cargo install cargo-binutils` then you can run
`cargo objdump --bin pac -- -d --no-show-raw-insn` to see the resulting

View File

@@ -21,7 +21,7 @@ in your project directory.
a range from SEGGER.
- The Debug Access Port is usually either a 5-pin JTAG interface or 2-pin Serial
Wire Debug.
- probe-rs is a library which you can integrate into your own tools if you want
- probe-rs is a library that you can integrate into your own tools if you want
to.
- The
[Microsoft Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/)
@@ -29,6 +29,6 @@ in your project directory.
microcontroller.
- cargo-embed is a binary built using the probe-rs library.
- RTT (Real Time Transfers) is a mechanism to transfer data between the debug
host and the target through a number of ringbuffers.
host and the target through a number of ring buffers.
</details>

View File

@@ -7,12 +7,12 @@
<details>
- Pins don't implement `Copy` or `Clone`, so only one instance of each can
exist. Once a pin is moved out of the port struct nobody else can take it.
exist. Once a pin is moved out of the port struct, nobody else can take it.
- Changing the configuration of a pin consumes the old pin instance, so you
can’t keep use the old instance afterwards.
- The type of a value indicates the state that it is in: e.g. in this case, the
can't use the old instance afterwards.
- The type of a value indicates the state it is in: e.g., in this case, the
configuration state of a GPIO pin. This encodes the state machine into the
type system, and ensures that you don't try to use a pin in a certain way
type system and ensures that you don't try to use a pin in a certain way
without properly configuring it first. Illegal state transitions are caught at
compile time.
- You can call `is_high` on an input pin and `set_high` on an output pin, but

View File

@@ -1,4 +1,4 @@
# Useful crates
We'll look at a few crates which solve some common problems in bare-metal
We'll look at a few crates that solve some common problems in bare-metal
programming.

View File

@@ -1,6 +1,6 @@
# `buddy_system_allocator`
[`buddy_system_allocator`][1] is a crate implementing a basic buddy system
[`buddy_system_allocator`][1] is a crate that implements a basic buddy system
allocator. It can be used both to implement [`GlobalAlloc`][3] (using
[`LockedHeap`][2]) so you can use the standard `alloc` crate (as we saw
[before][4]), or for allocating other address space (using

View File

@@ -25,8 +25,8 @@ fn main() {
- Be careful to avoid deadlock if you take locks in interrupt handlers.
- `spin` also has a ticket lock mutex implementation; equivalents of `RwLock`,
`Barrier` and `Once` from `std::sync`; and `Lazy` for lazy initialisation.
- The [`once_cell`][2] crate also has some useful types for late initialisation
`Barrier` and `Once` from `std::sync`; and `Lazy` for lazy initialization.
- The [`once_cell`][2] crate also has some useful types for late initialization
with a slightly different approach to `spin::once::Once`.
- The Rust Playground includes `spin`, so this example will run fine inline.

View File

@@ -1,8 +1,8 @@
# `tinyvec`
Sometimes you want something which can be resized like a `Vec`, but without heap
Sometimes you want something that can be resized like a `Vec`, but without heap
allocation. [`tinyvec`][1] provides this: a vector backed by an array or slice,
which could be statically allocated or on the stack, which keeps track of how
which could be statically allocated or on the stack, that keeps track of how
many elements are used and panics if you try to use more than are allocated.
<!-- mdbook-xgettext: skip -->
@@ -23,7 +23,7 @@ fn main() {
<details>
- `tinyvec` requires that the element type implement `Default` for
initialisation.
initialization.
- The Rust Playground includes `tinyvec`, so this example will run fine inline.
</details>