Compare commits

...

43 Commits

Author SHA1 Message Date
fa07bdb681 libcortex_a9/mmu: share ocm3.
This fixes issue #54.
2020-08-07 15:10:38 +08:00
4565a75766 experiments: add I2C bitbang EEPROM writing/reading example 2020-08-07 11:10:20 +08:00
16b2df91ca i2c: fix GPIO register mapping, I2C control & EEPROM write operations 2020-08-07 11:10:18 +08:00
f7d3135ec7 i2c: implement EEPROM operations; add CountDown waiting indication 2020-08-05 20:10:30 +08:00
c60230af25 i2c: implement basic i2c bitbanging 2020-08-05 17:35:33 +08:00
e8ba73a8c7 Updated build instruction. 2020-08-05 17:08:24 +08:00
3958953ceb libcortex_a9/sync_channel: added drop_elements function. 2020-08-05 15:29:28 +08:00
a36a82d86d reduce ethernet verbosity 2020-08-04 22:15:01 +08:00
25c6d5eeaa Changes usage of sev/wfe to spinlock functions. 2020-08-04 13:54:19 +08:00
9e97102e12 libcortex_a9: implemented semaphore. 2020-08-04 13:34:08 +08:00
b65606f2d0 libcortex_a9/sync_channel: added reset. 2020-08-03 15:50:31 +08:00
ee4089c52e updated cargoSha256 2020-08-03 14:59:49 +08:00
36c3fbdacd experiments: fixed linker script. 2020-08-03 14:48:44 +08:00
8328ffc66b libsupport_zynq/ram: allow single allocator. 2020-08-03 14:48:44 +08:00
84041a3154 libsupport_zynq/ram: use core0 allocator by default. 2020-08-03 14:48:44 +08:00
5850401d72 libsupport_zynq/ram: split allocators for two cores. 2020-08-03 14:48:44 +08:00
ccce37dffd linked_list_allocator: upgraded to 0.8.4
So we get the `used` function to check heap usage.
2020-08-03 14:48:17 +08:00
3bbd1513fb build.sh: specify build experiments
Otherwise we cannot turn off the default feature for libsupport_zynq.
https://github.com/rust-lang/cargo/issues/8366#issuecomment-644995218
2020-08-03 14:09:36 +08:00
7d38c53c18 libsupport_zynq/abort: moved core1 restart code to user code. 2020-08-03 14:09:36 +08:00
02a2c4d1e3 experiments: updated example. 2020-08-03 12:35:17 +08:00
12669124a4 libcortex_a9/mutex: added interrupt critical section mask. 2020-08-03 12:35:17 +08:00
8f0a6bd5ea libsupport_zynq/abort: restart core1 main on core1 IRQ#0. 2020-08-03 12:35:17 +08:00
c1f61b5673 libcortex_a9/boot: enable IRQ on reset. 2020-08-03 12:35:17 +08:00
2927c43309 libboard_zynq/gic: refactored and added SGI functions. 2020-08-03 12:35:17 +08:00
187801c4a7 gic: start implementation 2020-08-03 12:35:17 +08:00
91ece367f2 libboard_zynq/mpcore: added generated register definitions 2020-08-03 12:35:17 +08:00
1f05e6977e eth::phy: replace ExtendedStatus with PSSR 2020-07-29 21:49:18 +02:00
e408a8b22d eth::phy::extended_status: fix cap_1000base_x_full() bit position 2020-07-29 21:29:28 +02:00
27effb6257 eth::phy: s/Marvel/Marvell/ 2020-07-29 20:08:38 +02:00
de5f605d60 eth: refactor peripheral instance into type parameter, improve clock setup 2020-07-29 19:45:01 +02:00
ad47521e4b libsupport_zynq/boot: fixed core1 disable. 2020-07-28 12:36:23 +08:00
c50e72f91e experiments: use OCM instead of OCM3 (#54) 2020-07-28 12:36:23 +08:00
b099c56569 libcortex_a9/sync_channel: new version compiled. 2020-07-28 12:36:16 +08:00
ef4fb598fb ddr: improve dci divisors calculation 2020-07-28 00:43:33 +02:00
0aa75d3544 experiments: fix timer.get_us() usage 2020-07-22 23:47:57 +02:00
f36b1a610e timer::global: wrap us in Microseconds, impl embedded_hal blocking delay traits 2020-07-22 23:41:15 +02:00
7f45d10af3 timer::global::CountDown: fix delaying from "up to" to "at least" the timespan 2020-07-22 22:43:10 +02:00
855d94c48e dmac: remove unused module 2020-07-20 19:42:32 +02:00
84f1380f48 libasync: assert that callback consumes data in smoltcp recv 2020-07-19 16:14:29 +08:00
f8785c3f07 fix some compilation warnings 2020-07-19 15:39:08 +08:00
7b78bc0494 libasync: new stream.recv API
M-Labs/artiq-zynq#40 (comment)
2020-07-19 15:34:32 +08:00
ef88a1313a shell.nix: remove gcc 2020-07-19 15:34:18 +08:00
484e385160 eth: implement DeviceCapabilities.max_burst_size
this is a hint that /could/ boost TCP performance.
2020-07-16 00:17:13 +02:00
40 changed files with 1661 additions and 945 deletions

6
Cargo.lock generated
View File

@ -98,13 +98,13 @@ dependencies = [
"libboard_zynq 0.0.0",
"libcortex_a9 0.0.0",
"libregister 0.0.0",
"linked_list_allocator 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
"linked_list_allocator 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"r0 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "linked_list_allocator"
version = "0.8.3"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -170,7 +170,7 @@ dependencies = [
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum compiler_builtins 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "38f18416546abfbf8d801c555a0e99524453e7214f9cc9107ad49de3d5948ccc"
"checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b"
"checksum linked_list_allocator 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d6b60501dd4c850950bb43f970d544f6ce04e0ca021da2db2538fbe9d923f19e"
"checksum linked_list_allocator 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e70e46c13c0e8374c26cec5752e3347ca1087d9711de8f45aa513a7700efd73d"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6"
"checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc"

View File

@ -1 +1 @@
nix-shell --command "cargo xbuild --release" && scp -P 2204 -C target/armv7-none-eabihf/release/zc706-experiments $1@nixbld.m-labs.hk:/home/$1/zc706/zc706.elf
nix-shell --command "cargo xbuild --release -p experiments" && scp -P 2204 -C target/armv7-none-eabihf/release/zc706-experiments $1@nixbld.m-labs.hk:/home/$1/zc706/zc706.elf

View File

@ -49,7 +49,7 @@ let
name = "${crate}";
src = ./.;
crateSubdir = crate;
cargoSha256 = "0xlynsr94dyv0g41qwk5490w3wnzd5g70msaih6mcbgr3v4s2q34";
cargoSha256 = "1c1qpg9by8bg93yhgllb5xs155g27qmh99pbrb681wazm8k7nwim";
cargoFeatures = features;
doCheck = false;
dontFixup = true;

View File

@ -16,5 +16,5 @@ embedded-hal = "0.2"
libregister = { path = "../libregister" }
libcortex_a9 = { path = "../libcortex_a9" }
libboard_zynq = { path = "../libboard_zynq" }
libsupport_zynq = { path = "../libsupport_zynq" }
libsupport_zynq = { path = "../libsupport_zynq", default-features = false, features = ["panic_handler"]}
libasync = { path = "../libasync" }

View File

@ -44,6 +44,10 @@ SECTIONS
__stack0_end = .;
. = ORIGIN(OCM3) + LENGTH(OCM3) - 8;
__stack0_start = .;
/* unused heap0 to prevent the linker from complaining*/
__heap0_start = .;
__heap0_end = .;
} > OCM3
/DISCARD/ :

View File

@ -1,10 +1,11 @@
#![no_std]
#![no_main]
#![feature(const_in_array_repeat_expressions)]
#![feature(naked_functions)]
extern crate alloc;
use alloc::{borrow::ToOwned, collections::BTreeMap, format};
use core::task::Poll;
use alloc::collections::BTreeMap;
use libasync::{
delay,
smoltcp::{Sockets, TcpStream},
@ -14,34 +15,80 @@ use libboard_zynq::{
self as zynq,
clocks::source::{ArmPll, ClockSource, IoPll},
clocks::Clocks,
print, println,
sdio::sd_card::SdCard,
print, println, stdio,
mpcore,
gic,
smoltcp::{
self,
iface::{EthernetInterfaceBuilder, NeighborCache, Routes},
time::Instant,
wire::{EthernetAddress, IpAddress, IpCidr},
},
time::Milliseconds,
};
#[cfg(feature = "target_zc706")]
use libboard_zynq::ps7_init;
use libcortex_a9::{
mutex::Mutex,
sync_channel::{self, sync_channel},
sync_channel::{Sender, Receiver},
sync_channel,
regs::{MPIDR, SP},
spin_lock_yield, notify_spin_lock,
asm
};
use libregister::RegisterR;
use libregister::{RegisterR, RegisterW};
use libsupport_zynq::{
boot, ram,
};
use log::{info, warn};
use core::sync::atomic::{AtomicBool, Ordering};
const HWADDR: [u8; 6] = [0, 0x23, 0xde, 0xea, 0xbe, 0xef];
static mut CORE1_REQ: (Sender<usize>, Receiver<usize>) = sync_channel!(usize, 10);
static mut CORE1_RES: (Sender<usize>, Receiver<usize>) = sync_channel!(usize, 10);
extern "C" {
static mut __stack1_start: u32;
}
static CORE1_RESTART: AtomicBool = AtomicBool::new(false);
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn IRQ() {
if MPIDR.read().cpu_id() == 1{
let mpcore = mpcore::RegisterBlock::new();
let mut gic = gic::InterruptController::new(mpcore);
let id = gic.get_interrupt_id();
if id.0 == 0 {
gic.end_interrupt(id);
asm::exit_irq();
SP.write(&mut __stack1_start as *mut _ as u32);
asm::enable_irq();
CORE1_RESTART.store(false, Ordering::Relaxed);
notify_spin_lock();
main_core1();
}
}
stdio::drop_uart();
println!("IRQ");
loop {}
}
pub fn restart_core1() {
let mut interrupt_controller = gic::InterruptController::new(mpcore::RegisterBlock::new());
CORE1_RESTART.store(true, Ordering::Relaxed);
interrupt_controller.send_sgi(gic::InterruptId(0), gic::CPUCore::Core1.into());
while CORE1_RESTART.load(Ordering::Relaxed) {
spin_lock_yield();
}
}
#[no_mangle]
pub fn main_core0() {
// zynq::clocks::CpuClocks::enable_io(1_250_000_000);
println!("\nzc706 main");
let mut interrupt_controller = gic::InterruptController::new(mpcore::RegisterBlock::new());
interrupt_controller.enable_interrupts();
// ps7_init::apply();
libboard_zynq::stdio::drop_uart();
@ -84,25 +131,6 @@ pub fn main_core0() {
clocks.cpu_1x()
);
let sd = libboard_zynq::sdio::SDIO::sdio0(true);
// only test SD card if it is inserted
if sd.is_card_inserted() {
let result = SdCard::from_sdio(sd);
match &result {
Ok(_) => info!("OK!"),
Err(a) => info!("{}", a),
};
const SIZE: usize = 512 * 2 + 1;
let mut sd_card = result.unwrap();
if false {
let buffer: [u8; SIZE] = [5; SIZE];
sd_card.write_block(0x0, 2, &buffer).unwrap();
}
let mut buffer: [u8; SIZE] = [0; SIZE];
sd_card.read_block(0 /*0x1*/, 2, &mut buffer[1..]).unwrap();
info!("buffer = {:?}", &buffer[..]);
}
let mut flash = zynq::flash::Flash::new(200_000_000).linear_addressing_mode();
let flash_ram: &[u8] = unsafe { core::slice::from_raw_parts(flash.ptr(), flash.size()) };
for i in 0..=1 {
@ -112,7 +140,7 @@ pub fn main_core0() {
}
println!("");
}
let mut flash = flash.stop();
let _flash = flash.stop();
let timer = libboard_zynq::timer::GlobalTimer::start();
@ -160,20 +188,51 @@ pub fn main_core0() {
flash = flash_io.stop();
}
let core1 = boot::Core1::start(false);
boot::Core1::start(false);
let (mut core1_req, rx) = sync_channel(10);
*CORE1_REQ.lock() = Some(rx);
let (tx, mut core1_res) = sync_channel(10);
*CORE1_RES.lock() = Some(tx);
let core1_req = unsafe { &mut CORE1_REQ.0 };
let core1_res = unsafe { &mut CORE1_RES.1 };
task::block_on(async {
for i in 0..10 {
restart_core1();
core1_req.async_send(i).await;
let j = core1_res.async_recv().await;
println!("{} -> {}", i, j);
}
});
core1.disable();
unsafe {
core1_req.drop_elements();
}
// Test I2C
let mut i2c = zynq::i2c::I2C::i2c();
i2c.init();
println!("I2C bit-banging enabled");
let mut eeprom = zynq::i2c::eeprom::EEPROM::new(&mut i2c, 16);
// Write to 0x00 and 0x08
let eeprom_buffer: [u8; 22] = [
0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb,
0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01,
];
eeprom.write(0x00, &eeprom_buffer[0..6]);
eeprom.write(0x08, &eeprom_buffer[6..22]);
println!("Data written to EEPROM");
let mut eeprom_buffer = [0u8; 24];
// Read from 0x00
eeprom.read(0x00, &mut eeprom_buffer);
print!("Data read from EEPROM @ 0x00: (hex) ");
for i in 0..6 {
print!("{:02x} ", eeprom_buffer[i]);
}
println!("");
// Read from 0x08
eeprom.read(0x08, &mut eeprom_buffer);
print!("Data read from EEPROM @ 0x08: (hex) ");
for i in 0..16 {
print!("{:02x} ", eeprom_buffer[i]);
}
println!("");
let eth = zynq::eth::Eth::default(HWADDR.clone());
println!("Eth on");
@ -198,44 +257,9 @@ pub fn main_core0() {
.neighbor_cache(neighbor_cache)
.finalize();
#[cfg(feature = "target_zc706")]
ps7_init::report_differences();
Sockets::init(32);
/// `chargen`
const TCP_PORT: u16 = 19;
async fn handle_connection(stream: TcpStream) -> smoltcp::Result<()> {
stream.send("Enter your name: ".bytes()).await?;
let name = stream
.recv(|buf| {
for (i, b) in buf.iter().enumerate() {
if *b == '\n' as u8 {
return match core::str::from_utf8(&buf[0..i]) {
Ok(name) => Poll::Ready((i + 1, Some(name.to_owned()))),
Err(_) => Poll::Ready((i + 1, None)),
};
}
}
if buf.len() > 100 {
// Too much input, consume all
Poll::Ready((buf.len(), None))
} else {
Poll::Pending
}
})
.await?;
match name {
Some(name) => stream.send(format!("Hello {}!\n", name).bytes()).await?,
None => {
stream
.send("I had trouble reading your name.\n".bytes())
.await?
}
}
let _ = stream.close().await;
Ok(())
}
const TCP_PORT: u16 = 19;
// (rx, tx)
let stats = alloc::rc::Rc::new(core::cell::RefCell::new((0, 0)));
let stats_tx = stats.clone();
@ -264,7 +288,7 @@ pub fn main_core0() {
let stats_rx = stats_rx.clone();
task::spawn(async move {
loop {
match stream.recv(|buf| Poll::Ready((buf.len(), buf.len()))).await {
match stream.recv(|buf| (buf.len(), buf.len())).await {
Ok(len) => stats_rx.borrow_mut().0 += len,
Err(e) => {
warn!("rx: {:?}", e);
@ -281,7 +305,7 @@ pub fn main_core0() {
loop {
delay(&mut countdown, Milliseconds(1000)).await;
let timestamp = timer.get_us();
let timestamp = timer.get_us().0;
let seconds = timestamp / 1_000_000;
let micros = timestamp % 1_000_000;
let (rx, tx) = {
@ -299,27 +323,18 @@ pub fn main_core0() {
})
}
static CORE1_REQ: Mutex<Option<sync_channel::Receiver<usize>>> = Mutex::new(None);
static CORE1_RES: Mutex<Option<sync_channel::Sender<usize>>> = Mutex::new(None);
static DONE: Mutex<bool> = Mutex::new(false);
#[no_mangle]
pub fn main_core1() {
println!("Hello from core1!");
let mut req = None;
while req.is_none() {
req = CORE1_REQ.lock().take();
}
let req = req.unwrap();
let mut res = None;
while res.is_none() {
res = CORE1_RES.lock().take();
}
let mut res = res.unwrap();
let mut interrupt_controller = gic::InterruptController::new(mpcore::RegisterBlock::new());
interrupt_controller.enable_interrupts();
let req = unsafe { &mut CORE1_REQ.1 };
let res = unsafe { &mut CORE1_RES.0 };
for i in req {
res.send(*i * *i);
res.send(i * i);
}
println!("core1 done!");

View File

@ -103,21 +103,19 @@ impl TcpStream {
/// Probe the receive buffer
///
/// Instead of handing you the data on the heap all at once,
/// smoltcp's read interface is wrapped so that your callback can
/// just return `Poll::Pending` if there is not enough data
/// yet. Likewise, return the amount of bytes consumed from the
/// buffer in the `Poll::Ready` result.
/// Your callback will only be called when there is some data available,
/// and it must consume at least one byte. It returns a tuple with the
/// number of bytes it consumed, and a user-defined return value of type R.
pub async fn recv<F, R>(&self, f: F) -> Result<R>
where
F: Fn(&[u8]) -> Poll<(usize, R)>,
F: Fn(&[u8]) -> (usize, R),
{
struct Recv<'a, F: FnOnce(&[u8]) -> Poll<(usize, R)>, R> {
struct Recv<'a, F: FnOnce(&[u8]) -> (usize, R), R> {
stream: &'a TcpStream,
f: F,
}
impl<'a, F: Fn(&[u8]) -> Poll<(usize, R)>, R> Future for Recv<'a, F, R> {
impl<'a, F: Fn(&[u8]) -> (usize, R), R> Future for Recv<'a, F, R> {
type Output = Result<R>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
@ -128,13 +126,9 @@ impl TcpStream {
socket.recv(|buf| {
if buf.len() > 0 {
match (self.f)(buf) {
Poll::Ready((amount, result)) =>
(amount, Poll::Ready(Ok(result))),
Poll::Pending =>
// 0 bytes consumed
(0, Poll::Pending),
}
let (amount, result) = (self.f)(buf);
assert!(amount > 0);
(amount, Poll::Ready(Ok(result)))
} else {
(0, Poll::Pending)
}

View File

@ -56,14 +56,35 @@ impl DdrRam {
clocks
}
fn calculate_dci_divisors(clocks: &Clocks) -> (u8, u8) {
let target = (DCI_FREQ - 1 + clocks.ddr) / DCI_FREQ;
let mut best = None;
let mut best_error = 0;
for divisor0 in 1..63 {
for divisor1 in 1..63 {
let current = (divisor0 as u32) * (divisor1 as u32);
let error = if current > target {
current - target
} else {
target - current
};
if best.is_none() || best_error > error {
best = Some((divisor0, divisor1));
best_error = error;
}
}
}
best.unwrap()
}
/// Zynq-7000 AP SoC Technical Reference Manual:
/// 10.6.2 DDR IOB Impedance Calibration
fn calibrate_iob_impedance(clocks: &Clocks) {
let divisor0 = ((DCI_FREQ - 1 + clocks.ddr) / DCI_FREQ)
.max(1).min(63) as u8;
let divisor1 = ((DCI_FREQ - 1 + clocks.ddr) / DCI_FREQ / u32::from(divisor0))
.max(1).min(63) as u8;
debug!("DDR DCI clock: {} Hz", clocks.ddr / u32::from(divisor0) / u32::from(divisor1));
let (divisor0, divisor1) = Self::calculate_dci_divisors(clocks);
debug!("DDR DCI clock: {} Hz (divisors={}*{})",
clocks.ddr / u32::from(divisor0) / u32::from(divisor1),
divisor0, divisor1);
slcr::RegisterBlock::unlocked(|slcr| {
// Step 1.

View File

@ -1,4 +1,3 @@
use super::clocks::Clocks;
use super::time::Milliseconds;
use crate::slcr;
use embedded_hal::timer::CountDown;
@ -11,7 +10,7 @@ mod regs;
pub struct DevC {
regs: &'static mut regs::RegisterBlock,
enabled: bool,
count_down: super::timer::global::CountDown,
count_down: super::timer::global::CountDown<Milliseconds>,
timeout_ms: Milliseconds,
}

View File

@ -1,3 +0,0 @@
//! PrimeCell DMA Controller (PL330)
mod regs;

View File

@ -1,386 +0,0 @@
use libregister::{
register, register_at,
register_bit, register_bits, register_bits_typed,
};
#[allow(unused)]
#[repr(C)]
pub struct RegisterBlock {
pub ds: Ds,
pub dpc: DPc,
pub inten: Inten,
pub es: Es,
pub intstatus: IntStatus,
pub intclr: IntClr,
pub fsm: Fsm,
pub fsc: Fsc,
pub ftm: Ftm,
pub ftc: [Ftc; 8],
pub cs0: Cs,
pub cpc0: Cpc,
pub cs1: Cs,
pub cpc1: Cpc,
pub cs2: Cs,
pub cpc2: Cpc,
pub cs3: Cs,
pub cpc3: Cpc,
pub cs4: Cs,
pub cpc4: Cpc,
pub cs5: Cs,
pub cpc5: Cpc,
pub cs6: Cs,
pub cpc6: Cpc,
pub cs7: Cs,
pub cpc7: Cpc,
pub sa0: Sa,
pub da0: Da,
pub cc0: Cc,
pub lc0_0: Lc,
pub lc0_1: Lc,
pub sa1: Sa,
pub da1: Da,
pub cc1: Cc,
pub lc1_0: Lc,
pub lc1_1: Lc,
pub sa2: Sa,
pub da2: Da,
pub cc2: Cc,
pub lc2_0: Lc,
pub lc2_1: Lc,
pub sa3: Sa,
pub da3: Da,
pub cc3: Cc,
pub lc3_0: Lc,
pub lc3_1: Lc,
pub sa4: Sa,
pub da4: Da,
pub cc4: Cc,
pub lc4_0: Lc,
pub lc4_1: Lc,
pub sa5: Sa,
pub da5: Da,
pub cc5: Cc,
pub lc5_0: Lc,
pub lc5_1: Lc,
pub sa6: Sa,
pub da6: Da,
pub cc6: Cc,
pub lc6_0: Lc,
pub lc6_1: Lc,
pub sa7: Sa,
pub da7: Da,
pub cc7: Cc,
pub lc7_0: Lc,
pub lc7_1: Lc,
pub dbgstatus: DbgStatus,
pub dbgcmd: DbgCmd,
pub dbginst0: DbgInst0,
pub dbginst1: DbgInst1,
pub cr0: Cr0,
pub cr1: Cr1,
pub cr2: Cr2,
pub cr3: Cr3,
pub cr4: Cr4,
pub crdn: Crdn,
pub wd: Wd,
pub periph_id_0: PeriphId0,
pub periph_id_1: PeriphId1,
pub periph_id_2: PeriphId2,
pub periph_id_3: PeriphId3,
pub pcell_id_0: PCellId0,
pub pcell_id_1: PCellId1,
pub pcell_id_2: PCellId2,
pub pcell_id_3: PCellId3,
}
register_at!(RegisterBlock, 0xF8004000, dmac0_ns);
register_at!(RegisterBlock, 0xF8003000, dmac0_s);
impl RegisterBlock {
pub fn channel_regs(&mut self, channel: usize) -> Option<ChannelRegisters>
{
match channel {
0 => Some(ChannelRegisters {
ftc: &mut self.ftc[0],
cs: &mut self.cs0,
cpc: &mut self.cpc0,
sa: &mut self.sa0,
da: &mut self.da0,
cc: &mut self.cc0,
lc: [&mut self.lc0_0, &mut self.lc0_1],
}),
1 => Some(ChannelRegisters {
ftc: &mut self.ftc[1],
cs: &mut self.cs1,
cpc: &mut self.cpc1,
sa: &mut self.sa1,
da: &mut self.da1,
cc: &mut self.cc1,
lc: [&mut self.lc1_0, &mut self.lc1_1],
}),
2 => Some(ChannelRegisters {
ftc: &mut self.ftc[2],
cs: &mut self.cs2,
cpc: &mut self.cpc2,
sa: &mut self.sa2,
da: &mut self.da2,
cc: &mut self.cc2,
lc: [&mut self.lc2_0, &mut self.lc2_1],
}),
3 => Some(ChannelRegisters {
ftc: &mut self.ftc[3],
cs: &mut self.cs3,
cpc: &mut self.cpc3,
sa: &mut self.sa3,
da: &mut self.da3,
cc: &mut self.cc3,
lc: [&mut self.lc3_0, &mut self.lc3_1],
}),
4 => Some(ChannelRegisters {
ftc: &mut self.ftc[4],
cs: &mut self.cs4,
cpc: &mut self.cpc4,
sa: &mut self.sa4,
da: &mut self.da4,
cc: &mut self.cc4,
lc: [&mut self.lc4_0, &mut self.lc4_1],
}),
5 => Some(ChannelRegisters {
ftc: &mut self.ftc[5],
cs: &mut self.cs5,
cpc: &mut self.cpc5,
sa: &mut self.sa5,
da: &mut self.da5,
cc: &mut self.cc5,
lc: [&mut self.lc5_0, &mut self.lc5_1],
}),
6 => Some(ChannelRegisters {
ftc: &mut self.ftc[6],
cs: &mut self.cs6,
cpc: &mut self.cpc6,
sa: &mut self.sa6,
da: &mut self.da6,
cc: &mut self.cc6,
lc: [&mut self.lc6_0, &mut self.lc6_1],
}),
7 => Some(ChannelRegisters {
ftc: &mut self.ftc[7],
cs: &mut self.cs7,
cpc: &mut self.cpc7,
sa: &mut self.sa7,
da: &mut self.da7,
cc: &mut self.cc7,
lc: [&mut self.lc7_0, &mut self.lc7_1],
}),
_ => None,
}
}
}
pub struct ChannelRegisters<'a> {
ftc: &'a mut Ftc,
cs: &'a mut Cs,
cpc: &'a mut Cpc,
sa: &'a mut Sa,
da: &'a mut Da,
cc: &'a mut Cc,
lc: [&'a mut Lc; 2],
}
#[allow(unused)]
#[repr(u8)]
pub enum WakeUpEvent{
// @missing: there's a binary prefix ahead of this as per TRM 1173 Wakeup_event
Event0 = 0b0000,
Event1 = 0b0001,
Event2 = 0b0010,
Event3 = 0b0011,
Event4 = 0b0100,
Event5 = 0b0101,
Event6 = 0b0110,
Event7 = 0b0111,
Event8 = 0b1000,
Event9 = 0b1001,
Event10 = 0b1010,
Event11 = 0b1011,
Event12 = 0b1100,
Event13 = 0b1101,
Event14 = 0b1110,
Event15 = 0b1111,
}
#[allow(unused)]
#[repr(u8)]
pub enum DMAStatus{
Stopped = 0b0000,
Executing = 0b0001,
CacheMiss = 0b0010,
UpdatingPc = 0b0011,
WaitingForEvent = 0b0100,
Reserved0 = 0b0101,
Reserved1 = 0b0110,
Reserved2 = 0b0111,
Reserved3 = 0b1000,
Reserved4 = 0b1001,
Reserved5 = 0b1010,
Reserved6 = 0b1011,
Reserved7 = 0b1100,
Reserved8 = 0b1101,
Reserved9 = 0b1110,
Faulting = 0b1111,
}
register!(ds, Ds, RW, u32);
register_bit!(ds, dns, 9);
register_bits_typed!(ds, wakeup_event, u8, WakeUpEvent, 4, 8);
register_bits_typed!(ds, dma_status, u8, DMAStatus, 0, 3);
register!(dpc, DPc, RW, u32);
register_bits!(dpc, pc_mgr, u8, 0, 31);
register!(inten, Inten, RW, u32);
register_bits!(inten, event_irq_select, u8, 0, 31);
register!(es, Es, RW, u32);
register_bits!(es, dmasev_active, u8, 0, 31);
register!(intstatus, IntStatus, RW, u32);
register_bits!(intstatus, irq_status, u8, 0, 31);
register!(intclr, IntClr, RW, u32);
register_bits!(intstatus, irq_clr, u8, 0, 31);
register!(fsm, Fsm, RW, u32);
register_bit!(fsm, fs_mgr, 0);
register!(fsc, Fsc, RW, u32);
register_bits!(fsc, fault_status, u8, 0, 7);
register!(ftm, Ftm, RW, u32);
register_bit!(ftm, dbg_instr, 30);
register_bit!(ftm, instr_fetch_err, 16);
register_bit!(ftm, mgr_evnt_err, 5);
register_bit!(ftm, dmago_err, 4);
register_bit!(ftm, operand_invalid, 1);
register_bit!(ftm, undef_instr, 0);
register!(ftc, Ftc, RW, u32);
register_bit!(ftc, lockup_err, 31);
register_bit!(ftc, dbg_instr, 30);
register_bit!(ftc, data_read_err, 18);
register_bit!(ftc, data_write_err, 17);
register_bit!(ftc, instr_fetch_err, 16);
register_bit!(ftc, st_data_unavailable, 13);
register_bit!(ftc, mfifo_err, 12);
register_bit!(ftc, ch_rdwr_err, 7);
register_bit!(ftc, ch_periph_err, 6);
register_bit!(ftc, ch_evnt_err, 5);
register_bit!(ftc, operand_invalid, 1);
register_bit!(ftc, undef_instr, 0);
register!(cs, Cs, RW, u32);
register_bit!(cs, cns, 21);
register_bit!(cs, dmawfp_periph, 15);
register_bit!(cs, dmawfp_b_ns, 14);
register_bits!(cs, wakeup_num, u8, 4, 8);
register_bits!(cs, channel_status, u8, 0, 3);
register!(cpc, Cpc, RW, u32);
register_bits!(cpc, pc_chnl, u8, 0, 31);
register!(sa, Sa, RW, u32);
register_bits!(sa, src_addr, u8, 0, 31);
register!(da, Da, RW, u32);
register_bits!(da, dest_addr, u8, 0, 31);
register!(cc, Cc, RW, u32);
register_bits!(cc, endian_swap_size, u8, 28, 30);
register_bits!(cc, dst_cache_ctrl, u8, 25, 27);
register_bits!(cc, dst_prot_ctrl, u8, 22, 24);
register_bits!(cc, dst_burst_len, u8, 18, 21);
register_bits!(cc, dst_burst_size, u8, 15, 17);
register_bit!(cc, dst_inc, 14);
register_bits!(cc, src_cache_ctrl, u8, 11, 13);
register_bits!(cc, src_prot_ctrl, u8, 8, 10);
register_bits!(cc, src_burst_len, u8, 4, 7);
register_bits!(cc, src_burst_size, u8, 1, 3);
register_bit!(cc, src_inc, 0);
register!(lc0, Lc, RW, u32);
register_bits!(lc0, loop_counter_iteration, u8, 0, 7);
register!(dbgstatus, DbgStatus, RW, u32);
register_bit!(dbgstatus, dbgstatus, 0);
register!(dbgcmd, DbgCmd, RW, u32);
register_bits!(dbgcmd, dbgcmd, u8, 0, 1);
register!(dbginst0, DbgInst0, RW, u32);
register_bits!(dbginst0, instruction_byte1, u8, 24, 31);
register_bits!(dbginst0, instruction_byte0, u8, 16, 23);
register_bits!(dbginst0, channel_num, u8, 8, 10);
register_bit!(dbginst0, debug_thread, 0);
register!(dbginst1, DbgInst1, RW, u32);
register_bits!(dbginst1, instruction_byte5, u8, 24, 31);
register_bits!(dbginst1, instruction_byte4, u8, 16, 23);
register_bits!(dbginst1, instruction_byte3, u8, 8, 10);
register_bits!(dbginst1, instruction_byte2, u8, 0, 7);
register!(cr0, Cr0, RW, u32);
register_bits!(cr0, num_events, u8, 17, 21);
register_bits!(cr0, num_periph_req, u8, 12, 16);
register_bits!(cr0, num_chnls, u8, 4, 6);
register_bit!(cr0, mgr_ns_at_rst, 2);
register_bit!(cr0, boot_en, 1);
register_bit!(cr0, periph_req, 0);
register!(cr1, Cr1, RW, u32);
register_bits!(cr1, num_icache_lines, u8, 4, 7);
register_bits!(cr1, icache_len, u8, 0, 2);
register!(cr2, Cr2, RW, u32);
register_bits!(cr2, boot_addr, u8, 0, 31);
register!(cr3, Cr3, RW, u32);
register_bits!(cr3, ins, u8, 0, 31);
register!(cr4, Cr4, RW, u32);
register_bits!(cr4, ins, u8, 0, 31);
register!(crdn, Crdn, RW, u32);
register_bits!(crdn, data_buffer_dep, u8, 20, 29);
register_bits!(crdn, rd_q_dep, u8, 16, 19);
register_bits!(crdn, rd_cap, u8, 12, 14);
register_bits!(crdn, wr_q_dep, u8, 8, 11);
register_bits!(crdn, wr_cap, u8, 4, 6);
register_bits!(crdn, data_width, u8, 0, 2);
register!(wd, Wd, RW, u32);
register_bit!(wd, wd_irq_only, 0);
register!(periph_id_0, PeriphId0, RW, u32);
register_bits!(periph_id_0, part_number_0, u8, 0, 7);
register!(periph_id_1, PeriphId1, RW, u32);
register_bits!(periph_id_1, designer_0, u8, 4, 7);
register_bits!(periph_id_1, part_number_1, u8, 0, 3);
register!(periph_id_2, PeriphId2, RW, u32);
register_bits!(periph_id_2, revision, u8, 4, 7);
register_bits!(periph_id_2, designer_1, u8, 0, 3);
register!(periph_id_3, PeriphId3, RW, u32);
register_bit!(periph_id_3, integration_cfg, 0);
register!(pcell_id_0, PCellId0, RW, u32);
register_bits!(pcell_id_0, pcell_id_0, u8, 0, 7);
register!(pcell_id_1, PCellId1, RW, u32);
register_bits!(pcell_id_1, pcell_id_1, u8, 0, 7);
register!(pcell_id_2, PCellId2, RW, u32);
register_bits!(pcell_id_2, pcell_id_2, u8, 0, 7);
register!(pcell_id_3, PCellId3, RW, u32);
register_bits!(pcell_id_3, pcell_id_3, u8, 0, 7);

View File

@ -1,5 +1,8 @@
use core::ops::{Deref, DerefMut};
use log::{error, info, warn};
use core::{
marker::PhantomData,
ops::{Deref, DerefMut},
};
use log::{debug, info, warn, error};
use libregister::*;
use super::slcr;
use super::clocks::Clocks;
@ -42,14 +45,109 @@ impl DerefMut for Buffer {
}
}
pub struct Eth<'r, RX, TX> {
/// Gigabit Ethernet Peripheral
pub trait Gem {
fn setup_clock(tx_clock: u32);
fn regs() -> &'static mut regs::RegisterBlock;
}
/// first Gigabit Ethernet peripheral
pub struct Gem0;
impl Gem for Gem0 {
fn setup_clock(tx_clock: u32) {
let (divisor0, divisor1) = calculate_tx_divisors(tx_clock);
slcr::RegisterBlock::unlocked(|slcr| {
slcr.gem0_clk_ctrl.write(
// 0x0050_0801: 8, 5: 100 Mb/s
// ...: 8, 1: 1000 Mb/s
slcr::GemClkCtrl::zeroed()
.clkact(true)
.srcsel(slcr::PllSource::IoPll)
.divisor(divisor0 as u8)
.divisor1(divisor1 as u8)
);
// Enable gem0 recv clock
slcr.gem0_rclk_ctrl.write(
// 0x0000_0801
slcr::RclkCtrl::zeroed()
.clkact(true)
);
});
}
fn regs() -> &'static mut regs::RegisterBlock {
regs::RegisterBlock::gem0()
}
}
/// second Gigabit Ethernet peripheal
pub struct Gem1;
impl Gem for Gem1 {
fn setup_clock(tx_clock: u32) {
let (divisor0, divisor1) = calculate_tx_divisors(tx_clock);
slcr::RegisterBlock::unlocked(|slcr| {
slcr.gem1_clk_ctrl.write(
slcr::GemClkCtrl::zeroed()
.clkact(true)
.srcsel(slcr::PllSource::IoPll)
.divisor(divisor0 as u8)
.divisor1(divisor1 as u8)
);
// Enable gem1 recv clock
slcr.gem1_rclk_ctrl.write(
// 0x0000_0801
slcr::RclkCtrl::zeroed()
.clkact(true)
);
});
}
fn regs() -> &'static mut regs::RegisterBlock {
regs::RegisterBlock::gem1()
}
}
fn calculate_tx_divisors(tx_clock: u32) -> (u8, u8) {
let io_pll = Clocks::get().io;
let target = (tx_clock - 1 + io_pll) / tx_clock;
let mut best = None;
let mut best_error = 0;
for divisor0 in 1..63 {
for divisor1 in 1..63 {
let current = (divisor0 as u32) * (divisor1 as u32);
let error = if current > target {
current - target
} else {
target - current
};
if best.is_none() || best_error > error {
best = Some((divisor0, divisor1));
best_error = error;
}
}
}
let result = best.unwrap();
debug!("Eth TX clock for {}: {} / {} / {} = {}",
tx_clock, io_pll,
result.0, result.1,
io_pll / result.0 as u32 / result.1 as u32
);
result
}
pub struct Eth<GEM: Gem, RX, TX> {
rx: RX,
tx: TX,
inner: EthInner<'r>,
inner: EthInner<GEM>,
phy: Phy,
}
impl<'r> Eth<'r, (), ()> {
impl Eth<Gem0, (), ()> {
pub fn default(macaddr: [u8; 6]) -> Self {
slcr::RegisterBlock::unlocked(|slcr| {
// Manual example: 0x0000_1280
@ -182,22 +280,23 @@ impl<'r> Eth<'r, (), ()> {
}
pub fn gem0(macaddr: [u8; 6]) -> Self {
Self::setup_gem0_clock(TX_1000);
let regs = regs::RegisterBlock::gem0();
Self::from_regs(regs, macaddr)
Self::new(macaddr)
}
}
impl Eth<Gem1, (), ()> {
pub fn gem1(macaddr: [u8; 6]) -> Self {
Self::setup_gem1_clock(TX_1000);
let regs = regs::RegisterBlock::gem1();
Self::from_regs(regs, macaddr)
Self::new(macaddr)
}
}
impl<GEM: Gem> Eth<GEM, (), ()> {
fn new(macaddr: [u8; 6]) -> Self {
GEM::setup_clock(TX_1000);
fn from_regs(regs: &'r mut regs::RegisterBlock, macaddr: [u8; 6]) -> Self {
let mut inner = EthInner {
regs,
gem: PhantomData,
link: None,
};
inner.init();
@ -216,54 +315,8 @@ impl<'r> Eth<'r, (), ()> {
}
}
impl<'r, RX, TX> Eth<'r, RX, TX> {
pub fn setup_gem0_clock(tx_clock: u32) {
let io_pll = Clocks::get().io;
let d0 = ((tx_clock - 1 + io_pll) / tx_clock).max(1).min(63);
let d1 = (io_pll / tx_clock / d0).max(1).min(63);
slcr::RegisterBlock::unlocked(|slcr| {
slcr.gem0_clk_ctrl.write(
// 0x0050_0801: 8, 5: 100 Mb/s
// ...: 8, 1: 1000 Mb/s
slcr::GemClkCtrl::zeroed()
.clkact(true)
.srcsel(slcr::PllSource::IoPll)
.divisor(d0 as u8)
.divisor1(d1 as u8)
);
// Enable gem0 recv clock
slcr.gem0_rclk_ctrl.write(
// 0x0000_0801
slcr::RclkCtrl::zeroed()
.clkact(true)
);
});
}
pub fn setup_gem1_clock(tx_clock: u32) {
let io_pll = Clocks::get().io;
let d0 = ((tx_clock - 1 + io_pll) / tx_clock).max(1).min(63);
let d1 = (io_pll / tx_clock / d0).max(1).min(63);
slcr::RegisterBlock::unlocked(|slcr| {
slcr.gem1_clk_ctrl.write(
slcr::GemClkCtrl::zeroed()
.clkact(true)
.srcsel(slcr::PllSource::IoPll)
.divisor(d0 as u8)
.divisor1(d1 as u8)
);
// Enable gem1 recv clock
slcr.gem1_rclk_ctrl.write(
// 0x0000_0801
slcr::RclkCtrl::zeroed()
.clkact(true)
);
});
}
pub fn start_rx(self, rx_size: usize) -> Eth<'r, rx::DescList, TX> {
impl<GEM: Gem, RX, TX> Eth<GEM, RX, TX> {
pub fn start_rx(self, rx_size: usize) -> Eth<GEM, rx::DescList, TX> {
let new_self = Eth {
rx: rx::DescList::new(rx_size),
tx: self.tx,
@ -272,17 +325,17 @@ impl<'r, RX, TX> Eth<'r, RX, TX> {
};
let list_addr = new_self.rx.list_addr();
assert!(list_addr & 0b11 == 0);
new_self.inner.regs.rx_qbar.write(
GEM::regs().rx_qbar.write(
regs::RxQbar::zeroed()
.rx_q_baseaddr(list_addr >> 2)
);
new_self.inner.regs.net_ctrl.modify(|_, w|
GEM::regs().net_ctrl.modify(|_, w|
w.rx_en(true)
);
new_self
}
pub fn start_tx(self, tx_size: usize) -> Eth<'r, RX, tx::DescList> {
pub fn start_tx(self, tx_size: usize) -> Eth<GEM, RX, tx::DescList> {
let new_self = Eth {
rx: self.rx,
tx: tx::DescList::new(tx_size),
@ -291,23 +344,23 @@ impl<'r, RX, TX> Eth<'r, RX, TX> {
};
let list_addr = &new_self.tx.list_addr();
assert!(list_addr & 0b11 == 0);
new_self.inner.regs.tx_qbar.write(
GEM::regs().tx_qbar.write(
regs::TxQbar::zeroed()
.tx_q_baseaddr(list_addr >> 2)
);
new_self.inner.regs.net_ctrl.modify(|_, w|
GEM::regs().net_ctrl.modify(|_, w|
w.tx_en(true)
);
new_self
}
}
impl<'r, TX> Eth<'r, rx::DescList, TX> {
impl<GEM: Gem, TX> Eth<GEM, rx::DescList, TX> {
pub fn recv_next<'s: 'p, 'p>(&'s mut self) -> Result<Option<rx::PktRef<'p>>, rx::Error> {
let status = self.inner.regs.rx_status.read();
let status = GEM::regs().rx_status.read();
if status.hresp_not_ok() {
// Clear
self.inner.regs.rx_status.write(
GEM::regs().rx_status.write(
regs::RxStatus::zeroed()
.hresp_not_ok(true)
);
@ -315,7 +368,7 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> {
}
if status.rx_overrun() {
// Clear
self.inner.regs.rx_status.write(
GEM::regs().rx_status.write(
regs::RxStatus::zeroed()
.rx_overrun(true)
);
@ -323,7 +376,7 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> {
}
if status.buffer_not_avail() {
// Clear
self.inner.regs.rx_status.write(
GEM::regs().rx_status.write(
regs::RxStatus::zeroed()
.buffer_not_avail(true)
);
@ -335,7 +388,7 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> {
match result {
Ok(None) => {
// No packet, clear status bit
self.inner.regs.rx_status.write(
GEM::regs().rx_status.write(
regs::RxStatus::zeroed()
.frame_recd(true)
);
@ -350,13 +403,13 @@ impl<'r, TX> Eth<'r, rx::DescList, TX> {
}
}
impl<'r, RX> Eth<'r, RX, tx::DescList> {
impl<GEM: Gem, RX> Eth<GEM, RX, tx::DescList> {
pub fn send<'s: 'p, 'p>(&'s mut self, length: usize) -> Option<tx::PktRef<'p>> {
self.tx.send(self.inner.regs, length)
self.tx.send(GEM::regs(), length)
}
}
impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescList> {
impl<'a, GEM: Gem> smoltcp::phy::Device<'a> for &mut Eth<GEM, rx::DescList, tx::DescList> {
type RxToken = rx::PktRef<'a>;
type TxToken = tx::Token<'a>;
@ -370,6 +423,7 @@ impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescLis
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = MTU;
caps.max_burst_size = Some(self.rx.len().min(self.tx.len()));
caps.checksum = checksum_caps;
caps
@ -379,7 +433,7 @@ impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescLis
match self.rx.recv_next() {
Ok(Some(pktref)) => {
let tx_token = tx::Token {
regs: self.inner.regs,
regs: GEM::regs(),
desc_list: &mut self.tx,
};
Some((pktref, tx_token))
@ -397,33 +451,32 @@ impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescLis
fn transmit(&'a mut self) -> Option<Self::TxToken> {
Some(tx::Token {
regs: self.inner.regs,
regs: GEM::regs(),
desc_list: &mut self.tx,
})
}
}
struct EthInner<'r> {
regs: &'r mut regs::RegisterBlock,
struct EthInner<GEM: Gem> {
gem: PhantomData<GEM>,
link: Option<phy::Link>,
}
impl<'r> EthInner<'r> {
impl<GEM: Gem> EthInner<GEM> {
fn init(&mut self) {
// Clear the Network Control register.
self.regs.net_ctrl.write(regs::NetCtrl::zeroed());
self.regs.net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true));
GEM::regs().net_ctrl.write(regs::NetCtrl::zeroed());
GEM::regs().net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true));
// Clear the Status registers.
self.regs.rx_status.write(
GEM::regs().rx_status.write(
regs::RxStatus::zeroed()
.buffer_not_avail(true)
.frame_recd(true)
.rx_overrun(true)
.hresp_not_ok(true)
);
self.regs.tx_status.write(
GEM::regs().tx_status.write(
regs::TxStatus::zeroed()
.used_bit_read(true)
.collision(true)
@ -437,7 +490,7 @@ impl<'r> EthInner<'r> {
.hresp_not_ok(true)
);
// Disable all interrupts.
self.regs.intr_dis.write(
GEM::regs().intr_dis.write(
regs::IntrDis::zeroed()
.mgmt_done(true)
.rx_complete(true)
@ -467,10 +520,10 @@ impl<'r> EthInner<'r> {
.tsu_sec_incr(true)
);
// Clear the buffer queues.
self.regs.rx_qbar.write(
GEM::regs().rx_qbar.write(
regs::RxQbar::zeroed()
);
self.regs.tx_qbar.write(
GEM::regs().tx_qbar.write(
regs::TxQbar::zeroed()
);
}
@ -479,7 +532,7 @@ impl<'r> EthInner<'r> {
let clocks = Clocks::get();
let mdc_clk_div = (clocks.cpu_1x() / MAX_MDC) + 1;
self.regs.net_cfg.write(
GEM::regs().net_cfg.write(
regs::NetCfg::zeroed()
.full_duplex(true)
.gige_en(true)
@ -504,17 +557,17 @@ impl<'r> EthInner<'r> {
(u32::from(macaddr[3]) << 16) |
(u32::from(macaddr[4]) << 8) |
u32::from(macaddr[5]);
self.regs.spec_addr1_top.write(
GEM::regs().spec_addr1_top.write(
regs::SpecAddrTop::zeroed()
.addr_msbs(macaddr_msbs)
);
self.regs.spec_addr1_bot.write(
GEM::regs().spec_addr1_bot.write(
regs::SpecAddrBot::zeroed()
.addr_lsbs(macaddr_lsbs)
);
self.regs.dma_cfg.write(
GEM::regs().dma_cfg.write(
regs::DmaCfg::zeroed()
// 1536 bytes
.ahb_mem_rx_buf_size((MTU >> 6) as u8)
@ -530,7 +583,7 @@ impl<'r> EthInner<'r> {
.ahb_fixed_burst_len(0x10)
);
self.regs.net_ctrl.write(
GEM::regs().net_ctrl.write(
regs::NetCtrl::zeroed()
.mgmt_port_en(true)
);
@ -538,7 +591,7 @@ impl<'r> EthInner<'r> {
fn wait_phy_idle(&self) {
while !self.regs.net_status.read().phy_mgmt_idle() {}
while !GEM::regs().net_status.read().phy_mgmt_idle() {}
}
@ -557,17 +610,15 @@ impl<'r> EthInner<'r> {
Some(link) => {
info!("eth: got {:?}", link);
use phy::LinkSpeed::*;
use phy::{LinkDuplex::Full, LinkSpeed::*};
let txclock = match link.speed {
S10 => TX_10,
S100 => TX_100,
S1000 => TX_1000,
};
Eth::<(), ()>::setup_gem0_clock(txclock);
/* .full_duplex(false) doesn't work even if
half duplex has been negotiated. */
self.regs.net_cfg.modify(|_, w| w
.full_duplex(true)
GEM::setup_clock(txclock);
GEM::regs().net_cfg.modify(|_, w| w
.full_duplex(link.duplex == Full)
.gige_en(link.speed == S1000)
.speed(link.speed != S10)
);
@ -586,10 +637,10 @@ impl<'r> EthInner<'r> {
}
}
impl<'r> PhyAccess for EthInner<'r> {
impl<GEM: Gem> PhyAccess for EthInner<GEM> {
fn read_phy(&mut self, addr: u8, reg: u8) -> u16 {
self.wait_phy_idle();
self.regs.phy_maint.write(
GEM::regs().phy_maint.write(
regs::PhyMaint::zeroed()
.clause_22(true)
.operation(regs::PhyOperation::Read)
@ -598,12 +649,12 @@ impl<'r> PhyAccess for EthInner<'r> {
.must_10(0b10)
);
self.wait_phy_idle();
self.regs.phy_maint.read().data()
GEM::regs().phy_maint.read().data()
}
fn write_phy(&mut self, addr: u8, reg: u8, data: u16) {
self.wait_phy_idle();
self.regs.phy_maint.write(
GEM::regs().phy_maint.write(
regs::PhyMaint::zeroed()
.clause_22(true)
.operation(regs::PhyOperation::Write)

View File

@ -1,59 +0,0 @@
use bit_field::BitField;
use super::{PhyRegister, Link, LinkDuplex, LinkSpeed};
#[derive(Clone, Copy, Debug)]
/// 1000Base-T Extended Status Register
pub struct ExtendedStatus(pub u16);
impl ExtendedStatus {
pub fn cap_1000base_t_half(&self) -> bool {
self.0.get_bit(12)
}
pub fn cap_1000base_t_full(&self) -> bool {
self.0.get_bit(13)
}
pub fn cap_1000base_x_half(&self) -> bool {
self.0.get_bit(14)
}
pub fn cap_1000base_x_full(&self) -> bool {
self.0.get_bit(12)
}
pub fn get_link(&self) -> Option<Link> {
if self.cap_1000base_t_half() {
Some(Link {
speed: LinkSpeed::S1000,
duplex: LinkDuplex::Half,
})
} else if self.cap_1000base_t_full() {
Some(Link {
speed: LinkSpeed::S1000,
duplex: LinkDuplex::Full,
})
} else if self.cap_1000base_x_half() {
Some(Link {
speed: LinkSpeed::S1000,
duplex: LinkDuplex::Half,
})
} else if self.cap_1000base_x_full() {
Some(Link {
speed: LinkSpeed::S1000,
duplex: LinkDuplex::Full,
})
} else {
None
}
}
}
impl PhyRegister for ExtendedStatus {
fn addr() -> u8 {
0xF
}
}
impl From<u16> for ExtendedStatus {
fn from(value: u16) -> Self {
ExtendedStatus(value)
}
}

View File

@ -2,10 +2,10 @@ pub mod id;
use id::{identify_phy, PhyIdentifier};
mod status;
pub use status::Status;
mod extended_status;
pub use extended_status::ExtendedStatus;
mod control;
pub use control::Control;
mod pssr;
pub use pssr::PSSR;
#[derive(Clone, Debug, PartialEq)]
pub struct Link {
@ -39,43 +39,36 @@ pub struct Phy {
#[derive(Clone, Copy)]
pub enum PhyDevice {
Marvel88E1116R,
Marvell88E1116R,
Rtl8211E,
}
const OUI_MARVEL: u32 = 0x005043;
const OUI_MARVELL: u32 = 0x005043;
const OUI_REALTEK: u32 = 0x000732;
impl Phy {
/// Probe all addresses on MDIO for a known PHY
pub fn find<PA: PhyAccess>(pa: &mut PA) -> Option<Phy> {
for addr in 1..32 {
let device = match identify_phy(pa, addr) {
(1..32).filter_map(|addr| {
match identify_phy(pa, addr) {
Some(PhyIdentifier {
oui: OUI_MARVEL,
oui: OUI_MARVELL,
model: 36,
..
}) => Some(PhyDevice::Marvel88E1116R),
}) => Some(PhyDevice::Marvell88E1116R),
Some(PhyIdentifier {
oui: OUI_REALTEK,
model: 0b010001,
rev: 0b0101,
}) => Some(PhyDevice::Rtl8211E),
_ => None,
};
match device {
Some(device) =>
return Some(Phy { addr, device }),
None => {}
}
}
None
}.map(|device| Phy { addr, device })
}).next()
}
pub fn name(&self) -> &'static str {
match self.device {
PhyDevice::Marvel88E1116R => &"Marvel 88E1116R",
PhyDevice::Marvell88E1116R => &"Marvell 88E1116R",
PhyDevice::Rtl8211E => &"RTL8211E",
}
}
@ -120,12 +113,8 @@ impl Phy {
if !status.link_status() {
None
} else if status.cap_1000base_t_extended_status() {
let ext_status: ExtendedStatus = self.read_reg(pa);
if let Some(link) = ext_status.get_link() {
Some(link)
} else {
status.get_link()
}
let phy_status: PSSR = self.read_reg(pa);
phy_status.get_link()
} else {
status.get_link()
}

View File

@ -0,0 +1,52 @@
use bit_field::BitField;
use super::{PhyRegister, Link, LinkDuplex, LinkSpeed};
#[derive(Clone, Copy, Debug)]
/// PHY-Specific Status Register
pub struct PSSR(pub u16);
impl PSSR {
pub fn link(&self) -> bool {
self.0.get_bit(10)
}
pub fn duplex(&self) -> LinkDuplex {
if self.0.get_bit(13) {
LinkDuplex::Full
} else {
LinkDuplex::Half
}
}
pub fn speed(&self) -> Option<LinkSpeed> {
match self.0.get_bits(14..=15) {
0b00 => Some(LinkSpeed::S10),
0b01 => Some(LinkSpeed::S100),
0b10 => Some(LinkSpeed::S1000),
_ => None,
}
}
pub fn get_link(&self) -> Option<Link> {
if self.link() {
Some(Link {
speed: self.speed()?,
duplex: self.duplex(),
})
} else {
None
}
}
}
impl PhyRegister for PSSR {
fn addr() -> u8 {
0x11
}
}
impl From<u16> for PSSR {
fn from(value: u16) -> Self {
PSSR(value)
}
}

View File

@ -93,6 +93,10 @@ impl DescList {
}
}
pub fn len(&self) -> usize {
self.list.len().min(self.buffers.len())
}
pub fn list_addr(&self) -> u32 {
&self.list[0] as *const _ as u32
}

View File

@ -85,6 +85,10 @@ impl DescList {
}
}
pub fn len(&self) -> usize {
self.list.len().min(self.buffers.len())
}
pub fn list_addr(&self) -> u32 {
&self.list[0] as *const _ as u32
}

150
libboard_zynq/src/gic.rs Normal file
View File

@ -0,0 +1,150 @@
//! ARM Generic Interrupt Controller
use bit_field::BitField;
use libregister::{RegisterW, RegisterRW, RegisterR};
use super::mpcore;
#[derive(Debug, Clone, Copy)]
pub struct InterruptId(pub u8);
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum CPUCore {
Core0 = 0b01,
Core1 = 0b10
}
#[derive(Debug, Clone, Copy)]
pub struct TargetCPU(u8);
impl TargetCPU {
pub const fn none() -> TargetCPU {
TargetCPU(0)
}
pub const fn and(self, other: TargetCPU) -> TargetCPU {
TargetCPU(self.0 | other.0)
}
}
impl From<CPUCore> for TargetCPU {
fn from(core: CPUCore) -> Self {
TargetCPU(core as u8)
}
}
pub enum TargetList {
CPUList(TargetCPU),
Others,
This
}
impl From<CPUCore> for TargetList {
fn from(core: CPUCore) -> Self {
TargetList::CPUList(TargetCPU(core as u8))
}
}
impl From<TargetCPU> for TargetList {
fn from(cpu: TargetCPU) -> Self {
TargetList::CPUList(cpu)
}
}
#[derive(Debug, Clone, Copy)]
pub enum InterruptSensitivity {
Level,
Edge,
}
pub struct InterruptController {
mpcore: &'static mut mpcore::RegisterBlock,
}
impl InterruptController {
pub fn new(mpcore: &'static mut mpcore::RegisterBlock) -> Self {
InterruptController { mpcore }
}
pub fn disable_interrupts(&mut self) {
self.mpcore.iccicr.modify(|_, w| w.enable_ns(false)
.enable_s(false));
// FIXME: Should we disable the distributor globally when we disable interrupt (for a single
// core)?
// self.mpcore.icddcr.modify(|_, w| w.enable_secure(false)
// .enable_non_secure(false));
}
/// enable interrupt signaling
pub fn enable_interrupts(&mut self) {
self.mpcore.iccicr.modify(|_, w| w.enable_ns(true)
.enable_s(true));
self.mpcore.icddcr.modify(|_, w| w.enable_secure(true));
// Enable all interrupts except those of the lowest priority.
self.mpcore.iccpmr.write(mpcore::ICCPMR::zeroed().priority(0xFF));
}
/// send software generated interrupt
pub fn send_sgi(&mut self, id: InterruptId, targets: TargetList) {
assert!(id.0 < 16);
self.mpcore.icdsgir.modify(|_, w| match targets {
TargetList::CPUList(list) => w.target_list_filter(0).cpu_target_list(list.0),
TargetList::Others => w.target_list_filter(0b01),
TargetList::This => w.target_list_filter(0b10)
}.sgiintid(id.0).satt(false));
}
/// enable the interrupt *for this core*.
/// Not needed for SGI.
pub fn enable(&mut self, id: InterruptId, target_cpu: CPUCore, sensitivity: InterruptSensitivity, priority: u8) {
// only 5 bits of the priority is useful
assert!(priority < 32);
self.disable_interrupts();
// enable
let m = (id.0 >> 5) as usize;
let n = (id.0 & 0x1F) as usize;
assert!(m < 3);
unsafe {
self.mpcore.icdiser[m].modify(|mut icdiser| *icdiser.set_bit(n, true));
}
// target cpu
let m = (id.0 >> 2) as usize;
let n = (8 * (id.0 & 3)) as usize;
unsafe {
self.mpcore.icdiptr[m].modify(|mut icdiptr| *icdiptr.set_bits(n..=n+1, target_cpu as u32 + 1));
}
// sensitivity
let m = (id.0 >> 4) as usize;
let n = (2 * (id.0 & 0xF)) as usize;
unsafe {
self.mpcore.icdicfr[m].modify(|mut icdicfr| *icdicfr.set_bits(n..=n+1, match sensitivity {
InterruptSensitivity::Level => 0b00,
InterruptSensitivity::Edge => 0b10,
}));
}
// priority
let offset = (id.0 % 4) * 8;
let priority: u32 = (priority as u32) << (offset + 3);
let mask: u32 = 0xFFFFFFFF ^ (0xFF << offset);
unsafe {
self.mpcore.icdipr[id.0 as usize / 4].modify(|v| (v & mask) | priority);
}
self.enable_interrupts();
}
pub fn end_interrupt(&mut self, id: InterruptId) {
self.mpcore.icceoir.modify(|_, w| w.eoiintid(id.0 as u32));
}
pub fn get_interrupt_id(&self) -> InterruptId {
InterruptId(self.mpcore.icciar.read().ackintid() as u8)
}
}

View File

@ -0,0 +1,107 @@
use super::I2C;
use crate::time::Milliseconds;
use embedded_hal::timer::CountDown;
pub struct EEPROM<'a> {
i2c: &'a mut I2C,
port: u8,
address: u8,
page_size: u8,
count_down: crate::timer::global::CountDown<Milliseconds>
}
impl<'a> EEPROM<'a> {
#[cfg(feature = "target_zc706")]
pub fn new(i2c: &'a mut I2C, page_size: u8) -> Self {
EEPROM {
i2c: i2c,
port: 2,
address: 0b1010100,
page_size: page_size,
count_down: unsafe { crate::timer::GlobalTimer::get() }.countdown()
}
}
#[cfg(feature = "target_zc706")]
fn select(&mut self) -> Result<(), &'static str> {
let mask: u16 = 1 << self.port;
self.i2c.pca9548_select(0b1110100, mask as u8)?;
Ok(())
}
/// Random read
pub fn read<'r>(&mut self, addr: u8, buf: &'r mut [u8]) -> Result<(), &'static str> {
self.select()?;
self.i2c.start()?;
self.i2c.write(self.address << 1)?;
self.i2c.write(addr)?;
self.i2c.restart()?;
self.i2c.write((self.address << 1) | 1)?;
let buf_len = buf.len();
for (i, byte) in buf.iter_mut().enumerate() {
*byte = self.i2c.read(i < buf_len - 1)?;
}
self.i2c.stop()?;
Ok(())
}
/// Smart multi-page writing
/// Using the "Page Write" function of an EEPROM, the memory region for each transaction
/// (i.e. from byte `addr` to byte `addr+buf.len()`) should fit under each page
/// (i.e. `addr+buf.len()` < `addr/self.page_size+1`); otherwise, a roll-oever occurs,
/// where bytes beyond the page end. This smart function takes care of the scenario to avoid
/// any roll-over when writing ambiguous memory regions.
pub fn write(&mut self, addr: u8, buf: &[u8]) -> Result<(), &'static str> {
self.select()?;
let buf_len = buf.len();
let mut pb: u8 = addr % self.page_size;
for (i, byte) in buf.iter().enumerate() {
if (i == 0) || (pb == 0) {
self.i2c.start()?;
self.i2c.write(self.address << 1)?;
self.i2c.write(addr + (i as u8))?;
}
self.i2c.write(*byte)?;
pb += 1;
if (i == buf_len-1) || (pb == self.page_size) {
self.i2c.stop()?;
self.poll(1_000)?;
pb = 0;
}
}
Ok(())
}
/// Poll
pub fn poll(&mut self, timeout_ms: u64) -> Result<(), &'static str> {
self.select()?;
self.count_down.start(Milliseconds(timeout_ms));
loop {
self.i2c.start()?;
let ack = self.i2c.write(self.address << 1)?;
self.i2c.stop()?;
if ack {
break
};
if !self.count_down.waiting() {
return Err("I2C polling timeout")
}
}
Ok(())
}
pub fn read_eui48<'r>(&mut self) -> Result<[u8; 6], &'static str> {
let mut buffer = [0u8; 6];
self.read(0xFA, &mut buffer)?;
Ok(buffer)
}
}

View File

@ -0,0 +1,238 @@
//! I2C Bit-banging Controller
mod regs;
pub mod eeprom;
use super::clocks::Clocks;
use super::slcr;
use super::time::Microseconds;
use embedded_hal::timer::CountDown;
use libregister::{RegisterR, RegisterRW, RegisterW};
const INVALID_BUS: &'static str = "Invalid I2C bus";
#[cfg(feature = "target_zc706")]
const GPIO_OUTPUT_MASK: u16 = 0xFFFF - 0x000C;
pub struct I2C {
regs: regs::RegisterWrapper,
count_down: super::timer::global::CountDown<Microseconds>
}
impl I2C {
#[cfg(feature = "target_zc706")]
pub fn i2c() -> Self {
// Route I2C 0 SCL / SDA Signals to MIO Pins 50 / 51
slcr::RegisterBlock::unlocked(|slcr| {
// SCL
slcr.mio_pin_50.write(
slcr::MioPin50::zeroed()
.l3_sel(0b000) // as GPIO 50
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
.disable_rcvr(true)
);
// SDA
slcr.mio_pin_51.write(
slcr::MioPin51::zeroed()
.l3_sel(0b000) // as GPIO 51
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
.disable_rcvr(true)
);
// Reset
slcr.gpio_rst_ctrl.reset_gpio();
});
Self::ctor_common()
}
fn ctor_common() -> Self {
// Setup register block
let clocks = Clocks::get();
let self_ = Self {
regs: regs::RegisterWrapper::new(),
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown()
};
// Setup GPIO output mask
self_.regs.gpio_output_mask.modify(|_, w| {
w.mask(GPIO_OUTPUT_MASK)
});
// Setup GPIO driver direction
self_.regs.gpio_direction.modify(|_, w| {
w.scl(true).sda(true)
});
self_
}
/// Delay for I2C operations, simple wrapper for nb.
fn delay_us(&mut self, us: u64) {
self.count_down.start(Microseconds(us));
nb::block!(self.count_down.wait()).unwrap();
}
fn half_period(&mut self) { self.delay_us(100) }
fn sda_i(&mut self) -> bool {
self.regs.gpio_input.read().sda()
}
fn scl_i(&mut self) -> bool {
self.regs.gpio_input.read().scl()
}
fn sda_oe(&mut self, oe: bool) {
self.regs.gpio_output_enable.modify(|_, w| {
w.sda(oe)
})
}
fn sda_o(&mut self, o: bool) {
self.regs.gpio_output_mask.modify(|_, w| {
w.sda_o(o)
})
}
fn scl_oe(&mut self, oe: bool) {
self.regs.gpio_output_enable.modify(|_, w| {
w.scl(oe)
})
}
fn scl_o(&mut self, o: bool) {
self.regs.gpio_output_mask.modify(|_, w| {
w.scl_o(o)
})
}
pub fn init(&mut self) -> Result<(), &'static str> {
self.scl_oe(false);
self.sda_oe(false);
self.scl_o(false);
self.sda_o(false);
// Check the I2C bus is ready
self.half_period();
self.half_period();
if !self.sda_i() {
// Try toggling SCL a few times
for _bit in 0..8 {
self.scl_oe(true);
self.half_period();
self.scl_oe(false);
self.half_period();
}
}
if !self.sda_i() {
return Err("SDA is stuck low and doesn't get unstuck");
}
if !self.scl_i() {
return Err("SCL is stuck low and doesn't get unstuck");
}
// postcondition: SCL and SDA high
Ok(())
}
pub fn start(&mut self) -> Result<(), &'static str> {
// precondition: SCL and SDA high
if !self.scl_i() {
return Err("SCL is stuck low and doesn't get unstuck");
}
if !self.sda_i() {
return Err("SDA arbitration lost");
}
self.sda_oe(true);
self.half_period();
self.scl_oe(true);
// postcondition: SCL and SDA low
Ok(())
}
pub fn restart(&mut self) -> Result<(), &'static str> {
// precondition SCL and SDA low
self.sda_oe(false);
self.half_period();
self.scl_oe(false);
self.half_period();
self.start()?;
// postcondition: SCL and SDA low
Ok(())
}
pub fn stop(&mut self) -> Result<(), &'static str> {
// precondition: SCL and SDA low
self.half_period();
self.scl_oe(false);
self.half_period();
self.sda_oe(false);
self.half_period();
if !self.sda_i() {
return Err("SDA arbitration lost");
}
// postcondition: SCL and SDA high
Ok(())
}
pub fn write(&mut self, data: u8) -> Result<bool, &'static str> {
// precondition: SCL and SDA low
// MSB first
for bit in (0..8).rev() {
self.sda_oe(data & (1 << bit) == 0);
self.half_period();
self.scl_oe(false);
self.half_period();
self.scl_oe(true);
}
self.sda_oe(false);
self.half_period();
self.scl_oe(false);
self.half_period();
// Read ack/nack
let ack = !self.sda_i();
self.scl_oe(true);
self.sda_oe(true);
// postcondition: SCL and SDA low
Ok(ack)
}
pub fn read(&mut self, ack: bool) -> Result<u8, &'static str> {
// precondition: SCL and SDA low
self.sda_oe(false);
let mut data: u8 = 0;
// MSB first
for bit in (0..8).rev() {
self.half_period();
self.scl_oe(false);
self.half_period();
if self.sda_i() { data |= 1 << bit }
self.scl_oe(true);
}
// Send ack/nack
self.sda_oe(ack);
self.half_period();
self.scl_oe(false);
self.half_period();
self.scl_oe(true);
self.sda_oe(true);
// postcondition: SCL and SDA low
Ok(data)
}
pub fn pca9548_select(&mut self, address: u8, channels: u8) -> Result<(), &'static str> {
self.start()?;
if !self.write(address << 1)? {
return Err("PCA9548 failed to ack write address")
}
if !self.write(channels)? {
return Err("PCA9548 failed to ack control word")
}
self.stop()?;
Ok(())
}
}

View File

@ -0,0 +1,93 @@
use volatile_register::{RO, WO, RW};
use libregister::{
register, register_at,
register_bit, register_bits
};
// With reference to:
//
// artiq:artiq/gateware/targets/kasli.py:
// self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda])
//
// misoc:misoc/cores/gpio.py:
// class GPIOTristate(Module, AutoCSR):
// def __init__(self, signals, reset_out=0, reset_oe=0):
// l = len(signals)
// self._in = CSRStatus(l)
// self._out = CSRStorage(l, reset=reset_out)
// self._oe = CSRStorage(l, reset=reset_oe)
//
// Hence, using GPIOs as SCL and SDA GPIOs respectively.
//
// Current compatibility:
// zc706: GPIO 50, 51 == SCL, SDA
pub struct RegisterWrapper {
pub gpio_output_mask: &'static mut GPIOOutputMask,
pub gpio_input: &'static mut GPIOInput,
pub gpio_direction: &'static mut GPIODirection,
pub gpio_output_enable: &'static mut GPIOOutputEnable,
}
impl RegisterWrapper {
pub fn new() -> Self {
Self {
gpio_output_mask: GPIOOutputMask::new(),
gpio_input: GPIOInput::new(),
gpio_direction: GPIODirection::new(),
gpio_output_enable: GPIOOutputEnable::new()
}
}
}
// MASK_DATA_1_MSW:
// Maskable output data for MIO[53:48]
register!(gpio_output_mask, GPIOOutputMask, RW, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIOOutputMask, 0xE000A00C, new);
// Output for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_mask, scl_o, 2);
// Output for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_mask, sda_o, 3);
// Mask for keeping bits except SCL and SDA unchanged
#[cfg(feature = "target_zc706")]
register_bits!(gpio_output_mask, mask, u16, 16, 31);
// DATA_1_RO:
// Input data for MIO[53:32]
register!(gpio_input, GPIOInput, RO, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIOInput, 0xE000A064, new);
// Input for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_input, scl, 18);
// Input for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_input, sda, 19);
// DIRM_1:
// Direction mode for MIO[53:32]; 0/1 = in/out
register!(gpio_direction, GPIODirection, RW, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIODirection, 0xE000A244, new);
// Direction for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_direction, scl, 18);
// Direction for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_direction, sda, 19);
// OEN_1:
// Output enable for MIO[53:32]
register!(gpio_output_enable, GPIOOutputEnable, RW, u32);
#[cfg(feature = "target_zc706")]
register_at!(GPIOOutputEnable, 0xE000A248, new);
// Output enable for SCL
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_enable, scl, 18);
// Output enable for SDA
#[cfg(feature = "target_zc706")]
register_bit!(gpio_output_enable, sda, 19);

View File

@ -15,10 +15,11 @@ pub mod axi_hp;
pub mod axi_gp;
pub mod ddr;
pub mod mpcore;
pub mod gic;
pub mod flash;
pub mod dmac;
pub mod time;
pub mod timer;
pub mod sdio;
pub mod i2c;
pub mod logger;
pub mod ps7_init;

View File

@ -19,7 +19,7 @@ impl log::Log for Logger {
if self.enabled(record.metadata()) {
let timestamp = unsafe {
GlobalTimer::get()
}.get_us();
}.get_us().0;
let seconds = timestamp / 1_000_000;
let micros = timestamp % 1_000_000;

View File

@ -8,48 +8,141 @@ use libregister::{
#[repr(C)]
pub struct RegisterBlock {
/// SCU Control Register
pub scu_control: ScuControl,
pub scu_config: RO<u32>,
pub scu_cpu_power: RW<u32>,
/// SCU Configuration Register
pub scu_config: ScuConfig,
/// SCU CPU Power Status Register
pub scu_cpu_power_status: SCUCPUPowerStatusRegister,
/// SCU Invalidate All Registers in Secure State
pub scu_invalidate: ScuInvalidate,
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>,
reserved2: [u32; 42],
pub iccicr: RW<u32>,
pub iccpmw: RW<u32>,
pub iccbpr: RW<u32>,
pub icciar: RW<u32>,
pub icceoir: RW<u32>,
pub iccrpr: RW<u32>,
pub icchpir: RW<u32>,
pub iccabpr: RW<u32>,
reserved3: [u32; 55],
pub iccidr: RW<u32>,
unused0: [u32; 12],
/// Filtering Start Address Register
pub filtering_start_address: FilteringStartAddressRegister,
/// Defined by FILTEREND input
pub filtering_end_address: FilteringEndAddressRegister,
unused1: [u32; 2],
/// SCU Access Control (SAC) Register
pub scu_access_control_sac: SCUAccessControlRegisterSAC,
/// SCU Non-secure Access Control Register SNSAC
pub scu_non_secure_access_control: SCUNonSecureAccessControlRegister,
unused2: [u32; 42],
/// CPU Interface Control Register
pub iccicr: ICCICR,
/// Interrupt Priority Mask Register
pub iccpmr: ICCPMR,
/// Binary Point Register
pub iccbpr: ICCBPR,
/// Interrupt Acknowledge Register
pub icciar: ICCIAR,
/// End Of Interrupt Register
pub icceoir: ICCEOIR,
/// Running Priority Register
pub iccrpr: ICCRPR,
/// Highest Pending Interrupt Register
pub icchpir: ICCHPIR,
/// Aliased Non-secure Binary Point Register
pub iccabpr: ICCABPR,
unused3: [u32; 55],
/// CPU Interface Implementer Identification Register
pub iccidr: ICCIDR,
/// Global Timer Counter Register 0
pub global_timer_counter0: ValueRegister,
pub global_timer_counter1: ValueRegister,
/// Global Timer Control Register
pub global_timer_control: GlobalTimerControl,
pub global_timer_interrupt_status: RW<u32>,
/// Global Timer Interrupt Status Register
pub global_timer_interrupt_status: GlobalTimerInterruptStatusRegister,
/// Comparator Value Register_0
pub comparator_value0: ValueRegister,
pub comparator_value1: ValueRegister,
pub auto_increment: ValueRegister,
reserved4: [u32; 249],
pub private_timer_load: ValueRegister,
pub private_timer_counter: ValueRegister,
pub private_timer_control: RW<u32>,
pub private_timer_interrupt_status: RW<u32>,
reserved5: [u32; 4],
pub watchdog_load: ValueRegister,
pub watchdog_counter: ValueRegister,
pub watchdog_control: RW<u32>,
pub watchdog_interrupt_status: RW<u32>,
// there is plenty more (unimplemented)
/// Auto-increment Register
pub auto_increment: RW<u32>,
unused4: [u32; 249],
/// Private Timer Load Register
pub private_timer_load: RW<u32>,
/// Private Timer Counter Register
pub private_timer_counter: RW<u32>,
/// Private Timer Control Register
pub private_timer_control: PrivateTimerControlRegister,
/// Private Timer Interrupt Status Register
pub private_timer_interrupt_status: PrivateTimerInterruptStatusRegister,
unused5: [u32; 4],
/// Watchdog Load Register
pub watchdog_load: RW<u32>,
/// Watchdog Counter Register
pub watchdog_counter: RW<u32>,
/// Watchdog Control Register
pub watchdog_control: WatchdogControlRegister,
/// Watchdog Interrupt Status Register
pub watchdog_interrupt_status: WatchdogInterruptStatusRegister,
/// Watchdog Reset Status Register
pub watchdog_reset_status: WatchdogResetStatusRegister,
/// Watchdog Disable Register
pub watchdog_disable: RW<u32>,
unused6: [u32; 626],
/// Distributor Control Register
pub icddcr: ICDDCR,
/// Interrupt Controller Type Register
pub icdictr: ICDICTR,
/// Distributor Implementer Identification Register
pub icdiidr: ICDIIDR,
unused7: [u32; 29],
/// Interrupt Security Register
pub icdisr0: RW<u32>,
pub icdisr1: RW<u32>,
pub icdisr2: RW<u32>,
unused8: [u32; 29],
/// Interrupt Set-enable Registers
pub icdiser: [RW<u32>; 3],
unused9: [u32; 29],
/// Interrupt Clear-Enable Register 0
pub icdicer0: RW<u32>,
/// Interrupt Clear-Enable Register 1
pub icdicer1: RW<u32>,
/// Interrupt Clear-Enable Register 2
pub icdicer2: RW<u32>,
unused10: [u32; 29],
/// Interrupt Set-pending Register
pub icdispr0: RW<u32>,
pub icdispr1: RW<u32>,
pub icdispr2: RW<u32>,
unused11: [u32; 29],
/// Interrupt Clear-Pending Register
pub icdicpr0: RW<u32>,
pub icdicpr1: RW<u32>,
pub icdicpr2: RW<u32>,
unused12: [u32; 29],
/// Active Bit register
pub icdabr0: RW<u32>,
pub icdabr1: RW<u32>,
pub icdabr2: RW<u32>,
unused13: [u32; 61],
/// Interrupt Priority Register
pub icdipr: [RW<u32>; 24],
unused14: [u32; 232],
/// Interrupt Processor Targets Registers
pub icdiptr: [RW<u32>; 24],
unused15: [u32; 232],
/// Interrupt Configuration Registers
pub icdicfr: [RW<u32>; 6],
unused16: [u32; 58],
/// PPI Status Register
pub ppi_status: PpiStatus,
/// SPI Status Register 0
pub spi_status_0: RO<u32>,
/// SPI Status Register 1
pub spi_status_1: RO<u32>,
unused17: [u32; 125],
/// Software Generated Interrupt Register
pub icdsgir: ICDSGIR,
}
register_at!(RegisterBlock, 0xF8F00000, new);
register!(value_register, ValueRegister, RW, u32);
register_bits!(value_register, value, u32, 0, 31);
register!(scu_control, ScuControl, RW, u32);
register_bit!(scu_control, ic_standby_enable, 6);
register_bit!(scu_control, scu_standby_enable, 5);
@ -65,6 +158,17 @@ impl ScuControl {
}
}
register!(scu_config, ScuConfig, RO, u32);
register_bits!(scu_config, tag_ram_sizes, u8, 8, 15);
register_bits!(scu_config, cpus_smp, u8, 4, 7);
register_bits!(scu_config, cpu_number, u8, 0, 1);
register!(scu_cpu_power_status, SCUCPUPowerStatusRegister, RW, u32);
register_bits!(scu_cpu_power_status, cpu3_status, u8, 24, 25);
register_bits!(scu_cpu_power_status, cpu2_status, u8, 16, 17);
register_bits!(scu_cpu_power_status, cpu1_status, u8, 8, 9);
register_bits!(scu_cpu_power_status, cpu0_status, u8, 0, 1);
register!(scu_invalidate, ScuInvalidate, WO, u32);
register_bits!(scu_invalidate, cpu0_ways, u8, 0, 3);
register_bits!(scu_invalidate, cpu1_ways, u8, 4, 7);
@ -88,8 +192,71 @@ impl ScuInvalidate {
}
}
register!(value_register, ValueRegister, RW, u32);
register_bits!(value_register, value, u32, 0, 31);
register!(filtering_start_address, FilteringStartAddressRegister, RW, u32);
register_bits!(filtering_start_address, filtering_start_address, u32, 20, 31);
register_bits!(filtering_start_address, sbz, u32, 0, 19);
register!(filtering_end_address, FilteringEndAddressRegister, RW, u32);
register_bits!(filtering_end_address, filtering_end_address, u32, 20, 31);
register_bits!(filtering_end_address, sbz, u32, 0, 19);
register!(scu_access_control_sac, SCUAccessControlRegisterSAC, RW, u32);
register_bit!(scu_access_control_sac, cp_u3, 3);
register_bit!(scu_access_control_sac, cp_u2, 2);
register_bit!(scu_access_control_sac, cp_u1, 1);
register_bit!(scu_access_control_sac, cp_u0, 0);
register!(scu_non_secure_access_control, SCUNonSecureAccessControlRegister, RO, u32);
register_bits!(scu_non_secure_access_control, sbz, u32, 12, 31);
register_bit!(scu_non_secure_access_control, cpu3_global_timer, 11);
register_bit!(scu_non_secure_access_control, cpu2_global_timer, 10);
register_bit!(scu_non_secure_access_control, cpu1_global_timer, 9);
register_bit!(scu_non_secure_access_control, cpu0_global_timer, 8);
register_bit!(scu_non_secure_access_control, private_timers_for_cpu3, 7);
register_bit!(scu_non_secure_access_control, private_timers_for_cpu2, 6);
register_bit!(scu_non_secure_access_control, private_timers_for_cpu1, 5);
register_bit!(scu_non_secure_access_control, private_timers_for_cpu0, 4);
register_bit!(scu_non_secure_access_control, component_access_for_cpu3, 3);
register_bit!(scu_non_secure_access_control, component_access_for_cpu2, 2);
register_bit!(scu_non_secure_access_control, component_access_for_cpu1, 1);
register_bit!(scu_non_secure_access_control, component_access_for_cpu0, 0);
register!(iccicr, ICCICR, RW, u32);
register_bit!(iccicr, sbpr, 4);
register_bit!(iccicr, fiq_en, 3);
register_bit!(iccicr, ack_ctl, 2);
register_bit!(iccicr, enable_ns, 1);
register_bit!(iccicr, enable_s, 0);
register!(iccpmr, ICCPMR, RW, u32);
register_bits!(iccpmr, priority, u8, 0, 7);
register!(iccbpr, ICCBPR, RW, u32);
register_bits!(iccbpr, binary_point, u8, 0, 2);
register!(icciar, ICCIAR, RW, u32);
register_bits!(icciar, cpuid, u8, 10, 12);
register_bits!(icciar, ackintid, u32, 0, 9);
register!(icceoir, ICCEOIR, RW, u32);
register_bits!(icceoir, cpuid, u8, 10, 12);
register_bits!(icceoir, eoiintid, u32, 0, 9);
register!(iccrpr, ICCRPR, RW, u32);
register_bits!(iccrpr, priority, u8, 0, 7);
register!(icchpir, ICCHPIR, RW, u32);
register_bits!(icchpir, cpuid, u8, 10, 12);
register_bits!(icchpir, pendintid, u32, 0, 9);
register!(iccabpr, ICCABPR, RW, u32);
register_bits!(iccabpr, binary_point, u8, 0, 2);
register!(iccidr, ICCIDR, RO, u32);
register_bits!(iccidr, part_number, u32, 20, 31);
register_bits!(iccidr, architecture_version, u8, 16, 19);
register_bits!(iccidr, revision_number, u8, 12, 15);
register_bits!(iccidr, implementer, u32, 0, 11);
register!(global_timer_control, GlobalTimerControl, RW, u32);
register_bits!(global_timer_control, prescaler, u8, 8, 15);
@ -97,3 +264,58 @@ register_bit!(global_timer_control, auto_increment_mode, 3);
register_bit!(global_timer_control, irq_enable, 2);
register_bit!(global_timer_control, comp_enablea, 1);
register_bit!(global_timer_control, timer_enable, 0);
register!(global_timer_interrupt_status, GlobalTimerInterruptStatusRegister, RW, u32);
register_bit!(global_timer_interrupt_status, event_flag, 0);
register!(private_timer_control, PrivateTimerControlRegister, RW, u32);
register_bits!(private_timer_control, sbzp, u32, 16, 31);
register_bits!(private_timer_control, prescaler, u8, 8, 15);
register_bits!(private_timer_control, unk_sbzp, u8, 3, 7);
register_bit!(private_timer_control, irq_enable, 2);
register_bit!(private_timer_control, auto_reload, 1);
register_bit!(private_timer_control, timer_enable, 0);
register!(private_timer_interrupt_status, PrivateTimerInterruptStatusRegister, RW, u32);
register_bits!(private_timer_interrupt_status, unk_sbzp, u32, 1, 31);
register!(watchdog_control, WatchdogControlRegister, RW, u32);
register_bits!(watchdog_control, prescaler, u8, 8, 15);
register_bit!(watchdog_control, watchdog_mode, 3);
register_bit!(watchdog_control, it_enable, 2);
register_bit!(watchdog_control, auto_reload, 1);
register_bit!(watchdog_control, watchdog_enable, 0);
register!(watchdog_interrupt_status, WatchdogInterruptStatusRegister, RW, u32);
register_bit!(watchdog_interrupt_status, event_flag, 0);
register!(watchdog_reset_status, WatchdogResetStatusRegister, RW, u32);
register_bit!(watchdog_reset_status, reset_flag, 0);
register!(icddcr, ICDDCR, RW, u32);
register_bit!(icddcr, enable_non_secure, 1);
register_bit!(icddcr, enable_secure, 0);
register!(icdictr, ICDICTR, RO, u32);
register_bits!(icdictr, lspi, u8, 11, 15);
register_bit!(icdictr, security_extn, 10);
register_bits!(icdictr, sbz, u8, 8, 9);
register_bits!(icdictr, cpu_number, u8, 5, 7);
register_bits!(icdictr, it_lines_number, u8, 0, 4);
register!(icdiidr, ICDIIDR, RO, u32);
register_bits!(icdiidr, implementation_version, u8, 24, 31);
register_bits!(icdiidr, revision_number, u32, 12, 23);
register_bits!(icdiidr, implementer, u32, 0, 11);
register!(ppi_status, PpiStatus, RO, u32);
register_bits!(ppi_status, ppi_status, u8, 11, 15);
register_bits!(ppi_status, sbz, u32, 0, 10);
register!(icdsgir, ICDSGIR, RW, u32);
register_bits!(icdsgir, target_list_filter, u8, 24, 25);
register_bits!(icdsgir, cpu_target_list, u8, 16, 23);
register_bit!(icdsgir, satt, 15);
register_bits!(icdsgir, sbz, u32, 4, 14);
register_bits!(icdsgir, sgiintid, u8, 0, 3);

View File

@ -14,7 +14,7 @@ use nb;
/// Basic SDIO Struct with common low-level functions.
pub struct SDIO {
regs: &'static mut regs::RegisterBlock,
count_down: super::timer::global::CountDown,
count_down: super::timer::global::CountDown<Milliseconds>,
input_clk_hz: u32,
card_type: CardType,
card_detect: bool,

View File

@ -132,7 +132,7 @@ pub struct RegisterBlock {
pub can_rst_ctrl: RW<u32>,
pub i2c_rst_ctrl: RW<u32>,
pub uart_rst_ctrl: UartRstCtrl,
pub gpio_rst_ctrl: RW<u32>,
pub gpio_rst_ctrl: GpioRstCtrl,
pub lqspi_rst_ctrl: LqspiRstCtrl,
pub smc_rst_ctrl: RW<u32>,
pub ocm_rst_ctrl: RW<u32>,
@ -531,6 +531,20 @@ impl UartRstCtrl {
}
}
register!(gpio_rst_ctrl, GpioRstCtrl, RW, u32);
register_bit!(gpio_rst_ctrl, gpio_cpu1x_rst, 0);
register_at!(GpioRstCtrl, 0xF800022C, new);
impl GpioRstCtrl {
pub fn reset_gpio(&mut self) {
self.modify(|_, w|
w.gpio_cpu1x_rst(true)
);
self.modify(|_, w|
w.gpio_cpu1x_rst(false)
);
}
}
register!(lqspi_clk_ctrl, LqspiClkCtrl, RW, u32);
register_bit!(lqspi_clk_ctrl, clkact, 0);
register_bits_typed!(lqspi_clk_ctrl, src_sel, u8, PllSource, 4, 5);

View File

@ -8,3 +8,18 @@ impl core::ops::Add for Milliseconds {
Milliseconds(self.0 + rhs.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Microseconds(pub u64);
impl core::ops::Add for Microseconds {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Microseconds(self.0 + rhs.0)
}
}
pub trait TimeSource<U> {
fn now(&self) -> U;
}

View File

@ -1,9 +1,10 @@
use core::ops::Add;
use void::Void;
use libregister::{RegisterR, RegisterW};
use crate::{
clocks::Clocks,
mpcore,
time::Milliseconds,
time::{Milliseconds, Microseconds, TimeSource},
};
/// "uptime"
@ -79,41 +80,91 @@ impl GlobalTimer {
}
/// read with high precision
pub fn get_us(&self) -> u64 {
pub fn get_us(&self) -> Microseconds {
let prescaler = self.regs.global_timer_control.read().prescaler() as u64;
let clocks = Clocks::get();
1_000_000 * self.get_counter() * (prescaler + 1) / clocks.cpu_3x2x() as u64
Microseconds(1_000_000 * self.get_counter() * (prescaler + 1) / clocks.cpu_3x2x() as u64)
}
/// return a handle that has implements
/// `embedded_hal::timer::CountDown`
pub fn countdown(&self) -> CountDown {
pub fn countdown<U>(&self) -> CountDown<U>
where
Self: TimeSource<U>,
{
CountDown {
timer: self.clone(),
timeout: Milliseconds(0),
timeout: self.now(),
}
}
}
#[derive(Clone)]
pub struct CountDown {
timer: GlobalTimer,
timeout: Milliseconds,
impl TimeSource<Milliseconds> for GlobalTimer {
fn now(&self) -> Milliseconds {
self.get_time()
}
}
impl embedded_hal::timer::CountDown for CountDown {
type Time = Milliseconds;
impl TimeSource<Microseconds> for GlobalTimer {
fn now(&self) -> Microseconds {
self.get_us()
}
}
#[derive(Clone)]
pub struct CountDown<U> {
timer: GlobalTimer,
timeout: U,
}
/// embedded-hal async API
impl<U: Add<Output=U> + PartialOrd> embedded_hal::timer::CountDown for CountDown<U>
where
GlobalTimer: TimeSource<U>,
{
type Time = U;
fn start<T: Into<Self::Time>>(&mut self, count: T) {
self.timeout = self.timer.get_time() + count.into();
self.timeout = self.timer.now() + count.into();
}
fn wait(&mut self) -> nb::Result<(), Void> {
if self.timer.get_time() < self.timeout {
if self.timer.now() <= self.timeout {
Err(nb::Error::WouldBlock)
} else {
Ok(())
}
}
}
impl<U: PartialOrd> CountDown<U>
where
GlobalTimer: TimeSource<U>,
{
pub fn waiting(&self) -> bool {
self.timer.now() <= self.timeout
}
}
/// embedded-hal sync API
impl embedded_hal::blocking::delay::DelayMs<u64> for GlobalTimer {
fn delay_ms(&mut self, ms: u64) {
use embedded_hal::timer::CountDown;
let mut countdown = self.countdown::<Milliseconds>();
countdown.start(Milliseconds(ms));
nb::block!(countdown.wait()).unwrap();
}
}
/// embedded-hal sync API
impl embedded_hal::blocking::delay::DelayUs<u64> for GlobalTimer {
fn delay_us(&mut self, us: u64) {
use embedded_hal::timer::CountDown;
let mut countdown = self.countdown::<Microseconds>();
countdown.start(Microseconds(us));
nb::block!(countdown.wait()).unwrap();
}
}

View File

@ -7,6 +7,7 @@ edition = "2018"
[features]
target_zc706 = []
target_cora_z7_10 = []
power_saving = []
default = ["target_zc706"]
[dependencies]

View File

@ -33,3 +33,44 @@ pub fn dsb() {
pub fn isb() {
unsafe { llvm_asm!("isb" :::: "volatile") }
}
/// Enable IRQ
#[inline]
pub unsafe fn enable_irq() {
llvm_asm!("cpsie i":::: "volatile");
}
/// Disable IRQ, return if IRQ was originally enabled.
#[inline]
pub unsafe fn enter_critical() -> bool {
let mut cpsr: u32;
llvm_asm!(
"mrs $0, cpsr
cpsid i"
: "=r"(cpsr) ::: "volatile");
(cpsr & (1 << 7)) == 0
}
#[inline]
pub unsafe fn exit_critical(enable: bool) {
// https://stackoverflow.com/questions/40019929/temporarily-disable-interrupts-on-arm
let mask: u32 = if enable {
1 << 7
} else {
0
};
llvm_asm!(
"mrs r1, cpsr
bic r1, r1, $0
msr cpsr_c, r1"
:: "r"(mask) : "r1");
}
/// Exiting IRQ
#[inline]
pub unsafe fn exit_irq() {
llvm_asm!("
mrs r0, SPSR
msr CPSR, r0
" ::: "r0");
}

View File

@ -1,6 +1,7 @@
#![no_std]
#![feature(llvm_asm, global_asm)]
#![feature(never_type)]
#![feature(const_fn)]
extern crate alloc;
@ -10,9 +11,26 @@ pub mod cache;
pub mod mmu;
pub mod mutex;
pub mod sync_channel;
pub mod semaphore;
mod uncached;
mod fpu;
pub use uncached::UncachedSlice;
pub use fpu::enable_fpu;
global_asm!(include_str!("exceptions.s"));
#[inline]
pub fn spin_lock_yield() {
#[cfg(feature = "power_saving")]
asm::wfe();
}
#[inline]
pub fn notify_spin_lock() {
#[cfg(feature = "power_saving")]
{
asm::dsb();
asm::sev();
}
}

View File

@ -338,7 +338,7 @@ impl L1Table {
/* 0xfff00000 - 0xffffffff (256K OCM when mapped to high address space) */
self.direct_mapped_section(0xfff, L1Section {
global: true,
shareable: false,
shareable: true,
access: AccessPermissions::FullAccess,
tex: 0b100,
domain: 0,

View File

@ -1,20 +1,10 @@
use core::ops::{Deref, DerefMut};
use core::sync::atomic::{AtomicU32, Ordering};
use core::cell::UnsafeCell;
use super::asm::*;
/// [Power-saving features](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html)
#[inline]
fn wait_for_update() {
wfe();
}
/// [Power-saving features](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s03s02.html)
#[inline]
fn signal_update() {
dsb();
sev();
}
use super::{
spin_lock_yield, notify_spin_lock,
asm::{dmb, enter_critical, exit_critical}
};
const LOCKED: u32 = 1;
const UNLOCKED: u32 = 0;
@ -41,19 +31,26 @@ impl<T> Mutex<T> {
/// Lock the Mutex, blocks when already locked
pub fn lock(&self) -> MutexGuard<T> {
let mut irq = unsafe { enter_critical() };
while self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::Acquire) != UNLOCKED {
wait_for_update();
unsafe {
exit_critical(irq);
spin_lock_yield();
irq = enter_critical();
}
}
dmb();
MutexGuard { mutex: self }
MutexGuard { mutex: self, irq }
}
pub fn try_lock(&self) -> Option<MutexGuard<T>> {
let irq = unsafe { enter_critical() };
if self.locked.compare_and_swap(UNLOCKED, LOCKED, Ordering::Acquire) != UNLOCKED {
unsafe { exit_critical(irq) };
None
} else {
dmb();
Some(MutexGuard { mutex: self })
Some(MutexGuard { mutex: self, irq })
}
}
@ -61,7 +58,7 @@ impl<T> Mutex<T> {
dmb();
self.locked.store(UNLOCKED, Ordering::Release);
signal_update();
notify_spin_lock();
}
}
@ -69,6 +66,7 @@ impl<T> Mutex<T> {
/// `Deref`/`DerefMutx`
pub struct MutexGuard<'a, T> {
mutex: &'a Mutex<T>,
irq: bool,
}
impl<'a, T> Deref for MutexGuard<'a, T> {
@ -88,5 +86,6 @@ impl<'a, T> DerefMut for MutexGuard<'a, T> {
impl<'a, T> Drop for MutexGuard<'a, T> {
fn drop(&mut self) {
self.mutex.unlock();
unsafe { exit_critical(self.irq) };
}
}

View File

@ -0,0 +1,71 @@
use super::{spin_lock_yield, notify_spin_lock};
use core::{
task::{Context, Poll},
pin::Pin,
future::Future,
sync::atomic::{AtomicI32, Ordering}
};
pub struct Semaphore {
value: AtomicI32,
max: i32
}
impl Semaphore {
pub fn new(value: i32, max: i32) -> Self {
Semaphore { value: AtomicI32::new(value), max}
}
pub fn try_wait(&self) -> Option<()> {
loop {
let value = self.value.load(Ordering::Relaxed);
if value > 0 {
if self.value.compare_and_swap(value, value - 1, Ordering::SeqCst) == value {
return Some(());
}
} else {
return None;
}
}
}
pub fn wait(&self) {
while self.try_wait().is_none() {
spin_lock_yield();
}
}
pub async fn async_wait(&self) {
struct Fut<'a>(&'a Semaphore);
impl Future for Fut<'_> {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.0.try_wait() {
Some(_) => Poll::Ready(()),
None => {
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
}
Fut(&self).await
}
pub fn signal(&self) {
loop {
let value = self.value.load(Ordering::Relaxed);
if value < self.max {
if self.value.compare_and_swap(value, value + 1, Ordering::SeqCst) == value {
notify_spin_lock();
return;
}
} else {
return;
}
}
}
}

View File

@ -1,115 +1,73 @@
use core::{
future::Future,
pin::Pin,
ptr::null_mut,
sync::atomic::{AtomicPtr, Ordering},
future::Future,
ptr::drop_in_place,
sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
task::{Context, Poll},
};
use alloc::{
boxed::Box,
sync::Arc,
vec::Vec,
};
use super::asm::*;
use alloc::boxed::Box;
use super::{spin_lock_yield, notify_spin_lock};
type Channel<T> = Vec<AtomicPtr<T>>;
/// Create a bounded channel
///
/// Returns `(tx, rx)` where one should be used one the local core,
/// and the other is to be shared with another core.
pub fn sync_channel<T>(bound: usize) -> (Sender<T>, Receiver<T>) {
// allow for bound=0
let len = bound + 1;
let mut channel = Vec::with_capacity(len);
for _ in 0..len {
channel.push(AtomicPtr::default());
}
let channel = Arc::new(channel);
let sender = Sender {
channel: channel.clone(),
pos: 0,
};
let receiver = Receiver {
channel: channel,
pos: 0,
};
(sender, receiver)
pub struct Sender<'a, T> where T: Clone {
list: &'a [AtomicPtr<T>],
write: &'a AtomicUsize,
read: &'a AtomicUsize,
}
/// Sending half of a channel
pub struct Sender<T> {
channel: Arc<Channel<T>>,
pos: usize,
pub struct Receiver<'a, T> where T: Clone {
list: &'a [AtomicPtr<T>],
write: &'a AtomicUsize,
read: &'a AtomicUsize,
}
impl<T> Sender<T> {
/// Blocking send
pub fn send<B: Into<Box<T>>>(&mut self, content: B) {
let ptr = Box::into_raw(content.into());
let entry = &self.channel[self.pos];
// try to write the new pointer if the current pointer is
// NULL, retrying while it is not NULL
while entry.compare_and_swap(null_mut(), ptr, Ordering::Acquire) != null_mut() {
// power-saving
wfe();
}
dsb();
// wake power-saving receivers
sev();
// advance
self.pos += 1;
// wrap
if self.pos >= self.channel.len() {
self.pos = 0;
}
impl<'a, T> Sender<'a, T> where T: Clone {
pub const fn new(list: &'static [AtomicPtr<T>], write: &'static AtomicUsize, read: &'static AtomicUsize) -> Self {
Sender {list, write, read}
}
/// Non-blocking send, handing you back ownership of the content on **failure**
pub fn try_send<B: Into<Box<T>>>(&mut self, content: B) -> Option<Box<T>> {
let ptr = Box::into_raw(content.into());
let entry = &self.channel[self.pos];
// try to write the new pointer if the current pointer is
// NULL
if entry.compare_and_swap(null_mut(), ptr, Ordering::Acquire) == null_mut() {
dsb();
// wake power-saving receivers
sev();
// advance
self.pos += 1;
// wrap
if self.pos >= self.channel.len() {
self.pos = 0;
}
// success
None
pub fn try_send<B: Into<Box<T>>>(&mut self, content: B) -> Result<(), B> {
let write = self.write.load(Ordering::Relaxed);
if (write + 1) % self.list.len() == self.read.load(Ordering::Acquire) {
Err(content)
} else {
let content = unsafe { Box::from_raw(ptr) };
// failure
Some(content)
let ptr = Box::into_raw(content.into());
let entry = &self.list[write];
let prev = entry.swap(ptr, Ordering::Relaxed);
// we allow other end get it first
self.write.store((write + 1) % self.list.len(), Ordering::Release);
notify_spin_lock();
if !prev.is_null() {
unsafe {
drop_in_place(prev);
}
}
Ok(())
}
}
pub fn send<B: Into<Box<T>>>(&mut self, content: B) {
let mut content = content;
while let Err(back) = self.try_send(content) {
content = back;
spin_lock_yield();
}
}
pub async fn async_send<B: Into<Box<T>>>(&mut self, content: B) {
struct Send<'a, T> {
sender: &'a mut Sender<T>,
content: Option<Box<T>>,
struct Send<'a, 'b, T> where T: Clone, 'b: 'a {
sender: &'a mut Sender<'b, T>,
content: Result<(), Box<T>>,
}
impl<T> Future for Send<'_, T> {
impl<T> Future for Send<'_, '_, T> where T: Clone {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.content.take() {
Some(content) => {
if let Some(content) = self.sender.try_send(content) {
match core::mem::replace(&mut self.content, Ok(())) {
Err(content) => {
if let Err(content) = self.sender.try_send(content) {
// failure
self.content = Some(content);
self.content = Err(content);
cx.waker().wake_by_ref();
Poll::Pending
} else {
@ -117,93 +75,80 @@ impl<T> Sender<T> {
Poll::Ready(())
}
}
None => panic!("Send future polled after success"),
Ok(_) => panic!("Send future polled after success"),
}
}
}
Send {
sender: self,
content: Some(content.into()),
content: Err(content.into()),
}.await
}
}
/// Receiving half of a channel
pub struct Receiver<T> {
channel: Arc<Channel<T>>,
pos: usize,
}
impl<T> Receiver<T> {
/// Blocking receive
pub fn recv(&mut self) -> Box<T> {
let entry = &self.channel[self.pos];
loop {
dmb();
let ptr = entry.swap(null_mut(), Ordering::Release);
if ptr != null_mut() {
dsb();
// wake power-saving senders
sev();
let content = unsafe { Box::from_raw(ptr) };
// advance
self.pos += 1;
// wrap
if self.pos >= self.channel.len() {
self.pos = 0;
}
return content;
/// free all items in the queue. It is the user's responsibility to
/// ensure no reader is trying to copy the data.
pub unsafe fn drop_elements(&mut self) {
for v in self.list.iter() {
let original = v.swap(core::ptr::null_mut(), Ordering::Relaxed);
if !original.is_null() {
drop_in_place(original);
}
// power-saving
wfe();
}
}
/// Non-blocking receive
pub fn try_recv(&mut self) -> Option<Box<T>> {
let entry = &self.channel[self.pos];
/// Reset the `sync_channel`, *forget* all items in the queue. Affects both the sender and
/// receiver.
pub unsafe fn reset(&mut self) {
self.write.store(0, Ordering::Relaxed);
self.read.store(0, Ordering::Relaxed);
for v in self.list.iter() {
v.store(core::ptr::null_mut(), Ordering::Relaxed);
}
}
}
dmb();
let ptr = entry.swap(null_mut(), Ordering::Release);
if ptr != null_mut() {
dsb();
// wake power-saving senders
sev();
impl<'a, T> Receiver<'a, T> where T: Clone {
pub const fn new(list: &'static [AtomicPtr<T>], write: &'static AtomicUsize, read: &'static AtomicUsize) -> Self {
Receiver {list, write, read}
}
let content = unsafe { Box::from_raw(ptr) };
// advance
self.pos += 1;
// wrap
if self.pos >= self.channel.len() {
self.pos = 0;
}
Some(content)
pub fn try_recv(&mut self) -> Result<T, ()> {
let read = self.read.load(Ordering::Relaxed);
if read == self.write.load(Ordering::Acquire) {
Err(())
} else {
None
let entry = &self.list[read];
let data = unsafe {
// we cannot deallocate the box
Box::leak(Box::from_raw(entry.load(Ordering::Relaxed)))
};
let result = data.clone();
self.read.store((read + 1) % self.list.len(), Ordering::Release);
notify_spin_lock();
Ok(result)
}
}
pub async fn async_recv(&mut self) -> Box<T> {
struct Recv<'a, T> {
receiver: &'a mut Receiver<T>,
pub fn recv(&mut self) -> T {
loop {
if let Ok(data) = self.try_recv() {
return data;
}
spin_lock_yield();
}
}
pub async fn async_recv(&mut self) -> T {
struct Recv<'a, 'b, T> where T: Clone, 'b: 'a {
receiver: &'a mut Receiver<'b, T>,
}
impl<T> Future for Recv<'_, T> {
type Output = Box<T>;
impl<T> Future for Recv<'_, '_, T> where T: Clone {
type Output = T;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Some(content) = self.receiver.try_recv() {
if let Ok(content) = self.receiver.try_recv() {
Poll::Ready(content)
} else {
cx.waker().wake_by_ref();
@ -218,10 +163,26 @@ impl<T> Receiver<T> {
}
}
impl<T> Iterator for Receiver<T> {
type Item = Box<T>;
impl<'a, T> Iterator for Receiver<'a, T> where T: Clone {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
Some(self.recv())
}
}
#[macro_export]
/// Macro for initializing the sync_channel with static buffer and indexes.
/// Note that this requires `#![feature(const_in_array_repeat_expressions)]`
macro_rules! sync_channel {
($t: ty, $cap: expr) => {
{
use core::sync::atomic::{AtomicUsize, AtomicPtr};
use $crate::sync_channel::{Sender, Receiver};
static LIST: [AtomicPtr<$t>; $cap + 1] = [AtomicPtr::new(core::ptr::null_mut()); $cap + 1];
static WRITE: AtomicUsize = AtomicUsize::new(0);
static READ: AtomicUsize = AtomicUsize::new(0);
(Sender::new(&LIST, &WRITE, &READ), Receiver::new(&LIST, &WRITE, &READ))
}
};
}

View File

@ -9,8 +9,10 @@ edition = "2018"
target_zc706 = ["libboard_zynq/target_zc706"]
target_cora_z7_10 = ["libboard_zynq/target_cora_z7_10"]
panic_handler = []
dummy_irq_handler = []
alloc_core = []
default = ["panic_handler"]
default = ["panic_handler", "dummy_irq_handler"]
[dependencies]
r0 = "1"

View File

@ -53,6 +53,7 @@ pub unsafe extern "C" fn ReservedException() {
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
#[cfg(feature = "dummy_irq_handler")]
pub unsafe extern "C" fn IRQ() {
stdio::drop_uart();
println!("IRQ");

View File

@ -4,7 +4,7 @@ use libregister::{
VolatileCell,
RegisterR, RegisterW, RegisterRW,
};
use libcortex_a9::{asm, regs::*, cache, mmu};
use libcortex_a9::{asm, regs::*, cache, mmu, spin_lock_yield, notify_spin_lock};
use libboard_zynq::{slcr, mpcore};
extern "C" {
@ -29,7 +29,7 @@ pub unsafe extern "C" fn Reset() -> ! {
}
1 => {
while !CORE1_ENABLED.get() {
asm::wfe();
spin_lock_yield();
}
SP.write(&mut __stack1_start as *mut _ as u32);
boot_core1();
@ -57,6 +57,7 @@ unsafe fn boot_core0() -> ! {
asm::dmb();
asm::dsb();
asm::enable_irq();
main_core0();
panic!("return from main");
});
@ -77,6 +78,7 @@ unsafe fn boot_core1() -> ! {
asm::dmb();
asm::dsb();
asm::enable_irq();
main_core1();
panic!("return from main_core1");
});
@ -142,6 +144,7 @@ impl Core1 {
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(false));
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(false));
});
notify_spin_lock();
Core1 {}
}
@ -149,6 +152,8 @@ impl Core1 {
pub fn disable(&self) {
unsafe {
CORE1_ENABLED.set(false);
cache::dccmvac(&CORE1_ENABLED as *const _ as usize);
asm::dsb();
}
self.restart();
}

View File

@ -1,55 +1,97 @@
use alloc::alloc::Layout;
use core::alloc::GlobalAlloc;
use core::ptr::NonNull;
use alloc::alloc::Layout;
use libcortex_a9::{
mutex::Mutex,
regs::MPIDR
};
use libregister::RegisterR;
use linked_list_allocator::Heap;
use libcortex_a9::mutex::Mutex;
#[cfg(not(feature = "alloc_core"))]
use libboard_zynq::ddr::DdrRam;
#[global_allocator]
static ALLOCATOR: CortexA9Alloc = CortexA9Alloc(Mutex::new(Heap::empty()));
static ALLOCATOR: CortexA9Alloc = CortexA9Alloc(
Mutex::new(Heap::empty()),
Mutex::new(Heap::empty()),
);
/// LockedHeap doesn't lock properly
struct CortexA9Alloc(Mutex<Heap>);
struct CortexA9Alloc(Mutex<Heap>, Mutex<Heap>);
unsafe impl Sync for CortexA9Alloc {}
unsafe impl GlobalAlloc for CortexA9Alloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
self.0.lock()
.allocate_first_fit(layout)
.ok()
.map_or(0 as *mut u8, |allocation| allocation.as_ptr())
if cfg!(not(feature = "alloc_core")) || MPIDR.read().cpu_id() == 0 {
&self.0
} else {
&self.1
}
.lock()
.allocate_first_fit(layout)
.ok()
.map_or(0 as *mut u8, |allocation| allocation.as_ptr())
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
self.0.lock()
.deallocate(NonNull::new_unchecked(ptr), layout)
if cfg!(not(feature = "alloc_core")) || MPIDR.read().cpu_id() == 0 {
&self.0
} else {
&self.1
}
.lock()
.deallocate(NonNull::new_unchecked(ptr), layout)
}
}
#[cfg(not(feature = "alloc_core"))]
pub fn init_alloc_ddr(ddr: &mut DdrRam) {
unsafe {
ALLOCATOR.0.lock()
ALLOCATOR
.0
.lock()
.init(ddr.ptr::<u8>() as usize, ddr.size());
}
}
extern "C" {
static __heap_start: usize;
static __heap_end: usize;
static __heap0_start: usize;
static __heap0_end: usize;
#[cfg(feature = "alloc_core")]
static __heap1_start: usize;
#[cfg(feature = "alloc_core")]
static __heap1_end: usize;
}
pub fn init_alloc_linker() {
pub fn init_alloc_core0() {
unsafe {
let start = &__heap_start as *const usize as usize;
let end = &__heap_end as *const usize as usize;
ALLOCATOR.0.lock()
.init(start, end - start);
let start = &__heap0_start as *const usize as usize;
let end = &__heap0_end as *const usize as usize;
ALLOCATOR.0.lock().init(start, end - start);
}
}
#[cfg(feature = "alloc_core")]
pub fn init_alloc_core1() {
unsafe {
let start = &__heap1_start as *const usize as usize;
let end = &__heap1_end as *const usize as usize;
ALLOCATOR.1.lock().init(start, end - start);
}
}
#[alloc_error_handler]
fn alloc_error(_: core::alloc::Layout) -> ! {
panic!("alloc_error")
fn alloc_error(layout: core::alloc::Layout) -> ! {
let id = MPIDR.read().cpu_id();
let used = if cfg!(not(feature = "alloc_core")) || id == 0 {
ALLOCATOR.0.lock().used()
} else {
ALLOCATOR.1.lock().used()
};
panic!(
"Core {} alloc_error, layout: {:?}, used memory: {}",
id,
layout,
used
);
}

View File

@ -12,7 +12,6 @@ stdenv.mkDerivation {
buildInputs = (with rustPlatform.rust; [
rustc cargo
cargo-xbuild rustcSrc
gcc
]) ++ (with pkgs; [ openocd gdb ]);
# Set Environment Variables
@ -20,6 +19,6 @@ stdenv.mkDerivation {
XARGO_RUST_SRC = "${rustcSrc}/src";
shellHook = ''
echo "Run 'cargo xbuild --release' to build."
echo "Run 'cargo xbuild --release -p experiments' to build."
'';
}