Compare commits

...

73 Commits
eth ... master

Author SHA1 Message Date
harry c69cd9951e Update README and build.sh (#59)
update build.sh to use rpi-4 directly; fix README

README: update build instruction

build.sh: fix missing package argument

Co-authored-by: Harry Ho <hh@m-labs.hk>
2020-08-11 11:24:21 +08:00
Harry Ho 76a4cac873 i2c: disable its usage on Cora Z7-10 2020-08-10 14:24:13 +08:00
Harry Ho 4614ed1371 i2c: simplify ctor_common() 2020-08-08 10:06:11 +08:00
pca006132 fa07bdb681 libcortex_a9/mmu: share ocm3.
This fixes issue #54.
2020-08-07 15:10:38 +08:00
Harry Ho 4565a75766 experiments: add I2C bitbang EEPROM writing/reading example 2020-08-07 11:10:20 +08:00
Harry Ho 16b2df91ca i2c: fix GPIO register mapping, I2C control & EEPROM write operations 2020-08-07 11:10:18 +08:00
Harry Ho f7d3135ec7 i2c: implement EEPROM operations; add CountDown waiting indication 2020-08-05 20:10:30 +08:00
Harry Ho c60230af25 i2c: implement basic i2c bitbanging 2020-08-05 17:35:33 +08:00
pca006132 e8ba73a8c7 Updated build instruction. 2020-08-05 17:08:24 +08:00
pca006132 3958953ceb libcortex_a9/sync_channel: added drop_elements function. 2020-08-05 15:29:28 +08:00
Sebastien Bourdeauducq a36a82d86d reduce ethernet verbosity 2020-08-04 22:15:01 +08:00
pca006132 25c6d5eeaa Changes usage of sev/wfe to spinlock functions. 2020-08-04 13:54:19 +08:00
pca006132 9e97102e12 libcortex_a9: implemented semaphore. 2020-08-04 13:34:08 +08:00
pca006132 b65606f2d0 libcortex_a9/sync_channel: added reset. 2020-08-03 15:50:31 +08:00
pca006132 ee4089c52e updated cargoSha256 2020-08-03 14:59:49 +08:00
pca006132 36c3fbdacd experiments: fixed linker script. 2020-08-03 14:48:44 +08:00
pca006132 8328ffc66b libsupport_zynq/ram: allow single allocator. 2020-08-03 14:48:44 +08:00
pca006132 84041a3154 libsupport_zynq/ram: use core0 allocator by default. 2020-08-03 14:48:44 +08:00
pca006132 5850401d72 libsupport_zynq/ram: split allocators for two cores. 2020-08-03 14:48:44 +08:00
pca006132 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
pca006132 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
pca006132 7d38c53c18 libsupport_zynq/abort: moved core1 restart code to user code. 2020-08-03 14:09:36 +08:00
pca006132 02a2c4d1e3 experiments: updated example. 2020-08-03 12:35:17 +08:00
pca006132 12669124a4 libcortex_a9/mutex: added interrupt critical section mask. 2020-08-03 12:35:17 +08:00
pca006132 8f0a6bd5ea libsupport_zynq/abort: restart core1 main on core1 IRQ#0. 2020-08-03 12:35:17 +08:00
pca006132 c1f61b5673 libcortex_a9/boot: enable IRQ on reset. 2020-08-03 12:35:17 +08:00
pca006132 2927c43309 libboard_zynq/gic: refactored and added SGI functions. 2020-08-03 12:35:17 +08:00
Astro 187801c4a7 gic: start implementation 2020-08-03 12:35:17 +08:00
pca006132 91ece367f2 libboard_zynq/mpcore: added generated register definitions 2020-08-03 12:35:17 +08:00
Astro 1f05e6977e eth::phy: replace ExtendedStatus with PSSR 2020-07-29 21:49:18 +02:00
Astro e408a8b22d eth::phy::extended_status: fix cap_1000base_x_full() bit position 2020-07-29 21:29:28 +02:00
Astro 27effb6257 eth::phy: s/Marvel/Marvell/ 2020-07-29 20:08:38 +02:00
Astro de5f605d60 eth: refactor peripheral instance into type parameter, improve clock setup 2020-07-29 19:45:01 +02:00
pca006132 ad47521e4b libsupport_zynq/boot: fixed core1 disable. 2020-07-28 12:36:23 +08:00
pca006132 c50e72f91e experiments: use OCM instead of OCM3 (#54) 2020-07-28 12:36:23 +08:00
pca006132 b099c56569 libcortex_a9/sync_channel: new version compiled. 2020-07-28 12:36:16 +08:00
Astro ef4fb598fb ddr: improve dci divisors calculation 2020-07-28 00:43:33 +02:00
Astro 0aa75d3544 experiments: fix timer.get_us() usage 2020-07-22 23:47:57 +02:00
Astro f36b1a610e timer::global: wrap us in Microseconds, impl embedded_hal blocking delay traits 2020-07-22 23:41:15 +02:00
Astro 7f45d10af3 timer::global::CountDown: fix delaying from "up to" to "at least" the timespan 2020-07-22 22:43:10 +02:00
Astro 855d94c48e dmac: remove unused module 2020-07-20 19:42:32 +02:00
Sebastien Bourdeauducq 84f1380f48 libasync: assert that callback consumes data in smoltcp recv 2020-07-19 16:14:29 +08:00
Sebastien Bourdeauducq f8785c3f07 fix some compilation warnings 2020-07-19 15:39:08 +08:00
Sebastien Bourdeauducq 7b78bc0494 libasync: new stream.recv API
M-Labs/artiq-zynq#40 (comment)
2020-07-19 15:34:32 +08:00
Sebastien Bourdeauducq ef88a1313a shell.nix: remove gcc 2020-07-19 15:34:18 +08:00
Astro 484e385160 eth: implement DeviceCapabilities.max_burst_size
this is a hint that /could/ boost TCP performance.
2020-07-16 00:17:13 +02:00
pca006132 074438c3c7 libcortex_a9: added `try_lock` for mutex. 2020-07-15 16:44:01 +08:00
Astro 191abf6b8f mpidr: wrap with proper bitfield getters
Prevents callers from dealing with CORE_MASK.
2020-07-08 00:04:54 +02:00
Sebastien Bourdeauducq 371e59cef5 libboard_zynq: add fpgax_clk_ctrl registers 2020-07-07 19:37:51 +08:00
pca006132 e67efe439b libsupport: fixed core1 restart.
The TRM mentioned that user should stop the clock, de-assert the reset,
and then restart the clock for core reset.

This fixes the kernel restart problem in one of the zc706 board.
2020-07-07 10:17:15 +08:00
Astro e4e7141bf3 ddr: delint 2020-07-06 19:46:18 +02:00
Sebastien Bourdeauducq f68b5896ce remove unused imports 2020-07-06 21:03:36 +08:00
Sebastien Bourdeauducq e430600683 fix exception vectors 2020-07-06 21:02:46 +08:00
Sebastien Bourdeauducq 0c60d684e4 slcr: remove soft reset
Does not work and probably difficult to get to work.
2020-07-06 13:06:10 +08:00
Sebastien Bourdeauducq 6fa3a6bbd9 fix previous commit 2020-07-06 12:11:20 +08:00
Sebastien Bourdeauducq 7082e07a18 experiments: move BSS and stack to OCM3 2020-07-06 11:57:02 +08:00
Sebastien Bourdeauducq 21c0c5cbc8 Revert "simplify ps7_init"
What the simplified ps7_init does can now be reproduced by the DDRC driver.
On the other hand, we are still experiencing crazy Zynq instability issues, so keep the original ps7_init around for debugging.

This reverts commit 9fcf9243f2.
2020-07-06 11:55:04 +08:00
pca006132 90904634cd DDR: fixed register write.
Previously it writes `0x20066`, while the ps7_init set it to be
`0x200066`, notice the 1 more 0.
This should perform the same writes to the registers, so we do not have
to apply the ps7_init in artiq_zynq.
2020-07-06 11:46:37 +08:00
Sebastien Bourdeauducq ae4d3e2455 smoltcp: enable IPv6 2020-07-06 11:30:48 +08:00
Sebastien Bourdeauducq 9fcf9243f2 simplify ps7_init 2020-07-06 00:52:40 +08:00
pca006132 90e33f688a FPU: moved enable function to zc706 2020-07-03 16:02:34 +08:00
Astro f0697c3ec3 ddr: implement additional configuration 2020-07-03 02:20:10 +02:00
Astro b2c707d543 ddr: remove superfluous `_reg` from register names 2020-07-03 02:20:10 +02:00
pca006132 6195ad40c3 libsupport_zynq: make panic handler an optional feature 2020-06-29 10:05:46 +08:00
Sebastien Bourdeauducq dd288912af fix experiments build for Cora 2020-06-28 17:47:33 +08:00
Astro ec252b099c experiments: don't write raw blocks to the sdcard by default 2020-06-26 23:27:28 +02:00
Astro a16c639eaf experiments: add bandwidth tester 2020-06-26 22:36:52 +02:00
Astro c6fa18344e uncached: disable cachable/bufferable 2020-06-26 22:32:49 +02:00
Astro 5c69bbdad6 mmu: fix L1Table.update() flush 2020-06-26 22:31:56 +02:00
Astro c0e66a632c ps7_init: move from experiments to libboard_zynq 2020-06-25 01:40:42 +02:00
Astro b129d3e0df panic: fix CORE_MASK 2020-06-25 01:27:23 +02:00
Astro 1e4be13869 experiments: implement ps7_init::apply() 2020-06-25 01:27:02 +02:00
Astro eea042e2ee experiments: update ps7_init for zc706 2020-06-24 22:23:05 +02:00
53 changed files with 6301 additions and 1331 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,7 +1,7 @@
# Build
```shell
nix-shell --command "cargo xbuild --release"
nix-shell --command "cargo xbuild --release -p experiments"
```
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"
nix-shell --command "cargo xbuild --release -p experiments"
cd openocd
openocd -f zc706.cfg
```
@ -57,23 +57,26 @@ 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 ~/zc706
./build.sh $your_user/ssh_id
cd ~/zynq-rs
./build.sh $your_user_or_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 zc706
cd zynq-rs
# For ZC706, run:
./tmux.sh 0
# For Cora Z7, run:
./tmux.sh
```

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 -C target/armv7-none-eabihf/release/experiments $1@rpi-4.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

@ -1,14 +1,4 @@
ENTRY(_boot_cores);
/* Provide some defaults */
PROVIDE(Reset = _boot_cores);
PROVIDE(UndefinedInstruction = Reset);
PROVIDE(SoftwareInterrupt = Reset);
PROVIDE(PrefetchAbort = Reset);
PROVIDE(DataAbort = Reset);
PROVIDE(ReservedException = Reset);
PROVIDE(IRQ = Reset);
PROVIDE(FIQ = Reset);
ENTRY(Reset);
MEMORY
{
@ -42,19 +32,23 @@ SECTIONS
*(.bss .bss.*);
. = ALIGN(4);
__bss_end = .;
} > OCM
} > OCM3
.stack1 (NOLOAD) : ALIGN(8) {
__stack1_end = .;
. += 0x200;
__stack1_start = .;
} > OCM
} > OCM3
.stack0 (NOLOAD) : ALIGN(8) {
__stack0_end = .;
. = ORIGIN(OCM) + LENGTH(OCM) - 8;
. = ORIGIN(OCM3) + LENGTH(OCM3) - 8;
__stack0_start = .;
} > OCM
/* 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,10 +15,10 @@ 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},
@ -26,22 +27,70 @@ use libboard_zynq::{
};
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;
mod ps7_init;
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();
libboard_zynq::logger::init().unwrap();
log::set_max_level(log::LevelFilter::Trace);
@ -82,28 +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();
{
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(0x1, 2, &mut buffer[1..]).unwrap();
for i in 0..buffer.len() {
info!("buffer[{}] = {}", i, buffer[i]);
}
info!("End");
}
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 {
@ -113,7 +140,7 @@ pub fn main_core0() {
}
println!("");
}
let mut flash = flash.stop();
let _flash = flash.stop();
let timer = libboard_zynq::timer::GlobalTimer::start();
@ -161,28 +188,62 @@ 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
#[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!("");
}
let eth = zynq::eth::Eth::default(HWADDR.clone());
println!("Eth on");
const RX_LEN: usize = 8;
const RX_LEN: usize = 4096;
// Number of transmission buffers (minimum is two because with
// one, duplicate packet transmission occurs)
const TX_LEN: usize = 8;
const TX_LEN: usize = 4096;
let eth = eth.start_rx(RX_LEN);
let mut eth = eth.start_tx(TX_LEN);
@ -199,56 +260,45 @@ 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)),
};
// (rx, tx)
let stats = alloc::rc::Rc::new(core::cell::RefCell::new((0, 0)));
let stats_tx = stats.clone();
task::spawn(async move {
while let Ok(stream) = TcpStream::accept(TCP_PORT, 0x10_0000, 0x10_0000).await {
let stats_tx = stats_tx.clone();
task::spawn(async move {
let tx_data = (0..=255).take(4096).collect::<alloc::vec::Vec<u8>>();
loop {
// const CHUNK_SIZE: usize = 65536;
// match stream.send((0..=255).cycle().take(CHUNK_SIZE)).await {
match stream.send_slice(&tx_data[..]).await {
Ok(len) => stats_tx.borrow_mut().1 += tx_data.len(), //CHUNK_SIZE,
Err(e) => {
warn!("tx: {:?}", e);
break
}
}
}
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(())
}
let counter = alloc::rc::Rc::new(core::cell::RefCell::new(0));
});
let stats_rx = stats.clone();
task::spawn(async move {
while let Ok(stream) = TcpStream::accept(TCP_PORT, 2048, 2408).await {
let counter = counter.clone();
while let Ok(stream) = TcpStream::accept(TCP_PORT+1, 0x10_0000, 0x10_0000).await {
let stats_rx = stats_rx.clone();
task::spawn(async move {
*counter.borrow_mut() += 1;
println!("Serving {} connections", *counter.borrow());
handle_connection(stream)
.await
.unwrap_or_else(|e| println!("Connection: {:?}", e));
*counter.borrow_mut() -= 1;
println!("Now serving {} connections", *counter.borrow());
loop {
match stream.recv(|buf| (buf.len(), buf.len())).await {
Ok(len) => stats_rx.borrow_mut().0 += len,
Err(e) => {
warn!("rx: {:?}", e);
break
}
}
}
});
}
});
@ -258,10 +308,16 @@ 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;
info!("time: {:6}.{:06}s", seconds, micros);
let (rx, tx) = {
let mut stats = stats.borrow_mut();
let result = *stats;
*stats = (0, 0);
result
};
info!("time: {:6}.{:06}s, rx: {}k/s, tx: {}k/s", seconds, micros, rx / 1024, tx / 1024);
}
});
@ -270,27 +326,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

@ -1,68 +0,0 @@
#![cfg(feature = "target_zc706")]
use libboard_zynq::println;
mod zc706;
// mod cora_z7_10;
#[cfg(feature = "target_zc706")]
use zc706 as target;
// #[cfg(feature = "target_cora_z7_10")]
// use cora_z7_10 as target;
pub fn report_differences() {
for (i, op) in target::INIT_DATA.iter().enumerate() {
let address = op.address();
let overwritten_later = target::INIT_DATA[(i + 1)..].iter()
.any(|later_op| later_op.address() == address);
if !overwritten_later {
op.report_difference();
}
}
}
pub enum InitOp {
MaskWrite(usize, usize, usize),
MaskPoll(usize, usize),
}
impl InitOp {
fn address(&self) -> usize {
match self {
InitOp::MaskWrite(address, _, _) => *address,
InitOp::MaskPoll(address, _) => *address,
}
}
fn read(&self) -> usize {
unsafe { *(self.address() as *const usize) }
}
fn difference(&self) -> Option<(usize, usize)> {
let (mask, expected) = match self {
InitOp::MaskWrite(_, mask, expected) =>
(*mask, *expected),
InitOp::MaskPoll(_, mask) =>
(*mask, *mask),
};
let actual = self.read();
if actual & mask == expected {
None
} else {
Some((actual & mask, expected))
}
}
pub fn report_difference(&self) {
if let Some((actual, expected)) = self.difference() {
println!(
"Register {:08X} is {:08X}&={:08X} != {:08X} expected",
self.address(),
self.read(),
actual,
expected
);
}
}
}

View File

@ -1,203 +0,0 @@
use super::InitOp::{self, *};
pub const INIT_DATA: &'static [InitOp] = &[
// ps7_mio_init_data_1_0
MaskWrite(0xF8000B40, 0x00000FFF, 0x00000600),
MaskWrite(0xF8000B44, 0x00000FFF, 0x00000600),
MaskWrite(0xF8000B48, 0x00000FFF, 0x00000672),
MaskWrite(0xF8000B4C, 0x00000FFF, 0x00000672),
MaskWrite(0xF8000B50, 0x00000FFF, 0x00000674),
MaskWrite(0xF8000B54, 0x00000FFF, 0x00000674),
MaskWrite(0xF8000B58, 0x00000FFF, 0x00000600),
MaskWrite(0xF8000B5C, 0xFFFFFFFF, 0x0018C61C),
MaskWrite(0xF8000B60, 0xFFFFFFFF, 0x00F9861C),
MaskWrite(0xF8000B64, 0xFFFFFFFF, 0x00F9861C),
MaskWrite(0xF8000B68, 0xFFFFFFFF, 0x00F9861C),
MaskWrite(0xF8000B6C, 0x000073FF, 0x00000209),
MaskWrite(0xF8000B70, 0x00000021, 0x00000021),
MaskWrite(0xF8000B70, 0x00000021, 0x00000020),
MaskWrite(0xF8000B70, 0x07FFFFFF, 0x00000823),
MaskWrite(0xF8000700, 0x00003FFF, 0x00000600),
MaskWrite(0xF8000704, 0x00003FFF, 0x00000702),
MaskWrite(0xF8000708, 0x00003FFF, 0x00000702),
MaskWrite(0xF800070C, 0x00003FFF, 0x00000702),
MaskWrite(0xF8000710, 0x00003FFF, 0x00000702),
MaskWrite(0xF8000714, 0x00003FFF, 0x00000702),
MaskWrite(0xF8000718, 0x00003FFF, 0x00000702),
MaskWrite(0xF800071C, 0x00003FFF, 0x00000600),
MaskWrite(0xF8000720, 0x00003FFF, 0x00000700),
MaskWrite(0xF8000724, 0x00003FFF, 0x00000600),
MaskWrite(0xF8000728, 0x00003FFF, 0x00000600),
MaskWrite(0xF800072C, 0x00003FFF, 0x00000600),
MaskWrite(0xF8000730, 0x00003FFF, 0x00000600),
MaskWrite(0xF8000734, 0x00003FFF, 0x00000600),
MaskWrite(0xF8000738, 0x00003FFF, 0x00000600),
MaskWrite(0xF800073C, 0x00003FFF, 0x00000600),
MaskWrite(0xF8000740, 0x00003FFF, 0x00000302),
MaskWrite(0xF8000744, 0x00003FFF, 0x00000302),
MaskWrite(0xF8000748, 0x00003FFF, 0x00000302),
MaskWrite(0xF800074C, 0x00003FFF, 0x00000302),
MaskWrite(0xF8000750, 0x00003FFF, 0x00000302),
MaskWrite(0xF8000754, 0x00003FFF, 0x00000302),
MaskWrite(0xF8000758, 0x00003FFF, 0x00000303),
MaskWrite(0xF800075C, 0x00003FFF, 0x00000303),
MaskWrite(0xF8000760, 0x00003FFF, 0x00000303),
MaskWrite(0xF8000764, 0x00003FFF, 0x00000303),
MaskWrite(0xF8000768, 0x00003FFF, 0x00000303),
MaskWrite(0xF800076C, 0x00003FFF, 0x00000303),
MaskWrite(0xF8000770, 0x00003FFF, 0x00000304),
MaskWrite(0xF8000774, 0x00003FFF, 0x00000305),
MaskWrite(0xF8000778, 0x00003FFF, 0x00000304),
MaskWrite(0xF800077C, 0x00003FFF, 0x00000305),
MaskWrite(0xF8000780, 0x00003FFF, 0x00000304),
MaskWrite(0xF8000784, 0x00003FFF, 0x00000304),
MaskWrite(0xF8000788, 0x00003FFF, 0x00000304),
MaskWrite(0xF800078C, 0x00003FFF, 0x00000304),
MaskWrite(0xF8000790, 0x00003FFF, 0x00000305),
MaskWrite(0xF8000794, 0x00003FFF, 0x00000304),
MaskWrite(0xF8000798, 0x00003FFF, 0x00000304),
MaskWrite(0xF800079C, 0x00003FFF, 0x00000304),
MaskWrite(0xF80007A0, 0x00003FFF, 0x00000380),
MaskWrite(0xF80007A4, 0x00003FFF, 0x00000380),
MaskWrite(0xF80007A8, 0x00003FFF, 0x00000380),
MaskWrite(0xF80007AC, 0x00003FFF, 0x00000380),
MaskWrite(0xF80007B0, 0x00003FFF, 0x00000380),
MaskWrite(0xF80007B4, 0x00003FFF, 0x00000380),
MaskWrite(0xF80007B8, 0x00003F01, 0x00000201),
MaskWrite(0xF80007BC, 0x00003F01, 0x00000201),
MaskWrite(0xF80007C0, 0x00003FFF, 0x000002E0),
MaskWrite(0xF80007C4, 0x00003FFF, 0x000002E1),
MaskWrite(0xF80007C8, 0x00003FFF, 0x00000200),
MaskWrite(0xF80007CC, 0x00003FFF, 0x00000200),
MaskWrite(0xF80007D0, 0x00003FFF, 0x00000280),
MaskWrite(0xF80007D4, 0x00003FFF, 0x00000280),
MaskWrite(0xF8000830, 0x003F003F, 0x002F002E),
// ps7_pll_init_data_1_0
MaskWrite(0xF8000110, 0x003FFFF0, 0x000FA220),
MaskWrite(0xF8000100, 0x0007F000, 0x00028000),
MaskWrite(0xF8000100, 0x00000010, 0x00000010),
MaskWrite(0xF8000100, 0x00000001, 0x00000001),
MaskWrite(0xF8000100, 0x00000001, 0x00000000),
MaskPoll(0xF800010C, 0x00000001),
MaskWrite(0xF8000100, 0x00000010, 0x00000000),
MaskWrite(0xF8000120, 0x1F003F30, 0x1F000200),
MaskWrite(0xF8000114, 0x003FFFF0, 0x0012C220),
MaskWrite(0xF8000104, 0x0007F000, 0x00020000),
MaskWrite(0xF8000104, 0x00000010, 0x00000010),
MaskWrite(0xF8000104, 0x00000001, 0x00000001),
MaskWrite(0xF8000104, 0x00000001, 0x00000000),
MaskPoll(0xF800010C, 0x00000002),
MaskWrite(0xF8000104, 0x00000010, 0x00000000),
MaskWrite(0xF8000124, 0xFFF00003, 0x0C200003),
MaskWrite(0xF8000118, 0x003FFFF0, 0x001452C0),
MaskWrite(0xF8000108, 0x0007F000, 0x0001E000),
MaskWrite(0xF8000108, 0x00000010, 0x00000010),
MaskWrite(0xF8000108, 0x00000001, 0x00000001),
MaskWrite(0xF8000108, 0x00000001, 0x00000000),
MaskPoll(0xF800010C, 0x00000004),
MaskWrite(0xF8000108, 0x00000010, 0x00000000),
// ps7_clock_init_data_1_0
MaskWrite(0xF8000128, 0x03F03F01, 0x00700F01),
MaskWrite(0xF8000138, 0x00000011, 0x00000001),
MaskWrite(0xF8000140, 0x03F03F71, 0x00100801),
MaskWrite(0xF800014C, 0x00003F31, 0x00000501),
MaskWrite(0xF8000150, 0x00003F33, 0x00001401),
MaskWrite(0xF8000154, 0x00003F33, 0x00001402),
MaskWrite(0xF8000168, 0x00003F31, 0x00000501),
MaskWrite(0xF8000170, 0x03F03F30, 0x00200400),
MaskWrite(0xF80001C4, 0x00000001, 0x00000001),
MaskWrite(0xF800012C, 0x01FFCCCD, 0x01EC044D),
// ps7_ddr_init_data_1_0
MaskWrite(0xF8006000, 0x0001FFFF, 0x00000080),
MaskWrite(0xF8006004, 0x1FFFFFFF, 0x00081081),
MaskWrite(0xF8006008, 0x03FFFFFF, 0x03C0780F),
MaskWrite(0xF800600C, 0x03FFFFFF, 0x02001001),
MaskWrite(0xF8006010, 0x03FFFFFF, 0x00014001),
MaskWrite(0xF8006014, 0x001FFFFF, 0x0004159B),
MaskWrite(0xF8006018, 0xF7FFFFFF, 0x452460D2),
MaskWrite(0xF800601C, 0xFFFFFFFF, 0x720238E5),
MaskWrite(0xF8006020, 0xFFFFFFFC, 0x272872D0),
MaskWrite(0xF8006024, 0x0FFFFFFF, 0x0000003C),
MaskWrite(0xF8006028, 0x00003FFF, 0x00002007),
MaskWrite(0xF800602C, 0xFFFFFFFF, 0x00000008),
MaskWrite(0xF8006030, 0xFFFFFFFF, 0x00040930),
MaskWrite(0xF8006034, 0x13FF3FFF, 0x000116D4),
MaskWrite(0xF8006038, 0x00001FC3, 0x00000000),
MaskWrite(0xF800603C, 0x000FFFFF, 0x00000777),
MaskWrite(0xF8006040, 0xFFFFFFFF, 0xFFF00000),
MaskWrite(0xF8006044, 0x0FFFFFFF, 0x0FF66666),
MaskWrite(0xF8006048, 0x3FFFFFFF, 0x0003C248),
MaskWrite(0xF8006050, 0xFF0F8FFF, 0x77010800),
MaskWrite(0xF8006058, 0x0001FFFF, 0x00000101),
MaskWrite(0xF800605C, 0x0000FFFF, 0x00005003),
MaskWrite(0xF8006060, 0x000017FF, 0x0000003E),
MaskWrite(0xF8006064, 0x00021FE0, 0x00020000),
MaskWrite(0xF8006068, 0x03FFFFFF, 0x00284141),
MaskWrite(0xF800606C, 0x0000FFFF, 0x00001610),
MaskWrite(0xF80060A0, 0x00FFFFFF, 0x00008000),
MaskWrite(0xF80060A4, 0xFFFFFFFF, 0x10200802),
MaskWrite(0xF80060A8, 0x0FFFFFFF, 0x0690CB73),
MaskWrite(0xF80060AC, 0x000001FF, 0x000001FE),
MaskWrite(0xF80060B0, 0x1FFFFFFF, 0x1CFFFFFF),
MaskWrite(0xF80060B4, 0x000007FF, 0x00000200),
MaskWrite(0xF80060B8, 0x01FFFFFF, 0x00200066),
MaskWrite(0xF80060C4, 0x00000003, 0x00000000),
MaskWrite(0xF80060C8, 0x000000FF, 0x00000000),
MaskWrite(0xF80060DC, 0x00000001, 0x00000000),
MaskWrite(0xF80060F0, 0x0000FFFF, 0x00000000),
MaskWrite(0xF80060F4, 0x0000000F, 0x00000008),
MaskWrite(0xF8006114, 0x000000FF, 0x00000000),
MaskWrite(0xF8006118, 0x7FFFFFFF, 0x40000001),
MaskWrite(0xF800611C, 0x7FFFFFFF, 0x40000001),
MaskWrite(0xF8006120, 0x7FFFFFFF, 0x40000001),
MaskWrite(0xF8006124, 0x7FFFFFFF, 0x40000001),
MaskWrite(0xF800612C, 0x000FFFFF, 0x00033C03),
MaskWrite(0xF8006130, 0x000FFFFF, 0x00034003),
MaskWrite(0xF8006134, 0x000FFFFF, 0x0002F400),
MaskWrite(0xF8006138, 0x000FFFFF, 0x00030400),
MaskWrite(0xF8006140, 0x000FFFFF, 0x00000035),
MaskWrite(0xF8006144, 0x000FFFFF, 0x00000035),
MaskWrite(0xF8006148, 0x000FFFFF, 0x00000035),
MaskWrite(0xF800614C, 0x000FFFFF, 0x00000035),
MaskWrite(0xF8006154, 0x000FFFFF, 0x00000083),
MaskWrite(0xF8006158, 0x000FFFFF, 0x00000083),
MaskWrite(0xF800615C, 0x000FFFFF, 0x00000080),
MaskWrite(0xF8006160, 0x000FFFFF, 0x00000080),
MaskWrite(0xF8006168, 0x001FFFFF, 0x00000124),
MaskWrite(0xF800616C, 0x001FFFFF, 0x00000125),
MaskWrite(0xF8006170, 0x001FFFFF, 0x00000112),
MaskWrite(0xF8006174, 0x001FFFFF, 0x00000116),
MaskWrite(0xF800617C, 0x000FFFFF, 0x000000C3),
MaskWrite(0xF8006180, 0x000FFFFF, 0x000000C3),
MaskWrite(0xF8006184, 0x000FFFFF, 0x000000C0),
MaskWrite(0xF8006188, 0x000FFFFF, 0x000000C0),
MaskWrite(0xF8006190, 0xFFFFFFFF, 0x10040080),
MaskWrite(0xF8006194, 0x000FFFFF, 0x0001FC82),
MaskWrite(0xF8006204, 0xFFFFFFFF, 0x00000000),
MaskWrite(0xF8006208, 0x000F03FF, 0x000803FF),
MaskWrite(0xF800620C, 0x000F03FF, 0x000803FF),
MaskWrite(0xF8006210, 0x000F03FF, 0x000803FF),
MaskWrite(0xF8006214, 0x000F03FF, 0x000803FF),
MaskWrite(0xF8006218, 0x000F03FF, 0x000003FF),
MaskWrite(0xF800621C, 0x000F03FF, 0x000003FF),
MaskWrite(0xF8006220, 0x000F03FF, 0x000003FF),
MaskWrite(0xF8006224, 0x000F03FF, 0x000003FF),
MaskWrite(0xF80062A8, 0x00000FF7, 0x00000000),
MaskWrite(0xF80062AC, 0xFFFFFFFF, 0x00000000),
MaskWrite(0xF80062B0, 0x003FFFFF, 0x00005125),
MaskWrite(0xF80062B4, 0x0003FFFF, 0x000012A8),
MaskPoll(0xF8000B74, 0x00002000),
MaskWrite(0xF8006000, 0x0001FFFF, 0x00000081),
MaskPoll(0xF8006054, 0x00000007),
// ps7_peripherals_init_data_1_0
MaskWrite(0xF8000B48, 0x00000180, 0x00000180),
MaskWrite(0xF8000B4C, 0x00000180, 0x00000180),
MaskWrite(0xF8000B50, 0x00000180, 0x00000180),
MaskWrite(0xF8000B54, 0x00000180, 0x00000180),
MaskWrite(0xE0001034, 0x000000FF, 0x00000006),
MaskWrite(0xE0001018, 0x0000FFFF, 0x0000003E),
MaskWrite(0xE0001000, 0x000001FF, 0x00000017),
MaskWrite(0xE0001004, 0x00000FFF, 0x00000020),
MaskWrite(0xE000D000, 0x00080000, 0x00080000),
MaskWrite(0xF8007000, 0x20000000, 0x00000000),
];

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

@ -21,5 +21,5 @@ libcortex_a9 = { path = "../libcortex_a9" }
[dependencies.smoltcp]
version = "0.6"
features = ["ethernet", "proto-ipv4", "socket-tcp"]
features = ["ethernet", "proto-ipv4", "proto-ipv6", "socket-tcp"]
default-features = false

View File

@ -29,6 +29,7 @@ impl DdrRam {
let regs = unsafe { regs::RegisterBlock::new() };
let mut ddr = DdrRam { regs };
ddr.configure();
ddr.reset_ddrc();
ddr
}
@ -55,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.
@ -181,6 +203,82 @@ impl DdrRam {
});
}
fn configure(&mut self) {
self.regs.dram_param0.write(
regs::DramParam0::zeroed()
.t_rc(0x1b)
.t_rfc_min(0x56)
.post_selfref_gap_x32(0x10)
);
self.regs.dram_param2.write(
regs::DramParam2::zeroed()
.write_latency(0x5)
.rd2wr(0x7)
.wr2rd(0xe)
.t_xp(0x4)
.pad_pd(0x0)
.rd2pre(0x4)
.t_rcd(0x7)
);
self.regs.dram_emr_mr.write(
regs::DramEmrMr::zeroed()
.mr(0x930)
.emr(0x4)
);
self.regs.phy_cmd_timeout_rddata_cpt.modify(
|_, w| w
.rd_cmd_to_data(0x0)
.wr_cmd_to_data(0x0)
.we_to_re_delay(0x8)
.rdc_fifo_rst_disable(false)
.use_fixed_re(true)
.rdc_fifo_rst_err_cnt_clr(false)
.dis_phy_ctrl_rstn(false)
.clk_stall_level(false)
.gatelvl_num_of_dq0(0x7)
.wrlvl_num_of_dq0(0x7)
);
self.regs.reg_2c.write(
regs::Reg2C::zeroed()
.wrlvl_max_x1024(0xfff)
.rdlvl_max_x1024(0xfff)
.twrlvl_max_error(false)
.trdlvl_max_error(false)
.dfi_wr_level_en(true)
.dfi_rd_dqs_gate_level(true)
.dfi_rd_data_eye_train(true)
);
self.regs.dfi_timing.write(
regs::DfiTiming::zeroed()
.rddata_en(0x6)
.ctrlup_min(0x3)
.ctrlup_max(0x40)
);
self.regs.phy_init_ratio3.write(
regs::PhyInitRatio::zeroed()
.wrlvl_init_ratio(0x21)
.gatelvl_init_ratio(0xee)
);
self.regs.reg_65.write(
regs::Reg65::zeroed()
.wr_rl_delay(0x2)
.rd_rl_delay(0x4)
.dll_lock_diff(0xf)
.use_wr_level(true)
.use_rd_dqs_gate_level(true)
.use_rd_data_eye_level(true)
.dis_calib_rst(false)
.ctrl_slave_delay(0x0)
);
}
/// Reset DDR controller
fn reset_ddrc(&mut self) {
#[cfg(feature = "target_zc706")]
@ -210,7 +308,7 @@ impl DdrRam {
}
pub fn status(&self) -> regs::ControllerStatus {
self.regs.mode_sts_reg.read().operating_mode()
self.regs.mode_sts.read().operating_mode()
}
pub fn ptr<T>(&mut self) -> *mut T {
@ -239,7 +337,7 @@ impl DdrRam {
for megabyte in 0..slice.len() / (1024 * 1024) {
let start = megabyte * 1024 * 1024 / 4;
let end = ((megabyte + 1) * 1024 * 1024 / 4);
let end = (megabyte + 1) * 1024 * 1024 / 4;
for b in slice[start..end].iter_mut() {
expected.map(|expected| {
let read: u32 = *b;

View File

@ -1,6 +1,6 @@
use volatile_register::{RO, RW};
use libregister::{register, register_bit, register_bits_typed};
use libregister::{register, register_bit, register_bits, register_bits_typed};
#[allow(unused)]
#[derive(Clone, Copy)]
@ -29,59 +29,59 @@ pub enum ControllerStatus {
pub struct RegisterBlock {
pub ddrc_ctrl: DdrcCtrl,
pub two_rank_cfg: RW<u32>,
pub hpr_reg: RW<u32>,
pub lpr_reg: RW<u32>,
pub wr_reg: RW<u32>,
pub dram_param_reg0: RW<u32>,
pub dram_param_reg1: RW<u32>,
pub dram_param_reg2: RW<u32>,
pub dram_param_reg3: RW<u32>,
pub dram_param_reg4: RW<u32>,
pub hpr: RW<u32>,
pub lpr: RW<u32>,
pub wr: RW<u32>,
pub dram_param0: DramParam0,
pub dram_param1: RW<u32>,
pub dram_param2: DramParam2,
pub dram_param3: RW<u32>,
pub dram_param4: RW<u32>,
pub dram_init_param: RW<u32>,
pub dram_emr_reg: RW<u32>,
pub dram_emr_mr_reg: RW<u32>,
pub dram_emr: RW<u32>,
pub dram_emr_mr: DramEmrMr,
pub dram_burst8_rdwr: RW<u32>,
pub dram_disable_dq: RW<u32>,
pub dram_addr_map_bank: RW<u32>,
pub dram_addr_map_col: RW<u32>,
pub dram_addr_map_row: RW<u32>,
pub dram_odt_reg: RW<u32>,
pub phy_dbg_reg: RW<u32>,
pub phy_cmd_timeout_rddata_cpt: RW<u32>,
pub mode_sts_reg: ModeStsReg,
pub dram_odt: RW<u32>,
pub phy_dbg: RW<u32>,
pub phy_cmd_timeout_rddata_cpt: PhyCmdTimeoutRddataCpt,
pub mode_sts: ModeStsReg,
pub dll_calib: RW<u32>,
pub odt_delay_hold: RW<u32>,
pub ctrl_reg1: RW<u32>,
pub ctrl_reg2: RW<u32>,
pub ctrl_reg3: RW<u32>,
pub ctrl_reg4: RW<u32>,
pub ctrl1: RW<u32>,
pub ctrl2: RW<u32>,
pub ctrl3: RW<u32>,
pub ctrl4: RW<u32>,
_unused0: [RO<u32>; 2],
pub ctrl_reg5: RW<u32>,
pub ctrl_reg6: RW<u32>,
pub ctrl5: RW<u32>,
pub ctrl6: RW<u32>,
_unused1: [RO<u32>; 8],
pub che_refresh_timer01: RW<u32>,
pub che_t_zq: RW<u32>,
pub che_t_zq_short_interval_reg: RW<u32>,
pub deep_pwrdwn_reg: RW<u32>,
pub reg_2c: RW<u32>,
pub che_t_zq_short_interval: RW<u32>,
pub deep_pwrdwn: RW<u32>,
pub reg_2c: Reg2C,
pub reg_2d: RW<u32>,
pub dfi_timing: RW<u32>,
pub dfi_timing: DfiTiming,
_unused2: [RO<u32>; 2],
pub che_ecc_control_reg_offset: RW<u32>,
pub che_corr_ecc_log_reg_offset: RW<u32>,
pub che_corr_ecc_addr_reg_offset: RW<u32>,
pub che_corr_ecc_data_31_0_reg_offset: RW<u32>,
pub che_corr_ecc_data_63_32_reg_offset: RW<u32>,
pub che_corr_ecc_data_71_64_reg_offset: RW<u32>,
pub che_uncorr_ecc_log_reg_offset: RW<u32>,
pub che_uncorr_ecc_addr_reg_offset: RW<u32>,
pub che_uncorr_ecc_data_31_0_reg_offset: RW<u32>,
pub che_uncorr_ecc_data_63_32_reg_offset: RW<u32>,
pub che_uncorr_ecc_data_71_64_reg_offset: RW<u32>,
pub che_ecc_stats_reg_offset: RW<u32>,
pub che_ecc_control_offset: RW<u32>,
pub che_corr_ecc_log_offset: RW<u32>,
pub che_corr_ecc_addr_offset: RW<u32>,
pub che_corr_ecc_data_31_0_offset: RW<u32>,
pub che_corr_ecc_data_63_32_offset: RW<u32>,
pub che_corr_ecc_data_71_64_offset: RW<u32>,
pub che_uncorr_ecc_log_offset: RW<u32>,
pub che_uncorr_ecc_addr_offset: RW<u32>,
pub che_uncorr_ecc_data_31_0_offset: RW<u32>,
pub che_uncorr_ecc_data_63_32_offset: RW<u32>,
pub che_uncorr_ecc_data_71_64_offset: RW<u32>,
pub che_ecc_stats_offset: RW<u32>,
pub ecc_scrub: RW<u32>,
pub che_ecc_corr_bit_mask_31_0_reg_offset: RW<u32>,
pub che_ecc_corr_bit_mask_63_32_reg_offset: RW<u32>,
pub che_ecc_corr_bit_mask_31_0_offset: RW<u32>,
pub che_ecc_corr_bit_mask_63_32_offset: RW<u32>,
_unused3: [RO<u32>; 5],
pub phy_rcvr_enable: RW<u32>,
pub phy_config0: RW<u32>,
@ -89,10 +89,10 @@ pub struct RegisterBlock {
pub phy_config2: RW<u32>,
pub phy_config3: RW<u32>,
_unused4: RO<u32>,
pub phy_init_ratio0: RW<u32>,
pub phy_init_ratio1: RW<u32>,
pub phy_init_ratio2: RW<u32>,
pub phy_init_ratio3: RW<u32>,
pub phy_init_ratio0: PhyInitRatio,
pub phy_init_ratio1: PhyInitRatio,
pub phy_init_ratio2: PhyInitRatio,
pub phy_init_ratio3: PhyInitRatio,
_unused5: RO<u32>,
pub phy_rd_dqs_cfg0: RW<u32>,
pub phy_rd_dqs_cfg1: RW<u32>,
@ -115,7 +115,7 @@ pub struct RegisterBlock {
pub wr_data_slv3: RW<u32>,
_unused9: RO<u32>,
pub reg_64: RW<u32>,
pub reg_65: RW<u32>,
pub reg_65: Reg65,
_unused10: [RO<u32>; 3],
pub reg69_6a0: RW<u32>,
pub reg69_6a1: RW<u32>,
@ -134,7 +134,7 @@ pub struct RegisterBlock {
_unused13: RO<u32>,
pub dll_lock_sts: RW<u32>,
pub phy_ctrl_sts: RW<u32>,
pub phy_ctrl_sts_reg2: RW<u32>,
pub phy_ctrl_sts2: RW<u32>,
_unused14: [RO<u32>; 5],
pub axi_id: RW<u32>,
pub page_mask: RW<u32>,
@ -172,6 +172,64 @@ register_bit!(ddrc_ctrl, powerdown_en, 1);
register_bits_typed!(ddrc_ctrl, data_bus_width, u8, DataBusWidth, 2, 3);
// (ddrc_ctrl) ...
register!(dram_param0, DramParam0, RW, u32);
register_bits!(dram_param0, t_rc, u8, 0, 5);
register_bits!(dram_param0, t_rfc_min, u8, 6, 13);
register_bits!(dram_param0, post_selfref_gap_x32, u8, 14, 20);
register!(dram_param2, DramParam2, RW, u32);
register_bits!(dram_param2, write_latency, u8, 0, 4);
register_bits!(dram_param2, rd2wr, u8, 5, 9);
register_bits!(dram_param2, wr2rd, u8, 10, 14);
register_bits!(dram_param2, t_xp, u8, 15, 19);
register_bits!(dram_param2, pad_pd, u8, 20, 22);
register_bits!(dram_param2, rd2pre, u8, 23, 27);
register_bits!(dram_param2, t_rcd, u8, 28, 31);
register!(dram_emr_mr, DramEmrMr, RW, u32);
register_bits!(dram_emr_mr, mr, u16, 0, 15);
register_bits!(dram_emr_mr, emr, u16, 16, 31);
register!(phy_cmd_timeout_rddata_cpt, PhyCmdTimeoutRddataCpt, RW, u32);
register_bits!(phy_cmd_timeout_rddata_cpt, rd_cmd_to_data, u8, 0, 3);
register_bits!(phy_cmd_timeout_rddata_cpt, wr_cmd_to_data, u8, 4, 7);
register_bits!(phy_cmd_timeout_rddata_cpt, we_to_re_delay, u8, 8, 11);
register_bit!(phy_cmd_timeout_rddata_cpt, rdc_fifo_rst_disable, 15);
register_bit!(phy_cmd_timeout_rddata_cpt, use_fixed_re, 16);
register_bit!(phy_cmd_timeout_rddata_cpt, rdc_fifo_rst_err_cnt_clr, 17);
register_bit!(phy_cmd_timeout_rddata_cpt, dis_phy_ctrl_rstn, 18);
register_bit!(phy_cmd_timeout_rddata_cpt, clk_stall_level, 19);
register_bits!(phy_cmd_timeout_rddata_cpt, gatelvl_num_of_dq0, u8, 24, 27);
register_bits!(phy_cmd_timeout_rddata_cpt, wrlvl_num_of_dq0, u8, 28, 31);
register!(reg_2c, Reg2C, RW, u32);
register_bits!(reg_2c, wrlvl_max_x1024, u16, 0, 11);
register_bits!(reg_2c, rdlvl_max_x1024, u16, 12, 23);
register_bit!(reg_2c, twrlvl_max_error, 24);
register_bit!(reg_2c, trdlvl_max_error, 25);
register_bit!(reg_2c, dfi_wr_level_en, 26);
register_bit!(reg_2c, dfi_rd_dqs_gate_level, 27);
register_bit!(reg_2c, dfi_rd_data_eye_train, 28);
register!(dfi_timing, DfiTiming, RW, u32);
register_bits!(dfi_timing, rddata_en, u8, 0, 4);
register_bits!(dfi_timing, ctrlup_min, u16, 5, 14);
register_bits!(dfi_timing, ctrlup_max, u16, 15, 24);
register!(phy_init_ratio, PhyInitRatio, RW, u32);
register_bits!(phy_init_ratio, wrlvl_init_ratio, u16, 0, 9);
register_bits!(phy_init_ratio, gatelvl_init_ratio, u16, 10, 19);
register!(reg_65, Reg65, RW, u32);
register_bits!(reg_65, wr_rl_delay, u8, 0, 4);
register_bits!(reg_65, rd_rl_delay, u8, 5, 9);
register_bits!(reg_65, dll_lock_diff, u8, 10, 13);
register_bit!(reg_65, use_wr_level, 14);
register_bit!(reg_65, use_rd_dqs_gate_level, 15);
register_bit!(reg_65, use_rd_data_eye_level, 16);
register_bit!(reg_65, dis_calib_rst, 17);
register_bits!(reg_65, ctrl_slave_delay, u8, 18, 19);
// Controller operation mode status
register!(mode_sts_reg,
ModeStsReg, RO, u32);

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,235 @@
//! 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

@ -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,9 +15,12 @@ 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;
#[cfg(feature = "target_zc706")]
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

@ -0,0 +1,108 @@
#![cfg(feature = "target_zc706")]
use crate::println;
mod zc706;
// mod cora_z7_10;
#[cfg(feature = "target_zc706")]
use zc706 as target;
// #[cfg(feature = "target_cora_z7_10")]
// use cora_z7_10 as target;
pub fn report_differences() {
for (i, op) in target::INIT_DATA.iter().enumerate() {
let address = op.address();
let overwritten_later = target::INIT_DATA[(i + 1)..].iter()
.any(|later_op| later_op.address() == address);
if !overwritten_later {
op.report_difference();
}
}
}
pub fn apply() {
for op in target::INIT_DATA {
op.apply();
}
}
#[derive(Clone, Debug)]
pub enum InitOp {
MaskWrite(usize, usize, usize),
MaskPoll(usize, usize),
MaskDelay(usize, usize),
}
impl InitOp {
fn address(&self) -> usize {
match self {
InitOp::MaskWrite(address, _, _) => *address,
InitOp::MaskPoll(address, _) => *address,
InitOp::MaskDelay(address, _) => *address,
}
}
fn read(&self) -> usize {
unsafe { *(self.address() as *const usize) }
}
fn difference(&self) -> Option<(usize, usize)> {
let expected = match self {
InitOp::MaskWrite(_, mask, expected) =>
Some((*mask, *expected)),
InitOp::MaskPoll(_, mask) =>
Some((*mask, *mask)),
_ => None,
};
match expected {
Some((mask, expected)) => {
let actual = self.read();
if actual & mask == expected {
None
} else {
Some((actual & mask, expected))
}
}
None =>
None
}
}
pub fn report_difference(&self) {
if let Some((actual, expected)) = self.difference() {
println!(
"Register {:08X} is {:08X}&={:08X} != {:08X} expected",
self.address(),
self.read(),
actual,
expected
);
}
}
pub fn apply(&self) {
let reg = self.address() as *mut usize;
println!("apply {:?}", self);
match self {
InitOp::MaskWrite(_, mask, val) =>
unsafe {
*reg = (val & mask) | (*reg & !mask);
},
InitOp::MaskPoll(_, mask) =>
while unsafe { *reg } & mask == 0 {},
InitOp::MaskDelay(_, mask) => {
let delay = get_number_of_cycles_for_delay(*mask);
while unsafe { *reg } < delay {
println!("W");
}
}
}
}
}
fn get_number_of_cycles_for_delay(delay: usize) -> usize {
const APU_FREQ: usize = 666666687;
APU_FREQ * delay/ (2 * 1000)
}

File diff suppressed because it is too large Load Diff

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

@ -102,19 +102,19 @@ pub struct RegisterBlock {
pub dbg_clk_ctrl: RW<u32>,
pub pcap_clk_ctrl: RW<u32>,
pub topsw_clk_ctrl: RW<u32>,
pub fpga0_clk_ctrl: RW<u32>,
pub fpga0_clk_ctrl: Fpga0ClkCtrl,
pub fpga0_thr_ctrl: RW<u32>,
pub fpga0_thr_cnt: RW<u32>,
pub fpga0_thr_sta: RO<u32>,
pub fpga1_clk_ctrl: RW<u32>,
pub fpga1_clk_ctrl: Fpga1ClkCtrl,
pub fpga1_thr_ctrl: RW<u32>,
pub fpga1_thr_cnt: RW<u32>,
pub fpga1_thr_sta: RO<u32>,
pub fpga2_clk_ctrl: RW<u32>,
pub fpga2_clk_ctrl: Fpga2ClkCtrl,
pub fpga2_thr_ctrl: RW<u32>,
pub fpga2_thr_cnt: RW<u32>,
pub fpga2_thr_sta: RO<u32>,
pub fpga3_clk_ctrl: RW<u32>,
pub fpga3_clk_ctrl: Fpga3ClkCtrl,
pub fpga3_thr_ctrl: RW<u32>,
pub fpga3_thr_cnt: RW<u32>,
pub fpga3_thr_sta: RO<u32>,
@ -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>,
@ -265,14 +265,6 @@ impl RegisterBlock {
r
}
/// Perform a soft reset
pub fn soft_reset(&mut self) {
self.pss_rst_ctrl.write(
PssRstCtrl::zeroed()
.soft_rst(true)
);
}
pub fn init_preload_fpga(&mut self) {
// Assert FPGA top level output resets
self.fpga_rst_ctrl.write(
@ -539,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);
@ -548,6 +554,26 @@ register!(lqspi_rst_ctrl, LqspiRstCtrl, RW, u32);
register_bit!(lqspi_rst_ctrl, ref_rst, 1);
register_bit!(lqspi_rst_ctrl, cpu1x_rst, 0);
register!(fpga0_clk_ctrl, Fpga0ClkCtrl, RW, u32);
register_bits!(fpga0_clk_ctrl, divisor1, u8, 20, 25);
register_bits!(fpga0_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(fpga0_clk_ctrl, src_sel, u8, PllSource, 4, 5);
register!(fpga1_clk_ctrl, Fpga1ClkCtrl, RW, u32);
register_bits!(fpga1_clk_ctrl, divisor1, u8, 20, 25);
register_bits!(fpga1_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(fpga1_clk_ctrl, src_sel, u8, PllSource, 4, 5);
register!(fpga2_clk_ctrl, Fpga2ClkCtrl, RW, u32);
register_bits!(fpga2_clk_ctrl, divisor1, u8, 20, 25);
register_bits!(fpga2_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(fpga2_clk_ctrl, src_sel, u8, PllSource, 4, 5);
register!(fpga3_clk_ctrl, Fpga3ClkCtrl, RW, u32);
register_bits!(fpga3_clk_ctrl, divisor1, u8, 20, 25);
register_bits!(fpga3_clk_ctrl, divisor0, u8, 8, 13);
register_bits_typed!(fpga3_clk_ctrl, src_sel, u8, PllSource, 4, 5);
register!(fpga_rst_ctrl, FpgaRstCtrl, RW, u32);
register_bit!(fpga_rst_ctrl, fpga0_out_rst, 0);
register_bit!(fpga_rst_ctrl, fpga1_out_rst, 1);

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

@ -44,6 +44,15 @@ pub fn dcisw(setway: u32) {
}
}
/// Data cache clean by set/way
#[inline(always)]
pub fn dccisw(setway: u32) {
unsafe {
llvm_asm!("mcr p15, 0, $0, c7, c14, 2" :: "r" (setway) :: "volatile");
}
}
/// A made-up "instruction": invalidate all of the L1 D-Cache
#[inline(always)]
pub fn dciall() {
@ -71,6 +80,33 @@ pub fn dciall() {
}
}
/// A made-up "instruction": flush and invalidate all of the L1 D-Cache
#[inline(always)]
pub fn dcciall() {
// the cache associativity could be read from a register, but will
// always be 4 in L1 data cache of a cortex a9
let ways = 4;
let bit_pos_of_way = 30; // 32 - log2(ways)
// the cache sets could be read from a register, but are always
// 256 for the cores in the zync-7000; in general, 128 or 512 are
// also possible.
let sets = 256;
let bit_pos_of_set = 5; // for a line size of 8 words = 2^5 bytes
// select L1 data cache
unsafe {
llvm_asm!("mcr p15, 2, $0, c0, c0, 0" :: "r" (0) :: "volatile");
}
// Invalidate entire D-Cache by iterating every set and every way
for set in 0..sets {
for way in 0..ways {
dccisw((set << bit_pos_of_set) | (way << bit_pos_of_way));
}
}
}
const CACHE_LINE: usize = 0x20;
const CACHE_LINE_MASK: usize = CACHE_LINE - 1;

14
libcortex_a9/src/fpu.rs Normal file
View File

@ -0,0 +1,14 @@
/// Enable FPU in the current core.
pub fn enable_fpu() {
unsafe {
llvm_asm!("
mrc p15, 0, r1, c1, c0, 2
orr r1, r1, (0b1111<<20)
mcr p15, 0, r1, c1, c0, 2
vmrs r1, fpexc
orr r1, r1, (1<<30)
vmsr fpexc, r1
":::"r1");
}
}

View File

@ -1,6 +1,7 @@
#![no_std]
#![feature(llvm_asm, global_asm)]
#![feature(never_type)]
#![feature(const_fn)]
extern crate alloc;
@ -10,7 +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

@ -1,5 +1,5 @@
use bit_field::BitField;
use super::{regs::*, asm, cache};
use super::{regs::*, asm::*, cache::*};
use libregister::RegisterW;
#[derive(Copy, Clone)]
@ -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,
@ -368,10 +368,19 @@ impl L1Table {
let result = f(&mut section);
entry.set_section(section);
asm::dmb();
cache::tlbiall();
asm::dsb();
asm::isb();
// Flush L1Dcache
dcciall();
// // TODO: L2?
// Invalidate TLB
tlbiall();
// Invalidate all branch predictors
bpiall();
// ensure completion of the BP and TLB invalidation
dsb();
// synchronize context on this processor
isb();
result
}
@ -406,9 +415,9 @@ pub fn with_mmu<F: FnMut() -> !>(l1table: &L1Table, mut f: F) -> ! {
// Synchronization barriers
// Allows MMU to start
asm::dsb();
dsb();
// Flushes pre-fetch buffer
asm::isb();
isb();
f();
}

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,18 +31,34 @@ 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, irq })
}
}
fn unlock(&self) {
dmb();
self.locked.store(UNLOCKED, Ordering::Release);
signal_update();
notify_spin_lock();
}
}
@ -60,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> {
@ -79,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

@ -91,8 +91,19 @@ pub struct HVBAR;
def_reg_r!(HVBAR, u32, "mrc p15, 4, $0, c12, c0, 0");
def_reg_w!(HVBAR, u32, "mcr p15, 4, $0, c12, c0, 0");
/// Multiprocess Affinity Register
pub struct MPIDR;
def_reg_r!(MPIDR, u32, "mrc p15, 0, $0, c0, c0, 5");
def_reg_r!(MPIDR, mpidr::Read, "mrc p15, 0, $0, c0, c0, 5");
wrap_reg!(mpidr);
register_bits!(mpidr,
/// CPU core index
cpu_id, u8, 0, 1);
register_bits!(mpidr,
/// Processor index in "multi-socket" systems
cluster_id, u8, 8, 11);
register_bit!(mpidr,
/// true if part of uniprocessor system
u, 30);
pub struct DFAR;
def_reg_r!(DFAR, u32, "mrc p15, 0, $0, c6, c0, 0");

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

@ -25,6 +25,7 @@ impl<T> UncachedSlice<T> {
for page_start in (start..(start + size)).step_by(L1_PAGE_SIZE) {
L1Table::get()
.update(page_start as *const (), |l1_section| {
l1_section.tex = 0b100;
l1_section.cacheable = false;
l1_section.bufferable = false;
});

View File

@ -8,6 +8,11 @@ edition = "2018"
[features]
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"]
[dependencies]
r0 = "1"

View File

@ -1,21 +1,70 @@
use libboard_zynq::{println, slcr, stdio};
use libregister::RegisterR;
use libcortex_a9::regs::{DFSR, MPIDR};
use libboard_zynq::{println, stdio};
#[link_section = ".text.boot"]
#[no_mangle]
pub unsafe extern "C" fn PrefetchAbort() {
#[naked]
pub unsafe extern "C" fn UndefinedInstruction() {
stdio::drop_uart();
println!("PrefetchAbort");
slcr::RegisterBlock::unlocked(|slcr| slcr.soft_reset());
println!("UndefinedInstruction");
loop {}
}
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn SoftwareInterrupt() {
stdio::drop_uart();
println!("SoftwareInterrupt");
loop {}
}
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn PrefetchAbort() {
stdio::drop_uart();
println!("PrefetchAbort");
loop {}
}
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn DataAbort() {
stdio::drop_uart();
println!("DataAbort");
println!("DataAbort on core {}", MPIDR.read().cpu_id());
println!("DFSR: {:03X}", DFSR.read());
slcr::RegisterBlock::unlocked(|slcr| slcr.soft_reset());
loop {}
}
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn ReservedException() {
stdio::drop_uart();
println!("ReservedException");
loop {}
}
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
#[cfg(feature = "dummy_irq_handler")]
pub unsafe extern "C" fn IRQ() {
stdio::drop_uart();
println!("IRQ");
loop {}
}
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn FIQ() {
stdio::drop_uart();
println!("FIQ");
loop {}
}

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" {
@ -21,17 +21,15 @@ static mut CORE1_ENABLED: VolatileCell<bool> = VolatileCell::new(false);
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn _boot_cores() -> ! {
const CORE_MASK: u32 = 0x3;
match MPIDR.read() & CORE_MASK {
pub unsafe extern "C" fn Reset() -> ! {
match MPIDR.read().cpu_id() {
0 => {
SP.write(&mut __stack0_start as *mut _ as u32);
boot_core0();
}
1 => {
while !CORE1_ENABLED.get() {
asm::wfe();
spin_lock_yield();
}
SP.write(&mut __stack1_start as *mut _ as u32);
boot_core1();
@ -59,6 +57,7 @@ unsafe fn boot_core0() -> ! {
asm::dmb();
asm::dsb();
asm::enable_irq();
main_core0();
panic!("return from main");
});
@ -79,6 +78,7 @@ unsafe fn boot_core1() -> ! {
asm::dmb();
asm::dsb();
asm::enable_irq();
main_core1();
panic!("return from main_core1");
});
@ -144,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 {}
}
@ -151,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();
}
@ -158,7 +161,9 @@ impl Core1 {
pub fn restart(&self) {
slcr::RegisterBlock::unlocked(|slcr| {
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(true));
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(true));
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_rst1(false));
slcr.a9_cpu_rst_ctrl.modify(|_, w| w.a9_clkstop1(false));
});
}
}

View File

@ -9,5 +9,6 @@ pub extern crate compiler_builtins;
pub mod boot;
mod abort;
#[cfg(feature = "panic_handler")]
mod panic;
pub mod ram;

View File

@ -1,4 +1,4 @@
use libboard_zynq::{slcr, print, println};
use libboard_zynq::{print, println};
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
@ -14,6 +14,5 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
println!("");
}
slcr::RegisterBlock::unlocked(|slcr| slcr.soft_reset());
loop {}
}

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."
'';
}