Выбрать главу

volatile bool signalled = false;

void ISR() {

// Signal that the interrupt has occurred

signalled = true;

}

void driver() {

while(true) {

// Sleep until signalled

while(!signalled) { WFI(); }

// Reset signalled indicator

signalled = false;

// Perform some task that was waiting for the interrupt

run_task();

}

}

The equivalent in Rust would use volatile methods on each access:

static mut SIGNALLED: bool = false;

#[interrupt]

fn ISR() {

// Signal that the interrupt has occurred

// (In real code, you should consider a higher level primitive,

// such as an atomic type).

unsafe { core::ptr::write_volatile(&mut SIGNALLED, true) };

}

fn driver() {

loop {

// Sleep until signalled

while unsafe { !core::ptr::read_volatile(&SIGNALLED) } {}

// Reset signalled indicator

unsafe { core::ptr::write_volatile(&mut SIGNALLED, false) };

// Perform some task that was waiting for the interrupt

run_task();

}

}

A few things are worth noting in the code sample:

   • We can pass &mut SIGNALLED into the function requiring *mut T, since &mut T automatically converts to a *mut T (and the same for *const T)

   • We need unsafe blocks for the read_volatile/write_volatile methods, since they are unsafe functions. It is the programmer's responsibility to ensure safe use: see the methods' documentation for further details.

It is rare to require these functions directly in your code, as they will usually be taken care of for you by higher-level libraries. For memory mapped peripherals, the peripheral access crates will implement volatile access automatically, while for concurrency primitives there are better abstractions available (see the Concurrency chapter).

In embedded C it is common to tell the compiler a variable must have a certain alignment or a struct must be packed rather than aligned, usually to meet specific hardware or protocol requirements.

In Rust this is controlled by the repr attribute on a struct or union. The default representation provides no guarantees of layout, so should not be used for code that interoperates with hardware or C. The compiler may re-order struct members or insert padding and the behaviour may change with future versions of Rust.

struct Foo {

x: u16,

y: u8,

z: u16,

}

fn main() {

let v = Foo { x: 0, y: 0, z: 0 };

println!("{:p} {:p} {:p}", &v.x, &v.y, &v.z);

}

// 0x7ffecb3511d0 0x7ffecb3511d4 0x7ffecb3511d2

// Note ordering has been changed to x, z, y to improve packing.

To ensure layouts that are interoperable with C, use repr(C):

#[repr(C)]

struct Foo {

x: u16,

y: u8,

z: u16,

}

fn main() {

let v = Foo { x: 0, y: 0, z: 0 };

println!("{:p} {:p} {:p}", &v.x, &v.y, &v.z);

}

// 0x7fffd0d84c60 0x7fffd0d84c62 0x7fffd0d84c64

// Ordering is preserved and the layout will not change over time.

// `z` is two-byte aligned so a byte of padding exists between `y` and `z`.

To ensure a packed representation, use repr(packed):

#[repr(packed)]

struct Foo {

x: u16,

y: u8,

z: u16,

}

fn main() {

let v = Foo { x: 0, y: 0, z: 0 };

// Unsafe is required to borrow a field of a packed struct.

unsafe { println!("{:p} {:p} {:p}", &v.x, &v.y, &v.z) };

}

// 0x7ffd33598490 0x7ffd33598492 0x7ffd33598493

// No padding has been inserted between `y` and `z`, so now `z` is unaligned.

Note that using repr(packed) also sets the alignment of the type to 1.

Finally, to specify a specific alignment, use repr(align(n)), where n is the number of bytes to align to (and must be a power of two):

#[repr(C)]

#[repr(align(4096))]

struct Foo {

x: u16,

y: u8,

z: u16,

}

fn main() {

let v = Foo { x: 0, y: 0, z: 0 };

let u = Foo { x: 0, y: 0, z: 0 };

println!("{:p} {:p} {:p}", &v.x, &v.y, &v.z);

println!("{:p} {:p} {:p}", &u.x, &u.y, &u.z);

}

// 0x7ffec909a000 0x7ffec909a002 0x7ffec909a004

// 0x7ffec909b000 0x7ffec909b002 0x7ffec909b004

// The two instances `u` and `v` have been placed on 4096-byte alignments,

// evidenced by the `000` at the end of their addresses.

Note we can combine repr(C) with repr(align(n)) to obtain an aligned and C-compatible layout. It is not permissible to combine repr(align(n)) with repr(packed), since repr(packed) sets the alignment to 1. It is also not permissible for a repr(packed) type to contain a repr(align(n)) type.

For further details on type layouts, refer to the type layout chapter of the Rust Reference.

   • In this book:

      • A little C with your Rust

      • A little Rust with your C

   • The Rust Embedded FAQs

   • Rust Pointers for C Programmers

   • I used to use pointers - now what?

Interoperability between Rust and C code is always dependent on transforming data between the two languages. For this purposes there are two dedicated modules in the stdlib called std::ffi and std::os::raw.