2019-08-30 15:55:59 +08:00
|
|
|
use crate::cortex_a9::asm;
|
|
|
|
use core::ptr::{read_volatile, write_volatile};
|
|
|
|
|
|
|
|
/*
|
|
|
|
One-way mailbox:
|
|
|
|
|
|
|
|
All transmissions must originate from one core only,
|
|
|
|
and all receives from the other core only.
|
|
|
|
|
|
|
|
Example transmission (to be executed on core 0):
|
|
|
|
{
|
|
|
|
while (!MAILBOX_FROM_CORE0.acknowledged()) {}
|
|
|
|
println!("ready to send");
|
|
|
|
MAILBOX_FROM_CORE0.send(&data);
|
|
|
|
println!("sent");
|
|
|
|
while (!MAILBOX_FROM_CORE0.acknowledged()) {}
|
|
|
|
println!("got receipt (acknowledgement)");
|
|
|
|
}
|
|
|
|
|
|
|
|
Example reception (to be executed on core 1):
|
|
|
|
{
|
|
|
|
println("wait for data");
|
|
|
|
while (!MAILBOX_FROM_CORE0.available()) {}
|
|
|
|
let data = MAILBOX_FROM_CORE0.receive();
|
|
|
|
println("data received");
|
|
|
|
MAILBOX_FROM_CORE0.acknowledge(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
Note that unsafe { ... } blocks must be used around most functions;
|
|
|
|
these have been omitted from the examples for clarity.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
pub struct OneWayMailbox {
|
|
|
|
// pointer (data to be transferred): write-only for sending core,
|
|
|
|
// readable and clearable (to 0) for receiving core
|
|
|
|
pointer: usize,
|
|
|
|
|
|
|
|
// helper variable (last pointer value received) for receiving
|
|
|
|
// core
|
|
|
|
echo: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub static mut MAILBOX_FROM_CORE0: OneWayMailbox = OneWayMailbox::new();
|
|
|
|
pub static mut MAILBOX_FROM_CORE1: OneWayMailbox = OneWayMailbox::new();
|
|
|
|
|
|
|
|
impl OneWayMailbox {
|
|
|
|
// instantiate a one-way mailbox with no undelivered message
|
|
|
|
pub const fn new() -> OneWayMailbox {
|
|
|
|
OneWayMailbox { pointer: 0, echo: 0 }
|
|
|
|
}
|
|
|
|
|
|
|
|
// recreate pristine condition; may only be called when producers
|
|
|
|
// and consumers are stopped (e.g. when starting core 1 from core
|
|
|
|
// 0).
|
|
|
|
pub fn reset_discard(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
write_volatile(&mut self.pointer, 0);
|
|
|
|
write_volatile(&mut self.echo, 0);
|
|
|
|
}
|
2019-08-30 15:56:42 +08:00
|
|
|
asm::dmb();
|
2019-08-30 15:55:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// send a pointer from one core to be received by the other core
|
|
|
|
pub fn send(&mut self, ptr: usize) -> usize {
|
|
|
|
assert!(ptr != 0); // ptr may not be the NULL-like flag
|
|
|
|
unsafe {
|
|
|
|
write_volatile(&mut self.pointer, ptr);
|
|
|
|
}
|
2019-08-30 15:56:42 +08:00
|
|
|
asm::dmb(); // ensure data at (ptr) has been fully written
|
2019-08-30 15:55:59 +08:00
|
|
|
ptr
|
|
|
|
}
|
|
|
|
|
|
|
|
// receive a pointer from the other core, or 0 if none is present
|
|
|
|
pub fn receive(&self) -> usize {
|
|
|
|
let ptr = unsafe {
|
|
|
|
read_volatile(&self.pointer)
|
|
|
|
};
|
|
|
|
// necessary memory barrier to guarantee that the data at
|
|
|
|
// (ptr) has been fully written before it may be accessed
|
|
|
|
// by the caller of this function
|
|
|
|
asm::dmb();
|
|
|
|
ptr
|
|
|
|
}
|
|
|
|
|
2019-08-30 15:56:42 +08:00
|
|
|
// return true if it is guaranteed that the next self.receive()
|
|
|
|
// will return actual data rather than 0
|
2019-08-30 15:55:59 +08:00
|
|
|
pub fn available(&self) -> bool {
|
|
|
|
let ptr = unsafe {
|
|
|
|
read_volatile(&self.pointer)
|
|
|
|
};
|
2019-08-30 15:56:42 +08:00
|
|
|
asm::dmb();
|
2019-08-30 15:55:59 +08:00
|
|
|
ptr != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// acknowledge receipt of data to the sender (i.e. release it)
|
|
|
|
pub fn acknowledge(&mut self, ptr: usize) {
|
|
|
|
// ensure that the data we release is the data last sent
|
|
|
|
assert_eq!(ptr, unsafe {
|
|
|
|
read_volatile(&self.pointer)
|
|
|
|
});
|
|
|
|
// first possibility for "release" flag:
|
|
|
|
// pointer and echo are equal
|
|
|
|
unsafe {
|
|
|
|
write_volatile(&mut self.echo, ptr);
|
|
|
|
}
|
|
|
|
asm::dmb(); // write to self.echo before self.pointer
|
|
|
|
// second possibility for "release" flag:
|
|
|
|
// NULL-like pointer
|
|
|
|
unsafe {
|
|
|
|
write_volatile(&mut self.pointer, 0);
|
|
|
|
}
|
|
|
|
asm::dmb();
|
|
|
|
// reset echo
|
|
|
|
unsafe {
|
|
|
|
write_volatile(&mut self.echo, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// has data been acknowledged?
|
|
|
|
pub fn acknowledged(&self) -> bool {
|
|
|
|
let ptr = unsafe {
|
|
|
|
read_volatile(&self.pointer)
|
|
|
|
};
|
|
|
|
// read self.pointer before self.echo, not after
|
|
|
|
asm::dmb();
|
|
|
|
let echo = unsafe {
|
|
|
|
read_volatile(&self.echo)
|
|
|
|
};
|
|
|
|
(ptr == 0) || (ptr == echo)
|
|
|
|
}
|
|
|
|
}
|