Compare commits

..

2 Commits
master ... gic

Author SHA1 Message Date
Astro 96b16d2c6c gic: start implementation 2020-07-31 00:01:48 +02:00
pca006132 d18021fc5c libboard_zynq/mpcore: added generated register definitions 2020-07-29 15:54:57 +08:00
31 changed files with 342 additions and 1138 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.4 (registry+https://github.com/rust-lang/crates.io-index)",
"linked_list_allocator 0.8.3 (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.4"
version = "0.8.3"
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.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e70e46c13c0e8374c26cec5752e3347ca1087d9711de8f45aa513a7700efd73d"
"checksum linked_list_allocator 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d6b60501dd4c850950bb43f970d544f6ce04e0ca021da2db2538fbe9d923f19e"
"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,7 +1,7 @@
# Build
```shell
nix-shell --command "cargo xbuild --release -p experiments"
nix-shell --command "cargo xbuild --release"
```
Currently the ELF output is placed at `target/armv7-none-eabihf/release/experiments`
@ -41,7 +41,7 @@ Proceed using gdb with `load`, `c`
### Running on the ZC706
```shell
nix-shell --command "cargo xbuild --release -p experiments"
nix-shell --command "cargo xbuild --release"
cd openocd
openocd -f zc706.cfg
```
@ -57,26 +57,23 @@ openocd -f cora-z7-10.cfg
### Loading a bitstream into volatile memory
```shell
openocd -f zc706.cfg -c "pld load 0 blinker_migen.bit; exit"
openocd -f zc706.cfg -c "pld load 0 blinker_migen.bit; exit"
```
### Development Process
Clone this repo onto your development/build machine and the raspberry pi that controls the Xilinx 7000 board
On the dev machine, the below script builds zc706 and secure copies it to the target pi (in your pi $HOME directory):
On the dev machine, the below script builds zc706 and secure copies it to the target pi (in your pi $HOME directory)
```shell
cd ~/zynq-rs
./build.sh $your_user_or_ssh_id
cd ~/zc706
./build.sh $your_user/ssh_id
```
On the pi, we need an information rich environment that includes a relatively reliable `gdb` experience (that includes `ctrl-p` and `ctrl-n` command history that persists across `cgdb` executions), run:
```shell
ssh pi4
cd zynq-rs
# For ZC706, run:
./tmux.sh 0
# For Cora Z7, run:
cd zc706
./tmux.sh
```

View File

@ -1 +1 @@
nix-shell --command "cargo xbuild --release -p experiments" && scp -C target/armv7-none-eabihf/release/experiments $1@rpi-4.m-labs.hk:/home/$1/zc706/zc706.elf
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

View File

@ -49,7 +49,7 @@ let
name = "${crate}";
src = ./.;
crateSubdir = crate;
cargoSha256 = "1c1qpg9by8bg93yhgllb5xs155g27qmh99pbrb681wazm8k7nwim";
cargoSha256 = "0xlynsr94dyv0g41qwk5490w3wnzd5g70msaih6mcbgr3v4s2q34";
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", default-features = false, features = ["panic_handler"]}
libsupport_zynq = { path = "../libsupport_zynq" }
libasync = { path = "../libasync" }

View File

@ -32,23 +32,19 @@ SECTIONS
*(.bss .bss.*);
. = ALIGN(4);
__bss_end = .;
} > OCM3
} > OCM
.stack1 (NOLOAD) : ALIGN(8) {
__stack1_end = .;
. += 0x200;
__stack1_start = .;
} > OCM3
} > OCM
.stack0 (NOLOAD) : ALIGN(8) {
__stack0_end = .;
. = ORIGIN(OCM3) + LENGTH(OCM3) - 8;
. = ORIGIN(OCM) + LENGTH(OCM) - 8;
__stack0_start = .;
/* unused heap0 to prevent the linker from complaining*/
__heap0_start = .;
__heap0_end = .;
} > OCM3
} > OCM
/DISCARD/ :
{

View File

@ -1,7 +1,6 @@
#![no_std]
#![no_main]
#![feature(const_in_array_repeat_expressions)]
#![feature(naked_functions)]
extern crate alloc;
@ -15,9 +14,8 @@ use libboard_zynq::{
self as zynq,
clocks::source::{ArmPll, ClockSource, IoPll},
clocks::Clocks,
print, println, stdio,
mpcore,
gic,
print, println,
sdio::sd_card::SdCard,
smoltcp::{
iface::{EthernetInterfaceBuilder, NeighborCache, Routes},
time::Instant,
@ -25,70 +23,24 @@ use libboard_zynq::{
},
time::Milliseconds,
};
#[cfg(feature = "target_zc706")]
use libboard_zynq::ps7_init;
use libcortex_a9::{
mutex::Mutex,
sync_channel::{Sender, Receiver},
sync_channel,
regs::{MPIDR, SP},
spin_lock_yield, notify_spin_lock,
asm
};
use libregister::{RegisterR, RegisterW};
use libregister::RegisterR;
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();
@ -131,6 +83,26 @@ pub fn main_core0() {
clocks.cpu_1x()
);
// commented out due to OCM full
// 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 {
@ -188,54 +160,20 @@ pub fn main_core0() {
flash = flash_io.stop();
}
boot::Core1::start(false);
let core1 = boot::Core1::start(false);
let core1_req = unsafe { &mut CORE1_REQ.0 };
let core1_res = unsafe { &mut CORE1_RES.1 };
let (mut core1_req, rx) = sync_channel!(usize, 10);
*CORE1_REQ.lock() = Some(rx);
let (tx, mut core1_res) = sync_channel!(usize, 10);
*CORE1_RES.lock() = Some(tx);
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);
}
});
unsafe {
core1_req.drop_elements();
}
// Test I2C
#[cfg(feature = "target_zc706")]
{
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!("");
}
core1.disable();
let eth = zynq::eth::Eth::default(HWADDR.clone());
println!("Eth on");
@ -260,6 +198,9 @@ pub fn main_core0() {
.neighbor_cache(neighbor_cache)
.finalize();
#[cfg(feature = "target_zc706")]
ps7_init::report_differences();
Sockets::init(32);
const TCP_PORT: u16 = 19;
@ -326,15 +267,24 @@ 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 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 };
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();
for i in req {
res.send(i * i);

View File

@ -1,8 +1,5 @@
use core::{
marker::PhantomData,
ops::{Deref, DerefMut},
};
use log::{debug, info, warn, error};
use core::ops::{Deref, DerefMut};
use log::{error, info, warn};
use libregister::*;
use super::slcr;
use super::clocks::Clocks;
@ -45,109 +42,14 @@ impl DerefMut for Buffer {
}
}
/// 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> {
pub struct Eth<'r, RX, TX> {
rx: RX,
tx: TX,
inner: EthInner<GEM>,
inner: EthInner<'r>,
phy: Phy,
}
impl Eth<Gem0, (), ()> {
impl<'r> Eth<'r, (), ()> {
pub fn default(macaddr: [u8; 6]) -> Self {
slcr::RegisterBlock::unlocked(|slcr| {
// Manual example: 0x0000_1280
@ -280,23 +182,22 @@ impl Eth<Gem0, (), ()> {
}
pub fn gem0(macaddr: [u8; 6]) -> Self {
Self::new(macaddr)
Self::setup_gem0_clock(TX_1000);
let regs = regs::RegisterBlock::gem0();
Self::from_regs(regs, macaddr)
}
}
impl Eth<Gem1, (), ()> {
pub fn gem1(macaddr: [u8; 6]) -> Self {
Self::new(macaddr)
Self::setup_gem1_clock(TX_1000);
let regs = regs::RegisterBlock::gem1();
Self::from_regs(regs, 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 {
gem: PhantomData,
regs,
link: None,
};
inner.init();
@ -315,8 +216,54 @@ impl<GEM: Gem> Eth<GEM, (), ()> {
}
}
impl<GEM: Gem, RX, TX> Eth<GEM, RX, TX> {
pub fn start_rx(self, rx_size: usize) -> Eth<GEM, rx::DescList, TX> {
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> {
let new_self = Eth {
rx: rx::DescList::new(rx_size),
tx: self.tx,
@ -325,17 +272,17 @@ impl<GEM: Gem, RX, TX> Eth<GEM, RX, TX> {
};
let list_addr = new_self.rx.list_addr();
assert!(list_addr & 0b11 == 0);
GEM::regs().rx_qbar.write(
new_self.inner.regs.rx_qbar.write(
regs::RxQbar::zeroed()
.rx_q_baseaddr(list_addr >> 2)
);
GEM::regs().net_ctrl.modify(|_, w|
new_self.inner.regs.net_ctrl.modify(|_, w|
w.rx_en(true)
);
new_self
}
pub fn start_tx(self, tx_size: usize) -> Eth<GEM, RX, tx::DescList> {
pub fn start_tx(self, tx_size: usize) -> Eth<'r, RX, tx::DescList> {
let new_self = Eth {
rx: self.rx,
tx: tx::DescList::new(tx_size),
@ -344,23 +291,23 @@ impl<GEM: Gem, RX, TX> Eth<GEM, RX, TX> {
};
let list_addr = &new_self.tx.list_addr();
assert!(list_addr & 0b11 == 0);
GEM::regs().tx_qbar.write(
new_self.inner.regs.tx_qbar.write(
regs::TxQbar::zeroed()
.tx_q_baseaddr(list_addr >> 2)
);
GEM::regs().net_ctrl.modify(|_, w|
new_self.inner.regs.net_ctrl.modify(|_, w|
w.tx_en(true)
);
new_self
}
}
impl<GEM: Gem, TX> Eth<GEM, rx::DescList, TX> {
impl<'r, TX> Eth<'r, rx::DescList, TX> {
pub fn recv_next<'s: 'p, 'p>(&'s mut self) -> Result<Option<rx::PktRef<'p>>, rx::Error> {
let status = GEM::regs().rx_status.read();
let status = self.inner.regs.rx_status.read();
if status.hresp_not_ok() {
// Clear
GEM::regs().rx_status.write(
self.inner.regs.rx_status.write(
regs::RxStatus::zeroed()
.hresp_not_ok(true)
);
@ -368,7 +315,7 @@ impl<GEM: Gem, TX> Eth<GEM, rx::DescList, TX> {
}
if status.rx_overrun() {
// Clear
GEM::regs().rx_status.write(
self.inner.regs.rx_status.write(
regs::RxStatus::zeroed()
.rx_overrun(true)
);
@ -376,7 +323,7 @@ impl<GEM: Gem, TX> Eth<GEM, rx::DescList, TX> {
}
if status.buffer_not_avail() {
// Clear
GEM::regs().rx_status.write(
self.inner.regs.rx_status.write(
regs::RxStatus::zeroed()
.buffer_not_avail(true)
);
@ -388,7 +335,7 @@ impl<GEM: Gem, TX> Eth<GEM, rx::DescList, TX> {
match result {
Ok(None) => {
// No packet, clear status bit
GEM::regs().rx_status.write(
self.inner.regs.rx_status.write(
regs::RxStatus::zeroed()
.frame_recd(true)
);
@ -403,13 +350,13 @@ impl<GEM: Gem, TX> Eth<GEM, rx::DescList, TX> {
}
}
impl<GEM: Gem, RX> Eth<GEM, RX, tx::DescList> {
impl<'r, RX> Eth<'r, RX, tx::DescList> {
pub fn send<'s: 'p, 'p>(&'s mut self, length: usize) -> Option<tx::PktRef<'p>> {
self.tx.send(GEM::regs(), length)
self.tx.send(self.inner.regs, length)
}
}
impl<'a, GEM: Gem> smoltcp::phy::Device<'a> for &mut Eth<GEM, rx::DescList, tx::DescList> {
impl<'r, 'a> smoltcp::phy::Device<'a> for &mut Eth<'r, rx::DescList, tx::DescList> {
type RxToken = rx::PktRef<'a>;
type TxToken = tx::Token<'a>;
@ -433,7 +380,7 @@ impl<'a, GEM: Gem> smoltcp::phy::Device<'a> for &mut Eth<GEM, rx::DescList, tx::
match self.rx.recv_next() {
Ok(Some(pktref)) => {
let tx_token = tx::Token {
regs: GEM::regs(),
regs: self.inner.regs,
desc_list: &mut self.tx,
};
Some((pktref, tx_token))
@ -451,32 +398,33 @@ impl<'a, GEM: Gem> smoltcp::phy::Device<'a> for &mut Eth<GEM, rx::DescList, tx::
fn transmit(&'a mut self) -> Option<Self::TxToken> {
Some(tx::Token {
regs: GEM::regs(),
regs: self.inner.regs,
desc_list: &mut self.tx,
})
}
}
struct EthInner<GEM: Gem> {
gem: PhantomData<GEM>,
struct EthInner<'r> {
regs: &'r mut regs::RegisterBlock,
link: Option<phy::Link>,
}
impl<GEM: Gem> EthInner<GEM> {
impl<'r> EthInner<'r> {
fn init(&mut self) {
// Clear the Network Control register.
GEM::regs().net_ctrl.write(regs::NetCtrl::zeroed());
GEM::regs().net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true));
self.regs.net_ctrl.write(regs::NetCtrl::zeroed());
self.regs.net_ctrl.write(regs::NetCtrl::zeroed().clear_stat_regs(true));
// Clear the Status registers.
GEM::regs().rx_status.write(
self.regs.rx_status.write(
regs::RxStatus::zeroed()
.buffer_not_avail(true)
.frame_recd(true)
.rx_overrun(true)
.hresp_not_ok(true)
);
GEM::regs().tx_status.write(
self.regs.tx_status.write(
regs::TxStatus::zeroed()
.used_bit_read(true)
.collision(true)
@ -490,7 +438,7 @@ impl<GEM: Gem> EthInner<GEM> {
.hresp_not_ok(true)
);
// Disable all interrupts.
GEM::regs().intr_dis.write(
self.regs.intr_dis.write(
regs::IntrDis::zeroed()
.mgmt_done(true)
.rx_complete(true)
@ -520,10 +468,10 @@ impl<GEM: Gem> EthInner<GEM> {
.tsu_sec_incr(true)
);
// Clear the buffer queues.
GEM::regs().rx_qbar.write(
self.regs.rx_qbar.write(
regs::RxQbar::zeroed()
);
GEM::regs().tx_qbar.write(
self.regs.tx_qbar.write(
regs::TxQbar::zeroed()
);
}
@ -532,7 +480,7 @@ impl<GEM: Gem> EthInner<GEM> {
let clocks = Clocks::get();
let mdc_clk_div = (clocks.cpu_1x() / MAX_MDC) + 1;
GEM::regs().net_cfg.write(
self.regs.net_cfg.write(
regs::NetCfg::zeroed()
.full_duplex(true)
.gige_en(true)
@ -557,17 +505,17 @@ impl<GEM: Gem> EthInner<GEM> {
(u32::from(macaddr[3]) << 16) |
(u32::from(macaddr[4]) << 8) |
u32::from(macaddr[5]);
GEM::regs().spec_addr1_top.write(
self.regs.spec_addr1_top.write(
regs::SpecAddrTop::zeroed()
.addr_msbs(macaddr_msbs)
);
GEM::regs().spec_addr1_bot.write(
self.regs.spec_addr1_bot.write(
regs::SpecAddrBot::zeroed()
.addr_lsbs(macaddr_lsbs)
);
GEM::regs().dma_cfg.write(
self.regs.dma_cfg.write(
regs::DmaCfg::zeroed()
// 1536 bytes
.ahb_mem_rx_buf_size((MTU >> 6) as u8)
@ -583,7 +531,7 @@ impl<GEM: Gem> EthInner<GEM> {
.ahb_fixed_burst_len(0x10)
);
GEM::regs().net_ctrl.write(
self.regs.net_ctrl.write(
regs::NetCtrl::zeroed()
.mgmt_port_en(true)
);
@ -591,7 +539,7 @@ impl<GEM: Gem> EthInner<GEM> {
fn wait_phy_idle(&self) {
while !GEM::regs().net_status.read().phy_mgmt_idle() {}
while !self.regs.net_status.read().phy_mgmt_idle() {}
}
@ -610,15 +558,17 @@ impl<GEM: Gem> EthInner<GEM> {
Some(link) => {
info!("eth: got {:?}", link);
use phy::{LinkDuplex::Full, LinkSpeed::*};
use phy::LinkSpeed::*;
let txclock = match link.speed {
S10 => TX_10,
S100 => TX_100,
S1000 => TX_1000,
};
GEM::setup_clock(txclock);
GEM::regs().net_cfg.modify(|_, w| w
.full_duplex(link.duplex == Full)
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)
.gige_en(link.speed == S1000)
.speed(link.speed != S10)
);
@ -637,10 +587,10 @@ impl<GEM: Gem> EthInner<GEM> {
}
}
impl<GEM: Gem> PhyAccess for EthInner<GEM> {
impl<'r> PhyAccess for EthInner<'r> {
fn read_phy(&mut self, addr: u8, reg: u8) -> u16 {
self.wait_phy_idle();
GEM::regs().phy_maint.write(
self.regs.phy_maint.write(
regs::PhyMaint::zeroed()
.clause_22(true)
.operation(regs::PhyOperation::Read)
@ -649,12 +599,12 @@ impl<GEM: Gem> PhyAccess for EthInner<GEM> {
.must_10(0b10)
);
self.wait_phy_idle();
GEM::regs().phy_maint.read().data()
self.regs.phy_maint.read().data()
}
fn write_phy(&mut self, addr: u8, reg: u8, data: u16) {
self.wait_phy_idle();
GEM::regs().phy_maint.write(
self.regs.phy_maint.write(
regs::PhyMaint::zeroed()
.clause_22(true)
.operation(regs::PhyOperation::Write)

View File

@ -0,0 +1,59 @@
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,36 +39,43 @@ pub struct Phy {
#[derive(Clone, Copy)]
pub enum PhyDevice {
Marvell88E1116R,
Marvel88E1116R,
Rtl8211E,
}
const OUI_MARVELL: u32 = 0x005043;
const OUI_MARVEL: 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> {
(1..32).filter_map(|addr| {
match identify_phy(pa, addr) {
for addr in 1..32 {
let device = match identify_phy(pa, addr) {
Some(PhyIdentifier {
oui: OUI_MARVELL,
oui: OUI_MARVEL,
model: 36,
..
}) => Some(PhyDevice::Marvell88E1116R),
}) => Some(PhyDevice::Marvel88E1116R),
Some(PhyIdentifier {
oui: OUI_REALTEK,
model: 0b010001,
rev: 0b0101,
}) => Some(PhyDevice::Rtl8211E),
_ => None,
}.map(|device| Phy { addr, device })
}).next()
};
match device {
Some(device) =>
return Some(Phy { addr, device }),
None => {}
}
}
None
}
pub fn name(&self) -> &'static str {
match self.device {
PhyDevice::Marvell88E1116R => &"Marvell 88E1116R",
PhyDevice::Marvel88E1116R => &"Marvel 88E1116R",
PhyDevice::Rtl8211E => &"RTL8211E",
}
}
@ -113,8 +120,12 @@ impl Phy {
if !status.link_status() {
None
} else if status.cap_1000base_t_extended_status() {
let phy_status: PSSR = self.read_reg(pa);
phy_status.get_link()
let ext_status: ExtendedStatus = self.read_reg(pa);
if let Some(link) = ext_status.get_link() {
Some(link)
} else {
status.get_link()
}
} else {
status.get_link()
}

View File

@ -1,52 +0,0 @@
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

@ -1,55 +1,11 @@
//! ARM Generic Interrupt Controller
use bit_field::BitField;
use libregister::{RegisterW, RegisterRW, RegisterR};
use libregister::{RegisterW, RegisterRW};
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)
}
}
pub struct InterruptId(u8);
#[derive(Debug, Clone, Copy)]
pub enum InterruptSensitivity {
@ -58,21 +14,19 @@ pub enum InterruptSensitivity {
}
pub struct InterruptController {
mpcore: &'static mut mpcore::RegisterBlock,
mpcore: mpcore::RegisterBlock,
}
impl InterruptController {
pub fn new(mpcore: &'static mut mpcore::RegisterBlock) -> Self {
pub fn new(mpcore: 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));
self.mpcore.icddcr.modify(|_, w| w.enable_secure(false)
.enable_non_secure(false));
}
/// enable interrupt signaling
@ -80,26 +34,10 @@ impl InterruptController {
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);
pub fn enable(&mut self, id: InterruptId, target_cpu: u32, sensitivity: InterruptSensitivity) {
assert!(target_cpu < 2);
self.disable_interrupts();
@ -115,7 +53,7 @@ impl InterruptController {
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));
self.mpcore.icdiptr[m].modify(|mut icdiptr| *icdiptr.set_bits(n..=n+1, target_cpu + 1));
}
// sensitivity
@ -128,23 +66,9 @@ impl InterruptController {
}));
}
// 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);
}
// filter no interrupts (lowest priority)
self.mpcore.iccpmr.write(mpcore::ICCPMR::zeroed().priority(0xFF));
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

@ -1,107 +0,0 @@
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

@ -1,235 +0,0 @@
//! 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";
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(0xFFFF - 0x000C)
}
fn ctor_common(gpio_output_mask: u16) -> 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

@ -1,93 +0,0 @@
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

@ -20,7 +20,5 @@ pub mod flash;
pub mod time;
pub mod timer;
pub mod sdio;
#[cfg(feature = "target_zc706")]
pub mod i2c;
pub mod logger;
pub mod ps7_init;

View File

@ -119,7 +119,30 @@ pub struct RegisterBlock {
pub icdabr2: RW<u32>,
unused13: [u32; 61],
/// Interrupt Priority Register
pub icdipr: [RW<u32>; 24],
pub icdipr0: RW<u32>,
pub icdipr1: RW<u32>,
pub icdipr2: RW<u32>,
pub icdipr3: RW<u32>,
pub icdipr4: RW<u32>,
pub icdipr5: RW<u32>,
pub icdipr6: RW<u32>,
pub icdipr7: RW<u32>,
pub icdipr8: RW<u32>,
pub icdipr9: RW<u32>,
pub icdipr10: RW<u32>,
pub icdipr11: RW<u32>,
pub icdipr12: RW<u32>,
pub icdipr13: RW<u32>,
pub icdipr14: RW<u32>,
pub icdipr15: RW<u32>,
pub icdipr16: RW<u32>,
pub icdipr17: RW<u32>,
pub icdipr18: RW<u32>,
pub icdipr19: RW<u32>,
pub icdipr20: RW<u32>,
pub icdipr21: RW<u32>,
pub icdipr22: RW<u32>,
pub icdipr23: RW<u32>,
unused14: [u32; 232],
/// Interrupt Processor Targets Registers
pub icdiptr: [RW<u32>; 24],

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: GpioRstCtrl,
pub gpio_rst_ctrl: RW<u32>,
pub lqspi_rst_ctrl: LqspiRstCtrl,
pub smc_rst_ctrl: RW<u32>,
pub ocm_rst_ctrl: RW<u32>,
@ -531,20 +531,6 @@ 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

@ -138,15 +138,6 @@ where
}
}
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) {

View File

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

View File

@ -33,44 +33,3 @@ 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

@ -11,26 +11,9 @@ 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: true,
shareable: false,
access: AccessPermissions::FullAccess,
tex: 0b100,
domain: 0,

View File

@ -1,10 +1,20 @@
use core::ops::{Deref, DerefMut};
use core::sync::atomic::{AtomicU32, Ordering};
use core::cell::UnsafeCell;
use super::{
spin_lock_yield, notify_spin_lock,
asm::{dmb, enter_critical, exit_critical}
};
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();
}
const LOCKED: u32 = 1;
const UNLOCKED: u32 = 0;
@ -31,26 +41,19 @@ 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 {
unsafe {
exit_critical(irq);
spin_lock_yield();
irq = enter_critical();
}
wait_for_update();
}
dmb();
MutexGuard { mutex: self, irq }
MutexGuard { mutex: self }
}
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, irq })
Some(MutexGuard { mutex: self })
}
}
@ -58,7 +61,7 @@ impl<T> Mutex<T> {
dmb();
self.locked.store(UNLOCKED, Ordering::Release);
notify_spin_lock();
signal_update();
}
}
@ -66,7 +69,6 @@ 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> {
@ -86,6 +88,5 @@ 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

@ -1,71 +0,0 @@
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

@ -6,7 +6,7 @@ use core::{
task::{Context, Poll},
};
use alloc::boxed::Box;
use super::{spin_lock_yield, notify_spin_lock};
use super::asm::*;
pub struct Sender<'a, T> where T: Clone {
list: &'a [AtomicPtr<T>],
@ -35,7 +35,9 @@ impl<'a, T> Sender<'a, T> where T: Clone {
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();
// wake up other core, actually I wonder if the dsb is really needed...
dsb();
sev();
if !prev.is_null() {
unsafe {
drop_in_place(prev);
@ -49,7 +51,7 @@ impl<'a, T> Sender<'a, T> where T: Clone {
let mut content = content;
while let Err(back) = self.try_send(content) {
content = back;
spin_lock_yield();
wfe();
}
}
@ -85,27 +87,6 @@ impl<'a, T> Sender<'a, T> where T: Clone {
content: Err(content.into()),
}.await
}
/// 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);
}
}
}
/// 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);
}
}
}
impl<'a, T> Receiver<'a, T> where T: Clone {
@ -125,7 +106,9 @@ impl<'a, T> Receiver<'a, T> where T: Clone {
};
let result = data.clone();
self.read.store((read + 1) % self.list.len(), Ordering::Release);
notify_spin_lock();
// wake up other core, still idk if the dsb is needed...
dsb();
sev();
Ok(result)
}
}
@ -135,7 +118,7 @@ impl<'a, T> Receiver<'a, T> where T: Clone {
if let Ok(data) = self.try_recv() {
return data;
}
spin_lock_yield();
wfe();
}
}

View File

@ -9,10 +9,8 @@ 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", "dummy_irq_handler"]
default = ["panic_handler"]
[dependencies]
r0 = "1"

View File

@ -53,7 +53,6 @@ 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, spin_lock_yield, notify_spin_lock};
use libcortex_a9::{asm, regs::*, cache, mmu};
use libboard_zynq::{slcr, mpcore};
extern "C" {
@ -29,7 +29,7 @@ pub unsafe extern "C" fn Reset() -> ! {
}
1 => {
while !CORE1_ENABLED.get() {
spin_lock_yield();
asm::wfe();
}
SP.write(&mut __stack1_start as *mut _ as u32);
boot_core1();
@ -57,7 +57,6 @@ unsafe fn boot_core0() -> ! {
asm::dmb();
asm::dsb();
asm::enable_irq();
main_core0();
panic!("return from main");
});
@ -78,7 +77,6 @@ unsafe fn boot_core1() -> ! {
asm::dmb();
asm::dsb();
asm::enable_irq();
main_core1();
panic!("return from main_core1");
});
@ -144,7 +142,6 @@ 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 {}
}

View File

@ -1,97 +1,55 @@
use alloc::alloc::Layout;
use core::alloc::GlobalAlloc;
use core::ptr::NonNull;
use libcortex_a9::{
mutex::Mutex,
regs::MPIDR
};
use libregister::RegisterR;
use alloc::alloc::Layout;
use linked_list_allocator::Heap;
#[cfg(not(feature = "alloc_core"))]
use libcortex_a9::mutex::Mutex;
use libboard_zynq::ddr::DdrRam;
#[global_allocator]
static ALLOCATOR: CortexA9Alloc = CortexA9Alloc(
Mutex::new(Heap::empty()),
Mutex::new(Heap::empty()),
);
static ALLOCATOR: CortexA9Alloc = CortexA9Alloc(Mutex::new(Heap::empty()));
struct CortexA9Alloc(Mutex<Heap>, Mutex<Heap>);
/// LockedHeap doesn't lock properly
struct CortexA9Alloc(Mutex<Heap>);
unsafe impl Sync for CortexA9Alloc {}
unsafe impl GlobalAlloc for CortexA9Alloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
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())
self.0.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) {
if cfg!(not(feature = "alloc_core")) || MPIDR.read().cpu_id() == 0 {
&self.0
} else {
&self.1
}
.lock()
.deallocate(NonNull::new_unchecked(ptr), layout)
self.0.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 __heap0_start: usize;
static __heap0_end: usize;
#[cfg(feature = "alloc_core")]
static __heap1_start: usize;
#[cfg(feature = "alloc_core")]
static __heap1_end: usize;
static __heap_start: usize;
static __heap_end: usize;
}
pub fn init_alloc_core0() {
pub fn init_alloc_linker() {
unsafe {
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);
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);
}
}
#[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(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
);
fn alloc_error(_: core::alloc::Layout) -> ! {
panic!("alloc_error")
}

View File

@ -19,6 +19,6 @@ stdenv.mkDerivation {
XARGO_RUST_SRC = "${rustcSrc}/src";
shellHook = ''
echo "Run 'cargo xbuild --release -p experiments' to build."
echo "Run 'cargo xbuild --release' to build."
'';
}