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); } asm::dmb(); } // 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); } asm::dmb(); // ensure data at (ptr) has been fully written 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 } // return true if it is guaranteed that the next self.receive() // will return actual data rather than 0 pub fn available(&self) -> bool { let ptr = unsafe { read_volatile(&self.pointer) }; asm::dmb(); 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) } }