Compare commits
3 Commits
master
...
multiproce
Author | SHA1 | Date |
---|---|---|
Björn Stein | 606fef6d5c | |
Björn Stein | 1f4add397b | |
Björn Stein | 60bab77a19 |
1
link.x
1
link.x
|
@ -1,5 +1,6 @@
|
||||||
ENTRY(_boot_cores);
|
ENTRY(_boot_cores);
|
||||||
|
|
||||||
|
/* Size of stack for core 0 in bytes */
|
||||||
STACK_SIZE = 0x8000;
|
STACK_SIZE = 0x8000;
|
||||||
|
|
||||||
/* Provide some defaults */
|
/* Provide some defaults */
|
||||||
|
|
|
@ -10,6 +10,12 @@ pub fn wfe() {
|
||||||
unsafe { asm!("wfe" :::: "volatile") }
|
unsafe { asm!("wfe" :::: "volatile") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send Event
|
||||||
|
#[inline]
|
||||||
|
pub fn sev() {
|
||||||
|
unsafe { asm!("sev" :::: "volatile") }
|
||||||
|
}
|
||||||
|
|
||||||
/// Data Memory Barrier
|
/// Data Memory Barrier
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn dmb() {
|
pub fn dmb() {
|
||||||
|
@ -27,3 +33,4 @@ pub fn dsb() {
|
||||||
pub fn isb() {
|
pub fn isb() {
|
||||||
unsafe { asm!("isb" :::: "volatile") }
|
unsafe { asm!("isb" :::: "volatile") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,7 +124,9 @@ impl L1Table {
|
||||||
tex: 0b101,
|
tex: 0b101,
|
||||||
domain: 0b1111,
|
domain: 0b1111,
|
||||||
exec: true,
|
exec: true,
|
||||||
cacheable: false,
|
// TODO: temporarily turn on cache for SMP testing;
|
||||||
|
// consider turning it off again for production
|
||||||
|
cacheable: !false,
|
||||||
bufferable: true,
|
bufferable: true,
|
||||||
});
|
});
|
||||||
/* (DDR cacheable) */
|
/* (DDR cacheable) */
|
||||||
|
|
|
@ -115,6 +115,45 @@ register_bit!(sctlr,
|
||||||
/// Thumb Exception Enable
|
/// Thumb Exception Enable
|
||||||
te, 30);
|
te, 30);
|
||||||
|
|
||||||
|
impl crate::regs::RegisterRW for SCTLR {
|
||||||
|
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
|
||||||
|
// todo: this may fail for .nmfi and, in non-secure state,
|
||||||
|
// also RR (bit 14)
|
||||||
|
let inner = self.read().inner;
|
||||||
|
let inner_w = f(
|
||||||
|
sctlr::Read { inner },
|
||||||
|
sctlr::Write { inner }
|
||||||
|
);
|
||||||
|
self.write(inner_w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Auxiliary Control Register
|
||||||
|
pub struct ACTLR;
|
||||||
|
wrap_reg!(actlr);
|
||||||
|
def_reg_r!(ACTLR, actlr::Read, "mrc p15, 0, $0, c1, c0, 1");
|
||||||
|
def_reg_w!(ACTLR, actlr::Write, "mcr p15, 0, $0, c1, c0, 1");
|
||||||
|
// SMP bit
|
||||||
|
register_bit!(actlr, parity_on, 9);
|
||||||
|
register_bit!(actlr, alloc_one_way, 8);
|
||||||
|
register_bit!(actlr, excl, 7);
|
||||||
|
register_bit!(actlr, smp, 6);
|
||||||
|
register_bit!(actlr, write_full_line_of_zeros, 3);
|
||||||
|
register_bit!(actlr, l1_prefetch_enable, 2);
|
||||||
|
// Cache/TLB maintenance broadcast
|
||||||
|
register_bit!(actlr, fw, 0);
|
||||||
|
|
||||||
|
impl crate::regs::RegisterRW for ACTLR {
|
||||||
|
fn modify<F: FnOnce(Self::R, Self::W) -> Self::W>(&mut self, f: F) {
|
||||||
|
let inner = self.read().inner;
|
||||||
|
let inner_w = f(
|
||||||
|
actlr::Read { inner },
|
||||||
|
actlr::Write { inner }
|
||||||
|
);
|
||||||
|
self.write(inner_w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Domain Access Control Register
|
/// Domain Access Control Register
|
||||||
pub struct DACR;
|
pub struct DACR;
|
||||||
def_reg_r!(DACR, u32, "mrc p15, 0, $0, c3, c0, 0");
|
def_reg_r!(DACR, u32, "mrc p15, 0, $0, c3, c0, 0");
|
||||||
|
@ -163,9 +202,51 @@ pub fn bpiall() {
|
||||||
|
|
||||||
/// Invalidate D-Cache
|
/// Invalidate D-Cache
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn dccisw() {
|
pub fn dcisw(setway: u32) {
|
||||||
// TODO: $0 is r11 at what value?
|
// TODO: $0 is r11 at what value?
|
||||||
unsafe {
|
unsafe {
|
||||||
asm!("mcr p15, 0, $0, c7, c5, 6" :: "r" (0) :: "volatile");
|
// steinb: the following is incorrect
|
||||||
|
//asm!("mcr p15, 0, $0, c7, c5, 6" :: "r" (0) :: "volatile");
|
||||||
|
|
||||||
|
// acc. to ARM Architecture Reference Manual, Figure B3-32;
|
||||||
|
// also see example code (for DCCISW, but DCISW will be
|
||||||
|
// analogous) "Example code for cache maintenance operations"
|
||||||
|
// on pages B2-1286 and B2-1287.
|
||||||
|
asm!("mcr p15, 0, $0, c7, c6, 2" :: "r" (setway) :: "volatile");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A made-up "instruction": invalidate all of the L1 D-Cache
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn dciall() {
|
||||||
|
// the cache associativity could be read from a register, but will
|
||||||
|
// always be 4 in L1 data cache of a cortex a9
|
||||||
|
let ways = 4;
|
||||||
|
let bit_pos_of_way = 30; // 32 - log2(ways)
|
||||||
|
|
||||||
|
// the cache sets could be read from a register, but are always
|
||||||
|
// 256 for the cores in the zync-7000; in general, 128 or 512 are
|
||||||
|
// also possible for a Cortex-A9.
|
||||||
|
let sets = 256;
|
||||||
|
let bit_pos_of_set = 5; // for a line size of 8 words = 2^5 bytes
|
||||||
|
|
||||||
|
// select L1 data cache
|
||||||
|
unsafe {
|
||||||
|
asm!("mcr p15, 2, $0, c0, c0, 0" :: "r" (0) :: "volatile");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalidate entire D-Cache by iterating every set and every way
|
||||||
|
for set in 0..sets {
|
||||||
|
for way in 0..ways {
|
||||||
|
dcisw((set << bit_pos_of_set) | (way << bit_pos_of_way));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// clear cache line by virtual address to point of coherency (DCCMVAC)
|
||||||
|
#[inline]
|
||||||
|
pub fn dccmvac(addr: u32) {
|
||||||
|
unsafe {
|
||||||
|
asm!("mcr p15, 0, $0, c7, c10, 1" :: "r" (addr) :: "volatile");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
235
src/main.rs
235
src/main.rs
|
@ -9,30 +9,41 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use core::mem::{uninitialized, transmute};
|
use core::mem::{uninitialized, transmute};
|
||||||
|
use core::ptr::write_volatile;
|
||||||
use r0::zero_bss;
|
use r0::zero_bss;
|
||||||
use compiler_builtins as _;
|
use compiler_builtins as _;
|
||||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
|
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
|
||||||
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, EthernetInterface};
|
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, EthernetInterface};
|
||||||
use smoltcp::time::Instant;
|
use smoltcp::time::Instant;
|
||||||
use smoltcp::socket::SocketSet;
|
use smoltcp::socket::SocketSet;
|
||||||
|
use mailbox::{MAILBOX_FROM_CORE0, MAILBOX_FROM_CORE1};
|
||||||
|
|
||||||
mod regs;
|
mod regs;
|
||||||
mod cortex_a9;
|
mod cortex_a9;
|
||||||
mod clocks;
|
mod clocks;
|
||||||
|
mod mailbox;
|
||||||
|
mod mpcore;
|
||||||
|
mod mutex;
|
||||||
mod slcr;
|
mod slcr;
|
||||||
mod uart;
|
mod uart;
|
||||||
mod stdio;
|
mod stdio;
|
||||||
mod eth;
|
mod eth;
|
||||||
|
|
||||||
use crate::regs::{RegisterR, RegisterW};
|
use crate::regs::{RegisterR, RegisterW, RegisterRW};
|
||||||
use crate::cortex_a9::{asm, regs::*, mmu};
|
use crate::cortex_a9::{asm, regs::*, mmu};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
static mut __bss_start: u32;
|
static mut __bss_start: u32;
|
||||||
static mut __bss_end: u32;
|
static mut __bss_end: u32;
|
||||||
static mut __stack_start: u32;
|
static mut __stack_start: u32; // refers to the stack for core 0
|
||||||
|
static mut __stack1_start: u32; // refers to the stack for core 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// program address as u32, for execution after setting up core 1
|
||||||
|
static mut START_ADDR_CORE1: u32 = 0;
|
||||||
|
// initial stack pointer for starting core 1
|
||||||
|
static mut INITIAL_SP_CORE1: u32 = 0; // must be zero (as a flag)
|
||||||
|
|
||||||
#[link_section = ".text.boot"]
|
#[link_section = ".text.boot"]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[naked]
|
#[naked]
|
||||||
|
@ -41,13 +52,24 @@ pub unsafe extern "C" fn _boot_cores() -> ! {
|
||||||
|
|
||||||
match MPIDR.read() & CORE_MASK {
|
match MPIDR.read() & CORE_MASK {
|
||||||
0 => {
|
0 => {
|
||||||
|
// executing on core 0
|
||||||
SP.write(&mut __stack_start as *mut _ as u32);
|
SP.write(&mut __stack_start as *mut _ as u32);
|
||||||
boot_core0();
|
boot_core0();
|
||||||
}
|
}
|
||||||
_ => loop {
|
_ => {
|
||||||
// if not core0, infinitely wait for events
|
// executing on core 1 (as there are only cores 0 and 1)
|
||||||
asm::wfe();
|
while INITIAL_SP_CORE1 == 0 {
|
||||||
},
|
// NOTE: This wfe and its loop can be removed as long
|
||||||
|
// as the regular boot loader remains in place
|
||||||
|
// (i.e. this program is not written into ROM).
|
||||||
|
asm::wfe();
|
||||||
|
}
|
||||||
|
|
||||||
|
// the following requires a stack (at least later, for the
|
||||||
|
// function for setting up the MMU)
|
||||||
|
SP.write(INITIAL_SP_CORE1);
|
||||||
|
boot_core1();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,16 +77,59 @@ pub unsafe extern "C" fn _boot_cores() -> ! {
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
unsafe fn boot_core0() -> ! {
|
unsafe fn boot_core0() -> ! {
|
||||||
l1_cache_init();
|
l1_cache_init();
|
||||||
|
|
||||||
|
// Invalidate SCU, for all cores
|
||||||
|
mpcore::RegisterBlock::new().scu_invalidate.write(0xffff);
|
||||||
|
|
||||||
zero_bss(&mut __bss_start, &mut __bss_end);
|
zero_bss(&mut __bss_start, &mut __bss_end);
|
||||||
|
|
||||||
let mmu_table = mmu::L1Table::get()
|
let mmu_table = mmu::L1Table::get()
|
||||||
.setup_flat_layout();
|
.setup_flat_layout();
|
||||||
mmu::with_mmu(mmu_table, || {
|
mmu::with_mmu(mmu_table, || {
|
||||||
|
// start SCU
|
||||||
|
mpcore::RegisterBlock::new().scu_control.modify(
|
||||||
|
|_, w| w.enable(true)
|
||||||
|
);
|
||||||
|
// enable SMP (for starting correct SCU operation)
|
||||||
|
ACTLR.modify(|_, w|
|
||||||
|
w.smp(true) // SMP mode
|
||||||
|
.fw(true) // cache and TLB maintenance broadcast on
|
||||||
|
);
|
||||||
|
asm::dmb();
|
||||||
|
asm::dsb();
|
||||||
main();
|
main();
|
||||||
panic!("return from main");
|
panic!("return from main");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[naked]
|
||||||
|
#[inline(never)]
|
||||||
|
unsafe fn boot_core1() -> ! {
|
||||||
|
l1_cache_init();
|
||||||
|
|
||||||
|
// Invalidate SCU, for core1 only
|
||||||
|
mpcore::RegisterBlock::new().scu_invalidate.write(0x00f0);
|
||||||
|
|
||||||
|
// use the MMU L1 Table already set up by core 0
|
||||||
|
let mmu_table = mmu::L1Table::get();
|
||||||
|
mmu::with_mmu(mmu_table, || {
|
||||||
|
// enable SMP (for correct SCU operation)
|
||||||
|
ACTLR.modify(|_, w|
|
||||||
|
w.smp(true) // SMP mode
|
||||||
|
.fw(true) // cache and TLB maintenance broadcast
|
||||||
|
);
|
||||||
|
|
||||||
|
asm::dmb();
|
||||||
|
asm::dsb();
|
||||||
|
|
||||||
|
// now that the MMU is active using the same table as active
|
||||||
|
// on the other core, one can branch to any normal memory
|
||||||
|
// location in which the code may reside
|
||||||
|
asm!("bx r1" :: "{r1}"(START_ADDR_CORE1) :: "volatile");
|
||||||
|
unreachable!();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn l1_cache_init() {
|
fn l1_cache_init() {
|
||||||
// Invalidate TLBs
|
// Invalidate TLBs
|
||||||
tlbiall();
|
tlbiall();
|
||||||
|
@ -73,13 +138,123 @@ fn l1_cache_init() {
|
||||||
// Invalidate Branch Predictor Array
|
// Invalidate Branch Predictor Array
|
||||||
bpiall();
|
bpiall();
|
||||||
// Invalidate D-Cache
|
// Invalidate D-Cache
|
||||||
dccisw();
|
//
|
||||||
|
// Note: Do use dcisw rather than dccisw to only invalidate rather
|
||||||
|
// than also clear (which may write values back into the
|
||||||
|
// underlying L2 cache or memory!)
|
||||||
|
//
|
||||||
|
// use the "made-up instruction" (see definition) dciall()
|
||||||
|
dciall();
|
||||||
|
|
||||||
|
asm::dsb();
|
||||||
|
asm::isb();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stop_core1() {
|
||||||
|
slcr::RegisterBlock::unlocked(|slcr| {
|
||||||
|
slcr.a9_cpu_rst_ctrl.modify(|_, w| {
|
||||||
|
w.a9_rst1(true)
|
||||||
|
});
|
||||||
|
slcr.a9_cpu_rst_ctrl.modify(|_, w| {
|
||||||
|
w.a9_clkstop1(true)
|
||||||
|
});
|
||||||
|
slcr.a9_cpu_rst_ctrl.modify(|_, w| {
|
||||||
|
w.a9_rst1(false)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute f on core 1 using the given stack. Note that these
|
||||||
|
// semantics are inherently unsafe as the stack needs to live longer
|
||||||
|
// than Rust semantics dictate...hence this method is marked as unsafe
|
||||||
|
// to remind the caller to take special care (but also many operations
|
||||||
|
// performed would otherwise require `unsafe` blocks).
|
||||||
|
unsafe fn run_on_core1(f: fn() -> !, stack: &mut [u32]) {
|
||||||
|
// reset and stop core 1 (this is safe to repeat, if the caller
|
||||||
|
// has already performed this)
|
||||||
|
stop_core1();
|
||||||
|
|
||||||
|
// ensure any mailbox access finishes before the mailbox reset
|
||||||
|
asm::dmb();
|
||||||
|
// reset the mailbox for sending messages
|
||||||
|
MAILBOX_FROM_CORE0.reset_discard();
|
||||||
|
MAILBOX_FROM_CORE1.reset_discard();
|
||||||
|
// determine address of f and save it as start address for core 1
|
||||||
|
write_volatile(
|
||||||
|
&mut START_ADDR_CORE1,
|
||||||
|
f as *const () as u32
|
||||||
|
);
|
||||||
|
write_volatile(
|
||||||
|
&mut INITIAL_SP_CORE1,
|
||||||
|
&mut stack[stack.len() - 1] as *const _ as u32
|
||||||
|
);
|
||||||
|
// ensure the above is written to cache before it is cleaned
|
||||||
|
asm::dmb();
|
||||||
|
// TODO: Is the following necessary, considering that the SCU
|
||||||
|
// should take care of coherency of all (normal) memory?
|
||||||
|
//
|
||||||
|
// clean cache lines containing START_ADDR_CORE1 and
|
||||||
|
// INITIAL_SP_CORE1
|
||||||
|
dccmvac(&START_ADDR_CORE1 as *const _ as u32);
|
||||||
|
dccmvac(&INITIAL_SP_CORE1 as *const _ as u32);
|
||||||
|
|
||||||
|
// clean cache lines containing mailboxes
|
||||||
|
dccmvac(&MAILBOX_FROM_CORE0 as *const _ as u32);
|
||||||
|
dccmvac(&MAILBOX_FROM_CORE1 as *const _ as u32);
|
||||||
|
|
||||||
|
// restart core 1
|
||||||
|
slcr::RegisterBlock::unlocked(|slcr| {
|
||||||
|
slcr.a9_cpu_rst_ctrl.modify(|_, w| {
|
||||||
|
w.a9_rst1(false)
|
||||||
|
});
|
||||||
|
slcr.a9_cpu_rst_ctrl.modify(|_, w| {
|
||||||
|
w.a9_clkstop1(false)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main_core1() -> ! {
|
||||||
|
let mut data: [u32; 2] = [42, 42];
|
||||||
|
println!("Core 1 SP: 0x{:X}", SP.read());
|
||||||
|
loop {
|
||||||
|
// effectively perform something similar to `println!("from
|
||||||
|
// core 1");` by passing a message to core 0 and having core 0
|
||||||
|
// output it via the println! macro
|
||||||
|
unsafe {
|
||||||
|
println!("sending from core 1");
|
||||||
|
MAILBOX_FROM_CORE1.send(&data as *const _ as usize);
|
||||||
|
while !MAILBOX_FROM_CORE1.acknowledged() {
|
||||||
|
println!("core 1 waiting for acknowledgement from core 0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// change data to make it more interesting
|
||||||
|
data[1] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main_core1_program2() -> ! {
|
||||||
|
let mut data: [u32; 2] = [4200, 4200];
|
||||||
|
println!("Core 1 SP: 0x{:X}", SP.read());
|
||||||
|
loop {
|
||||||
|
unsafe {
|
||||||
|
MAILBOX_FROM_CORE1.send(&data as *const _ as usize);
|
||||||
|
while !MAILBOX_FROM_CORE1.acknowledged() {}
|
||||||
|
}
|
||||||
|
// change data to make it more interesting
|
||||||
|
data[0] -= 1;
|
||||||
|
data[1] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reserve some memory as stack for core1
|
||||||
|
static mut STACK_CORE1: [u32; 256] = [0; 256];
|
||||||
|
|
||||||
const HWADDR: [u8; 6] = [0, 0x23, 0xde, 0xea, 0xbe, 0xef];
|
const HWADDR: [u8; 6] = [0, 0x23, 0xde, 0xea, 0xbe, 0xef];
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Main.");
|
println!("Main.");
|
||||||
|
println!("Core 0 SP: 0x{:X}", SP.read());
|
||||||
let clocks = clocks::CpuClocks::get();
|
let clocks = clocks::CpuClocks::get();
|
||||||
println!("Clocks: {:?}", clocks);
|
println!("Clocks: {:?}", clocks);
|
||||||
println!("CPU speeds: {}/{}/{}/{} MHz",
|
println!("CPU speeds: {}/{}/{}/{} MHz",
|
||||||
|
@ -92,6 +267,52 @@ fn main() {
|
||||||
println!("Eth on");
|
println!("Eth on");
|
||||||
eth.reset_phy();
|
eth.reset_phy();
|
||||||
|
|
||||||
|
// start executing main_core1() on core 1
|
||||||
|
unsafe {
|
||||||
|
run_on_core1(main_core1, &mut STACK_CORE1[..]);
|
||||||
|
}
|
||||||
|
println!("Started main_core1() on core 1");
|
||||||
|
for _ in 0..5 {
|
||||||
|
// wait for data
|
||||||
|
while unsafe { !MAILBOX_FROM_CORE1.available() } {}
|
||||||
|
// receive data
|
||||||
|
let data_ptr = unsafe { MAILBOX_FROM_CORE1.receive() };
|
||||||
|
println!(
|
||||||
|
"Received via mailbox from core 1: data {} and {} at address 0x{:X}",
|
||||||
|
unsafe { (*(data_ptr as *const [u32; 2]))[0] },
|
||||||
|
unsafe { (*(data_ptr as *const [u32; 2]))[1] },
|
||||||
|
data_ptr
|
||||||
|
);
|
||||||
|
unsafe {
|
||||||
|
MAILBOX_FROM_CORE1.acknowledge(data_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop_core1();
|
||||||
|
println!("Stopped core 1.");
|
||||||
|
|
||||||
|
// start executing main_core1_program2() on core 1
|
||||||
|
unsafe {
|
||||||
|
run_on_core1(main_core1_program2, &mut STACK_CORE1[..]);
|
||||||
|
}
|
||||||
|
println!("Started main_core1_program2() on core 1");
|
||||||
|
for _ in 0..5 {
|
||||||
|
// wait for data
|
||||||
|
while unsafe { !MAILBOX_FROM_CORE1.available() } {}
|
||||||
|
// receive data
|
||||||
|
let data_ptr = unsafe { MAILBOX_FROM_CORE1.receive() };
|
||||||
|
println!(
|
||||||
|
"Received via mailbox from core 1: data {} and {} at address 0x{:X}",
|
||||||
|
unsafe { (*(data_ptr as *const [u32; 2]))[0] },
|
||||||
|
unsafe { (*(data_ptr as *const [u32; 2]))[1] },
|
||||||
|
data_ptr
|
||||||
|
);
|
||||||
|
unsafe {
|
||||||
|
MAILBOX_FROM_CORE1.acknowledge(data_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop_core1();
|
||||||
|
println!("Stopped core 1.");
|
||||||
|
|
||||||
const RX_LEN: usize = 1;
|
const RX_LEN: usize = 1;
|
||||||
let mut rx_descs: [eth::rx::DescEntry; RX_LEN] = unsafe { uninitialized() };
|
let mut rx_descs: [eth::rx::DescEntry; RX_LEN] = unsafe { uninitialized() };
|
||||||
let mut rx_buffers = [[0u8; eth::MTU]; RX_LEN];
|
let mut rx_buffers = [[0u8; eth::MTU]; RX_LEN];
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
///! Register definitions for Application Processing Unit (mpcore)
|
||||||
|
|
||||||
|
use volatile_register::{RO, RW, WO};
|
||||||
|
use crate::{register, register_at, register_bit};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct RegisterBlock {
|
||||||
|
pub scu_control: ScuControl,
|
||||||
|
pub scu_config: RO<u32>,
|
||||||
|
pub scu_cpu_power: RW<u32>,
|
||||||
|
pub scu_invalidate: WO<u32>,
|
||||||
|
reserved0: [u32; 12],
|
||||||
|
pub filter_start: RW<u32>,
|
||||||
|
pub filter_end: RW<u32>,
|
||||||
|
reserved1: [u32; 2],
|
||||||
|
pub scu_access_control: RW<u32>,
|
||||||
|
pub scu_non_secure_access_control: RW<u32>,
|
||||||
|
// there is plenty more (unimplemented)
|
||||||
|
}
|
||||||
|
register_at!(RegisterBlock, 0xF8F00000, new);
|
||||||
|
|
||||||
|
register!(scu_control, ScuControl, RW, u32);
|
||||||
|
register_bit!(scu_control, ic_standby_enable, 6);
|
||||||
|
register_bit!(scu_control, scu_standby_enable, 5);
|
||||||
|
register_bit!(scu_control, force_to_port0_enable, 4);
|
||||||
|
register_bit!(scu_control, scu_speculative_linefill_enable, 3);
|
||||||
|
register_bit!(scu_control, scu_rams_parity_enable, 2);
|
||||||
|
register_bit!(scu_control, address_filtering_enable, 1);
|
||||||
|
register_bit!(scu_control, enable, 0);
|
|
@ -0,0 +1,71 @@
|
||||||
|
/// Mutex for SMP-safe locking
|
||||||
|
|
||||||
|
use crate::cortex_a9::asm;
|
||||||
|
|
||||||
|
pub struct Mutex {
|
||||||
|
state: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const UNLOCKED_MUTEX: u32 = 0;
|
||||||
|
const LOCKED_MUTEX: u32 = 1;
|
||||||
|
|
||||||
|
impl Mutex {
|
||||||
|
pub const fn new_unlocked() -> Mutex {
|
||||||
|
Mutex { state: UNLOCKED_MUTEX }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn new_locked() -> Mutex {
|
||||||
|
Mutex { state: LOCKED_MUTEX }
|
||||||
|
}
|
||||||
|
|
||||||
|
// inlining causes problems with the labels
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn acquire(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
// code adapted from an example by ARM at
|
||||||
|
// http://infocenter.arm.com (Home > ARM Synchronization
|
||||||
|
// Primitives > Practical uses > Implementing a mutex)
|
||||||
|
asm!("
|
||||||
|
mutex_acquire_label1:
|
||||||
|
ldrex r2, [$0];
|
||||||
|
cmp r2, $1;
|
||||||
|
beq mutex_acquire_label2;
|
||||||
|
strexne r2, $1, [$0];
|
||||||
|
cmpne r2, 1;
|
||||||
|
beq mutex_acquire_label1;
|
||||||
|
dmb;
|
||||||
|
b mutex_acquire_label3;
|
||||||
|
mutex_acquire_label2:
|
||||||
|
wfe;
|
||||||
|
b mutex_acquire_label1;
|
||||||
|
mutex_acquire_label3: ;
|
||||||
|
"
|
||||||
|
::
|
||||||
|
// inputs
|
||||||
|
"r" (&mut self.state as *mut _ as u32), "r" (LOCKED_MUTEX)
|
||||||
|
:
|
||||||
|
// clobbers
|
||||||
|
"r2"
|
||||||
|
:
|
||||||
|
"volatile"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
asm!("
|
||||||
|
dmb;
|
||||||
|
str $1, [$0];
|
||||||
|
dsb;
|
||||||
|
sev;
|
||||||
|
"
|
||||||
|
::
|
||||||
|
// inputs
|
||||||
|
"r" (&mut self.state as *mut _ as u32), "r" (UNLOCKED_MUTEX)
|
||||||
|
::
|
||||||
|
"volatile"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,7 +90,7 @@ pub struct RegisterBlock {
|
||||||
pub ocm_rst_ctrl: RW<u32>,
|
pub ocm_rst_ctrl: RW<u32>,
|
||||||
reserved4: [u32; 1],
|
reserved4: [u32; 1],
|
||||||
pub fpga_rst_ctrl: RW<u32>,
|
pub fpga_rst_ctrl: RW<u32>,
|
||||||
pub a9_cpu_rst_ctrl: RW<u32>,
|
pub a9_cpu_rst_ctrl: A9CpuRstCtrl,
|
||||||
reserved5: [u32; 1],
|
reserved5: [u32; 1],
|
||||||
pub rs_awdt_ctrl: RW<u32>,
|
pub rs_awdt_ctrl: RW<u32>,
|
||||||
reserved6: [u32; 2],
|
reserved6: [u32; 2],
|
||||||
|
@ -365,6 +365,13 @@ impl UartRstCtrl {
|
||||||
register!(pss_rst_ctrl, PssRstCtrl, RW, u32);
|
register!(pss_rst_ctrl, PssRstCtrl, RW, u32);
|
||||||
register_bit!(pss_rst_ctrl, soft_rst, 1);
|
register_bit!(pss_rst_ctrl, soft_rst, 1);
|
||||||
|
|
||||||
|
register!(a9_cpu_rst_ctrl, A9CpuRstCtrl, RW, u32);
|
||||||
|
register_bit!(a9_cpu_rst_ctrl, peri_rst, 8);
|
||||||
|
register_bit!(a9_cpu_rst_ctrl, a9_clkstop1, 5);
|
||||||
|
register_bit!(a9_cpu_rst_ctrl, a9_clkstop0, 4);
|
||||||
|
register_bit!(a9_cpu_rst_ctrl, a9_rst1, 1);
|
||||||
|
register_bit!(a9_cpu_rst_ctrl, a9_rst0, 0);
|
||||||
|
|
||||||
/// Used for MioPin*.io_type
|
/// Used for MioPin*.io_type
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum IoBufferType {
|
pub enum IoBufferType {
|
||||||
|
|
34
src/stdio.rs
34
src/stdio.rs
|
@ -1,11 +1,12 @@
|
||||||
use crate::uart::Uart;
|
use crate::uart::Uart;
|
||||||
|
use crate::mutex::Mutex;
|
||||||
|
|
||||||
const UART_RATE: u32 = 115_200;
|
const UART_RATE: u32 = 115_200;
|
||||||
static mut UART: Option<Uart> = None;
|
static mut UART: Option<Uart> = None;
|
||||||
|
static mut UART_MUTEX: Mutex = Mutex::new_unlocked();
|
||||||
|
|
||||||
// TODO: locking for SMP
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn get_uart() -> &'static mut Uart {
|
fn get_uart() -> &'static mut Uart {
|
||||||
unsafe {
|
unsafe {
|
||||||
match &mut UART {
|
match &mut UART {
|
||||||
None => {
|
None => {
|
||||||
|
@ -18,22 +19,35 @@ pub fn get_uart() -> &'static mut Uart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// call f(UART) with UART locked via UART_MUTEX
|
||||||
|
pub fn with_uart<F>(f: F) where F: Fn(&mut Uart) -> () {
|
||||||
|
unsafe {
|
||||||
|
UART_MUTEX.acquire();
|
||||||
|
}
|
||||||
|
f(get_uart());
|
||||||
|
unsafe {
|
||||||
|
UART_MUTEX.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! print {
|
macro_rules! print {
|
||||||
($($arg:tt)*) => ({
|
($($arg:tt)*) => ({
|
||||||
use core::fmt::Write;
|
crate::stdio::with_uart(|uart| {
|
||||||
let uart = crate::stdio::get_uart();
|
use core::fmt::Write;
|
||||||
let _ = write!(uart, $($arg)*);
|
let _ = write!(uart, $($arg)*);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! println {
|
macro_rules! println {
|
||||||
($($arg:tt)*) => ({
|
($($arg:tt)*) => ({
|
||||||
use core::fmt::Write;
|
crate::stdio::with_uart(|uart| {
|
||||||
let uart = crate::stdio::get_uart();
|
use core::fmt::Write;
|
||||||
let _ = write!(uart, $($arg)*);
|
let _ = write!(uart, $($arg)*);
|
||||||
let _ = write!(uart, "\r\n");
|
let _ = write!(uart, "\r\n");
|
||||||
while !uart.tx_fifo_empty() {}
|
while !uart.tx_fifo_empty() {}
|
||||||
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue