Compare commits

...

42 Commits

Author SHA1 Message Date
meierj 5acb8043bf fixed merge conflicts
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
2026-05-17 15:55:05 +02:00
meierj a9c5c8de57 function to invalidate l2cache
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
2026-05-17 12:49:41 +02:00
muellerr 32e0e27ca7 Merge pull request 'prepare next zynq7000 version' (#84) from bump-zynq7000 into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #84
2026-05-15 19:30:08 +02:00
muellerr bdc4780bcc small tweak
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-15 19:29:58 +02:00
muellerr 7df10e6ea1 prepare next zynq7000 version
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-15 19:28:25 +02:00
muellerr 306ef90094 Merge pull request 'UART improvements' (#83) from update-uart-interrupt-reception into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #83
2026-05-15 14:37:36 +02:00
muellerr fcd971a7d3 UART improvements
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-15 14:37:21 +02:00
muellerr c09d75b602 Merge pull request 'zynq7000-rt and zynq7000-mmu release' (#82) from zynq7000-rt-zynq7000-mmu-release into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #82
2026-05-08 17:18:21 +02:00
muellerr c72ba780d4 zynq7000-rt and zynq7000-mmu release
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
2026-05-08 17:16:10 +02:00
muellerr 5472293907 Merge pull request 'prepare PAC release' (#81) from prep-pac-release into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #81
2026-05-08 17:09:09 +02:00
muellerr 80ad791061 prepare PAC release
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-08 17:07:08 +02:00
muellerr f78f159fde Merge pull request 'add clock enable for SPI and UART' (#80) from add-clk-enables into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #80
2026-05-08 17:00:50 +02:00
muellerr 1d66fcd077 add clock enable for SPI and UART
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-08 17:00:15 +02:00
muellerr 7bf6322fc2 Merge pull request 'improve register names' (#79) from improve-register-names into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #79
2026-05-08 16:48:13 +02:00
muellerr 90e4604187 improve register names
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-08 16:47:05 +02:00
muellerr 34727bf48e Merge pull request 'introduce interrupt registry' (#78) from interrupt-registry into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #78
2026-05-08 16:30:58 +02:00
muellerr 3bd62fc1eb introduce interrupt registry
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-08 16:29:59 +02:00
muellerr 11b37e9c2b Merge pull request 'Async OLED example' (#77) from oled-asynch into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #77
2026-05-08 14:43:49 +02:00
muellerr 9ed6ac32ce Async OLED example
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-08 14:42:50 +02:00
muellerr 7eeedec527 Merge pull request 'bump aarch32 deps' (#76) from bump-aarch32-dependencies into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #76
2026-05-08 12:55:37 +02:00
muellerr d30273aa0c bump aarch32 deps
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-08 12:53:31 +02:00
muellerr 79c6aea160 Merge pull request 'fix ping reply for net example' (#75) from fix-ping-for-net-example into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #75
2026-05-08 12:50:43 +02:00
muellerr b85e2cf1a3 fix ping reply for net example
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-08 12:50:21 +02:00
muellerr 78e2dd23f9 Merge pull request 'OLED example' (#74) from oled-driver into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #74
2026-05-07 20:04:59 +02:00
muellerr df887d5665 Add OLED example
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-07 20:01:58 +02:00
muellerr 62eebc6770 Merge pull request 'update async SPI module' (#70) from update-asynch-spi-module into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: #70
2026-05-07 18:02:27 +02:00
muellerr 5a44b3f658 update async SPI module
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-05 18:41:21 +02:00
muellerr c85f492c03 Merge pull request 'bugfix for SPI AMBA clock control' (#73) from spi-amba-clk-ctrl-bugfix into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: http://localhost:3000/rust/zynq7000-rs/pulls/73
2026-05-05 18:41:00 +02:00
muellerr 20102a2b7a bugfix for SPI AMBA clock control
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-05 18:40:42 +02:00
muellerr 110adc8a63 Merge pull request 'configure pull up properly' (#72) from oled-sdin-pullup into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: http://localhost:3000/rust/zynq7000-rs/pulls/72
2026-05-05 15:46:31 +02:00
muellerr 6fe5ac5edb configure pull up properly
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-05 15:05:38 +02:00
muellerr 923f988a32 Merge pull request 'fix asynch uart TX' (#71) from fix-asynch-uart into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: http://localhost:3000/rust/zynq7000-rs/pulls/71
2026-05-04 19:32:26 +02:00
muellerr a6c40c5fa9 fix asynch uart TX
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-04 19:31:50 +02:00
muellerr 21a930dc09 Merge pull request 'update asynch logger, simplify it' (#68) from update-asynch-logger into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: http://localhost:3000/rust/zynq7000-rs/pulls/68
2026-05-04 18:01:29 +02:00
muellerr 8215eec0cf clippy fix
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-04 17:54:44 +02:00
muellerr bf6f492c49 update asynch logger, simplify it
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-04 17:02:22 +02:00
muellerr f7074bcf7d Merge pull request 'update BD and XDC' (#69) from update-hw-design into main
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
Reviewed-on: http://localhost:3000/rust/zynq7000-rs/pulls/69
2026-05-04 16:57:46 +02:00
muellerr 349509c90c some minor clean up
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-04 16:56:31 +02:00
muellerr c38c98dfb0 update BD and XDC
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
ci / Check build (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
2026-05-04 16:39:33 +02:00
muellerr 69537126ec formatting
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
2026-04-15 18:14:20 +02:00
muellerr 004e862715 Merge pull request 'Function to set FIFO Trigger of PS UART' (#67) from meier/ps-uart-set-fifo-trigger into main
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
Reviewed-on: #67
2026-04-15 10:02:46 +02:00
meierj f1312c1b17 added pub function to set the rx fifo trigger of the ps uart
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
2026-04-14 17:01:39 +02:00
87 changed files with 2353 additions and 1205 deletions
+4
View File
@@ -4,6 +4,10 @@ Zynq 7000 Bare-Metal Rust Support
This crate collection provides support to write bare-metal Rust applications for the AMD Zynq 7000
family of SoCs.
<p align="center">
<img src="./ferris-zedboard.jpeg" alt="Ferris on the Zedboard" width="400" />
</p>
# List of crates
This project contains the following crates:
Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

+2 -2
View File
@@ -9,7 +9,7 @@ repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
[dependencies]
aarch32-cpu = { version = "0.2" }
aarch32-cpu = { version = "0.3", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal", features = ["defmt"] }
@@ -17,5 +17,5 @@ defmt = "1"
defmt-rtt = "1"
embedded-io = "0.7"
embedded-hal = "1"
fugit = "0.3"
fugit = "0.4"
log = "0.4"
+7
View File
@@ -27,3 +27,10 @@ SECTIONS
_ebss_uncached = .;
} > UNCACHED
}
PROVIDE(_und_stack_size = 2K);
PROVIDE(_svc_stack_size = 2K);
PROVIDE(_abt_stack_size = 2K);
PROVIDE(_hyp_stack_size = 1K);
PROVIDE(_sys_stack_size = 32K);
+4 -2
View File
@@ -11,18 +11,20 @@ keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
aarch32-cpu = { version = "0.2", features = ["critical-section-single-core"] }
aarch32-cpu = { version = "0.3", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }
zynq7000-embassy = { path = "../../zynq7000-embassy" }
dht-sensor = { git = "https://github.com/michaelbeaumont/dht-sensor.git", rev = "10319bdeae9ace3bb0fc79a15da2869c5bf50f52", features = ["async"] }
embedded-hal-async = "1"
embassy-sync = "0.8"
static_cell = "2"
critical-section = "1"
heapless = "0.9"
embedded-io = "0.7"
embedded-hal = "1"
fugit = "0.3"
fugit = "0.4"
log = "0.4"
embassy-executor = { version = "0.10", features = [
@@ -12,7 +12,8 @@ use log::{error, info, warn};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
generic_interrupt_handler,
gic::Configurator,
gpio::{Flex, Output, PinState, mio},
gtc::GlobalTimerCounter,
l2_cache,
@@ -45,7 +46,7 @@ async fn main(_spawner: Spawner) -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -157,23 +158,12 @@ async fn main(_spawner: Spawner) -> ! {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(_spi_interrupt) => (),
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
@@ -8,7 +8,9 @@ use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::{error, info};
use zynq7000_hal::{BootMode, InteruptConfig, clocks, gic, gpio, gtc, time::Hertz, uart};
use zynq7000_hal::{
BootMode, InteruptConfig, clocks, generic_interrupt_handler, gpio, gtc, time::Hertz, uart,
};
use zynq7000_rt as _;
@@ -71,23 +73,12 @@ async fn main(_spawner: Spawner) -> ! {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = gic::GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
gic::Interrupt::Sgi(_) => (),
gic::Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
gic::Interrupt::Spi(_spi_interrupt) => (),
gic::Interrupt::Invalid(_) => (),
gic::Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
@@ -13,12 +13,13 @@ use zynq7000::Peripherals;
use zynq7000_hal::{
BootMode,
clocks::Clocks,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
generic_interrupt_handler,
gic::Configurator,
gpio::{Output, PinState, mio},
gtc::GlobalTimerCounter,
l2_cache,
time::Hertz,
uart::{ClockConfig, Config, TxAsync, Uart, on_interrupt_tx},
uart::{ClockConfig, Config, TxAsync, Uart},
};
use zynq7000_rt as _;
@@ -40,7 +41,7 @@ async fn main(spawner: Spawner) -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -68,56 +69,64 @@ async fn main(spawner: Spawner) -> ! {
uart.flush().unwrap();
let (tx, _rx) = uart.split();
let mut logger = TxAsync::new(tx);
let mut logger = TxAsync::new(tx, true);
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace).unwrap();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
let led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
spawner.spawn(led_task(led).unwrap());
spawner.spawn(hello_task().unwrap());
let mut log_buf: [u8; 2048] = [0; 2048];
let frame_queue = zynq7000_hal::log::rb::get_frame_queue();
loop {
let next_frame_len = frame_queue.receive().await;
zynq7000_hal::log::rb::read_next_frame(next_frame_len, &mut log_buf);
logger.write(&log_buf[0..next_frame_len]).await;
let read_bytes = log_reader.read(&mut log_buf).await;
if read_bytes > 0 {
// Unwrap okay, checked that size is larger than 0.
logger.write(&log_buf[0..read_bytes]).unwrap().await;
}
}
}
#[embassy_executor::task]
async fn led_task(mut mio_led: Output) {
static ATOMIC_COUNTER: core::sync::atomic::AtomicUsize =
core::sync::atomic::AtomicUsize::new(0);
let mut ticker = Ticker::every(Duration::from_millis(1000));
loop {
mio_led.toggle().unwrap();
info!("Toggling LED");
info!(
"Toggling LED ({})",
ATOMIC_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
);
ticker.next().await;
}
}
#[embassy_executor::task]
async fn hello_task() {
static ATOMIC_COUNTER: core::sync::atomic::AtomicUsize =
core::sync::atomic::AtomicUsize::new(0);
let mut ticker = Ticker::every(Duration::from_millis(1000));
loop {
info!(
"Hello from another task ({})",
ATOMIC_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
);
ticker.next().await;
}
}
#[unsafe(no_mangle)]
pub extern "C" fn _irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(spi_interrupt) => {
if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Uart1 {
on_interrupt_tx(zynq7000_hal::uart::UartId::Uart1);
}
}
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
+8 -18
View File
@@ -18,7 +18,8 @@ use log::{error, info};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
generic_interrupt_handler,
gic::Configurator,
gpio::{Output, PinState, mio},
gtc::GlobalTimerCounter,
l2_cache,
@@ -46,7 +47,7 @@ async fn main(_spawner: Spawner) -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -107,23 +108,12 @@ async fn main(_spawner: Spawner) -> ! {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(_spi_interrupt) => (),
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
+5 -16
View File
@@ -7,7 +7,7 @@ use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use log::error;
use zynq7000_hal::{InteruptConfig, clocks, gic, gpio, gtc, time::Hertz};
use zynq7000_hal::{InteruptConfig, clocks, generic_interrupt_handler, gpio, gtc, time::Hertz};
// Define the clock frequency as a constant
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
@@ -42,22 +42,11 @@ async fn main(_spawner: Spawner) -> ! {
#[zynq7000_rt::irq]
pub fn irq_handler() {
let mut gic_helper = gic::GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
gic::Interrupt::Sgi(_) => (),
gic::Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
gic::Interrupt::Spi(_spi_interrupt) => (),
gic::Interrupt::Invalid(_) => (),
gic::Interrupt::Spurious => (),
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
+2 -2
View File
@@ -9,11 +9,11 @@ repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
[dependencies]
aarch32-cpu = { version = "0.2" }
aarch32-cpu = { version = "0.3" }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }
embedded-io = "0.7"
embedded-hal = "1"
fugit = "0.3"
fugit = "0.4"
log = "0.4"
+6
View File
@@ -23,3 +23,9 @@ SECTIONS
_ebss_uncached = .;
} > UNCACHED
}
PROVIDE(_und_stack_size = 2K);
PROVIDE(_svc_stack_size = 2K);
PROVIDE(_abt_stack_size = 2K);
PROVIDE(_hyp_stack_size = 1K);
PROVIDE(_sys_stack_size = 32K);
@@ -8,8 +8,9 @@ use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::{error, info};
use zynq7000_hal::{
Interrupt,
clocks::Clocks,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
gic,
gpio::{Output, PinState, mio},
gtc::GlobalTimerCounter,
l2_cache,
@@ -31,7 +32,7 @@ fn main() -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = gic::Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -83,8 +84,8 @@ fn main() -> ! {
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
let mut gic_helper = gic::InterruptGuard::new();
let irq_info = gic_helper.interrupt_info();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
+4 -5
View File
@@ -10,7 +10,7 @@ use log::{error, info};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
gic::{self, Configurator, Interrupt},
gpio::{Output, PinState, mio},
gtc::GlobalTimerCounter,
l2_cache,
@@ -32,7 +32,7 @@ fn main() -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -85,13 +85,12 @@ fn main() -> ! {
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
let mut gic_helper = gic::InterruptGuard::new();
let irq_info = gic_helper.interrupt_info();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
// TODO: Call embassy on interrupt handler here soon.
MS_TICKS.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
}
}
+9 -3
View File
@@ -11,7 +11,7 @@ keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
aarch32-cpu = { version = "0.2", features = ["critical-section-single-core"] }
aarch32-cpu = { version = "0.3", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../../zynq7000-rt" }
zynq7000 = { path = "../../zynq7000" }
zynq7000-hal = { path = "../../zynq7000-hal" }
@@ -27,14 +27,20 @@ critical-section = "1"
static_cell = "2"
embedded-alloc = "0.7"
embedded-hal = "1"
embedded-hal-bus = { version = "0.3", features = ["async"] }
embedded-hal-async = "1"
fugit = "0.3"
dummy-pin = "1"
fugit = "0.4"
fugit-03 = { version = "0.3", package = "fugit" }
embedded-graphics = "0.8"
log = "0.4"
ssd1306 = { version = "0.10", features = ["async"] }
tinybmp = "0.7"
rand = { version = "0.10", default-features = false }
embassy-executor = { version = "0.10", features = ["platform-cortex-ar", "executor-thread"] }
embassy-time = { version = "0.5", features = ["tick-hz-1_000_000"] }
embassy-net = { version = "0.9", features = ["dhcpv4", "packet-trace", "medium-ethernet", "icmp", "tcp", "udp"] }
embassy-net = { version = "0.9", features = ["dhcpv4", "packet-trace", "medium-ethernet", "icmp", "tcp", "udp", "auto-icmp-echo-reply"] }
embedded-sdmmc = { git = "https://github.com/robamu/embedded-sdmmc-rs.git", branch = "all-features" }
embassy-sync = { version = "0.8" }
heapless = "0.9"
Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
version="1.1"
width="0.16in" height="0.106667in"
viewBox="0 0 48 32">
<defs>
</defs>
<image id="raster0"
x="0"
y="0"
width="48"
height="32"
opacity="1.000000"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAgCAQAAAD+3TOXAAAHDmVYSWZJSSoACAAAAAAADgAAAAkA/gAEAAEAAAABAAAAAAEEAAEAAAAAAQAAAQEEAAEAAACqAAAAAgEDAAMAAACAAAAAAwEDAAEAAAAGAAAABgEDAAEAAAAGAAAAFQEDAAEAAAADAAAAAQIEAAEAAACGAAAAAgIEAAEAAACIBgAAAAAAAAgACAAIAP/Y/+AAEEpGSUYAAQEAAAEAAQAA/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy/9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy/8AAEQgAqgEAAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A+f6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAr6/+CX/ACSHQv8At4/9KJK+QK+v/gl/ySHQv+3j/wBKJKAPnzxr8Jte8B6NDqmqXemzQS3C26rayOzBirNk7kUYwh7+lcHX0/8AtHf8k80//sKx/wDoqWvmCgAroPAn/JQ/DX/YVtf/AEatc/XQeBP+Sh+Gv+wra/8Ao1aAPqP4s+CtS8eeFbXS9LntIZ4r1LhmunZVKhHXA2qxzlx29a+VPFPhu88I+I7vQ7+SCS6tdm94GJQ7kVxgkA9GHavuevkD42/8le13/t3/APSeOgDz+iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK+g/hL8WdB0rw74f8Hz2mpNqD3BtxIkaGLdLOxU5Lg4+cZ49etfPlWLC+uNM1G2v7OTy7q1lSaF9oO11IKnB4OCB1oA+968j+J3xZ0HSofEXg+e01JtQeye3EiRoYt0sOVOS4OPnGePXrXjn/AAu34h/9DD/5JW//AMbrj9b1vUfEesT6tq1x9ovp9vmS7FTdtUKOFAA4AHAoAz6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9mZmF3EAAAAAXNSR0IB2cksfwAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAhhJREFUSMft1T1I1WEUBvDfVQn1hoHoDQlMqCSlaLAiCKMhbIpqKLCtTQKJoLWh76GgpQ+isKWMgoiiocgpKoKEoCHCoHJJ1GjRAvu4p8GPrt57Ta/W5HmW9/w553ne9z3nf14WbJ6tXMu/or7vPM76aoVavbbMH3W/NtXSwnU/hJd6hA5LPXZ0btQbJNQLYVBMQdqgcBc1agoVCD1uZ1FnYtAt313+O9WiPAIzw8UpeUnnbEZy1C1WpskzqzJCLvnozowF3nnkm4ax3OU+CJU4IQU3bFMuDGuaEDgyY/I/GN1vwhshUKbf3hJb7TOiy4CUbgmstER61vXqsxpvbZo4yXEpQZuQtt4LIdTjaQG7H0erPWOrDmFENc1C6NUlhEYcKJj+s3IpPyf8U6NHuTDxoQ8NDhUs8FqzSmfGvIdKxu+vzZAQ2rXnS45pvEkYViuE04oyS1TlldAs6WM+gcjrTcJJXcL27C6o0yvtis45CaQ9Fw7mbrRlHsyhf/7gZiZpccZ6SNKOeZi+a5R6kutPapEWwq95OMW+cdLMSo9IgGO+ueTLdNMvhw1M8u7JOz2HVQuh1G7XsnsqR3n7ddqpGKFp6g4SWRKw2LAKQ6DGOiusVaF1SuxVfd7q1pORW+mT0izWHLdQ53ABz1OxEMr/HhiSNs6SvkSjIu9zFyiXRCG2KzuvKGdgwn4VBQhU5eHLI7Jg/8d+A9OUD/TqXTt5AAAAAElFTkSuQmCCAA==" />
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.4 KiB

+7
View File
@@ -23,3 +23,10 @@ SECTIONS
_ebss_uncached = .;
} > UNCACHED
}
PROVIDE(_und_stack_size = 2K);
PROVIDE(_svc_stack_size = 2K);
PROVIDE(_abt_stack_size = 2K);
PROVIDE(_hyp_stack_size = 1K);
PROVIDE(_irq_stack_size = 2K);
PROVIDE(_sys_stack_size = 32K);
+26 -31
View File
@@ -42,7 +42,8 @@ use zynq7000_hal::{
eth::{
AlignedBuffer, ClockDivSet, EthernetConfig, EthernetLowLevel, embassy_net::InterruptResult,
},
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
generic_interrupt_handler,
gic::{Configurator, Interrupt},
gpio::{GpioPins, Output, PinState},
gtc::GlobalTimerCounter,
l2_cache,
@@ -216,7 +217,7 @@ async fn main(spawner: Spawner) -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -278,6 +279,12 @@ async fn main(spawner: Spawner) -> ! {
"Calculated RGMII clock configuration: {:?}, errors (missmatch from ideal rate in hertz): {:?}",
clk_divs, clk_errors
);
zynq7000_hal::register_interrupt(
Interrupt::Spi(zynq7000_hal::gic::SpiInterrupt::Eth0),
custom_eth_interupt_handler,
);
// Unwrap okay, we use a standard clock config, and the clock config should never fail.
let eth_cfg = EthernetConfig::new(
zynq7000_hal::eth::ClockConfig::new(clk_divs.cfg_1000_mbps),
@@ -461,36 +468,24 @@ async fn main(spawner: Spawner) -> ! {
}
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(spi_interrupt) => {
if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Eth0 {
// This generic library provided interrupt handler takes care of waking
// the driver on received or sent frames while also reporting anomalies
// and errors.
let result = zynq7000_hal::eth::embassy_net::on_interrupt(
zynq7000_hal::eth::EthernetId::Eth0,
);
if result.has_errors() {
ETH_ERR_QUEUE.try_send(result).ok();
}
}
}
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
// Safety: Only called by interrupt handler, registered in global interrupt handler map.
unsafe fn custom_eth_interupt_handler() {
// This generic library provided interrupt handler takes care of waking
// the driver on received or sent frames while also reporting anomalies
// and errors.
let result = zynq7000_hal::eth::embassy_net::on_interrupt(zynq7000_hal::eth::EthernetId::Eth0);
if result.has_errors() {
ETH_ERR_QUEUE.try_send(result).ok();
}
}
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
@@ -20,8 +20,8 @@ use log::{error, info};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
configure_level_shifter,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
configure_level_shifter, generic_interrupt_handler,
gic::Configurator,
gpio::{GpioPins, Output, PinState},
gtc::GlobalTimerCounter,
i2c, l2_cache,
@@ -54,7 +54,7 @@ async fn main(_spawner: Spawner) -> ! {
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -153,24 +153,13 @@ async fn main(_spawner: Spawner) -> ! {
}
}
#[unsafe(no_mangle)]
pub extern "C" fn _irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(_spi_interrupt) => (),
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[unsafe(no_mangle)]
@@ -12,6 +12,7 @@
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use embassy_executor::Spawner;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_time::{Delay, Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_hal_async::delay::DelayNs;
@@ -20,14 +21,14 @@ use log::{error, info};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
configure_level_shifter,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
configure_level_shifter, generic_interrupt_handler,
gic::Configurator,
gpio::{GpioPins, Output, PinState},
gtc::GlobalTimerCounter,
l2_cache,
spi::{self, SpiAsync, SpiId, SpiWithHwCs, SpiWithHwCsAsync, on_interrupt},
spi::{self, SpiAsync, SpiWithHwCs, SpiWithHwCsAsync},
time::Hertz,
uart::{self, TxAsync, on_interrupt_tx},
uart::{self, TxAsync},
};
use zynq7000::{Peripherals, slcr::LevelShifterConfig, spi::DelayControl};
@@ -56,15 +57,20 @@ async fn main(spawner: Spawner) -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let mut clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// SPI reference clock must be larger than the CPU 1x clock.
let spi_ref_clk_div = spi::calculate_largest_allowed_spi_ref_clk_divisor(&clocks)
.unwrap()
.value()
- 1;
spi::configure_spi_ref_clk(&mut clocks, arbitrary_int::u6::new(spi_ref_clk_div as u8));
let target_spi_ref_clock = clocks.arm_clocks().cpu_1x_clk() * 2;
// SPI reference clock must be larger than the CPU 1x clock. Also, taking the largest value
// actually seems to be problematic. We take 200 MHz here, which is significantly larger than
// the CPU 1x clock which is around 110 MHz.
spi::configure_spi_ref_clock(&mut clocks, target_spi_ref_clock);
assert!(
clocks.io_clocks().spi_clk().to_raw()
> (clocks.arm_clocks().cpu_1x_clk().to_raw() as f32 * 1.2) as u32,
"SPI reference clock must be larger than CPU 1x clock"
);
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -90,30 +96,29 @@ async fn main(spawner: Spawner) -> ! {
.unwrap();
uart.write_all(b"-- Zynq 7000 Zedboard SPI L3GD20H example --\n\r")
.unwrap();
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace).unwrap();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
if DEBUG_SPI_CLK_CONFIG {
info!(
"SPI Clock Information: CPU 1x: {:?}, IO Ref Clk: {:?}, SPI Ref Clk: {:?}, DIV: {:?}",
"SPI Clock Information: CPU 1x: {:?}, IO Ref Clk: {:?}, SPI Ref Clk: {:?}",
clocks.arm_clocks().cpu_1x_clk(),
clocks.io_clocks().ref_clk(),
clocks.io_clocks().spi_clk(),
spi_ref_clk_div
);
}
let mut spi = spi::Spi::new_one_hw_cs(
dp.spi_1,
clocks.io_clocks(),
spi::Config::new(
// 10 MHz maximum rating of the sensor.
zynq7000::spi::BaudDivSel::By64,
//l3gd20::MODE,
embedded_hal::spi::MODE_3,
spi::SlaveSelectConfig::AutoWithAutoStart,
spi::SlaveSelectConfig::AutoCsAutoStart,
),
(
gpio_pins.mio.mio12,
@@ -123,10 +128,13 @@ async fn main(spawner: Spawner) -> ! {
gpio_pins.mio.mio13,
)
.unwrap();
let sclk = Hertz::from_raw(
clocks.io_clocks().spi_clk().to_raw() / zynq7000::spi::BaudDivSel::By64.div_value() as u32,
);
let mod_id = spi.regs().read_mod_id();
assert_eq!(mod_id, spi::MODULE_ID);
assert!(spi.sclk() <= Hertz::from_raw(10_000_000));
let min_delay = (spi.sclk().raw() * 5) / 1_000_000_000;
assert!(sclk <= Hertz::from_raw(10_000_000));
let min_delay = (sclk.to_raw() * 5) / 1_000_000_000;
spi.inner().configure_delays(
DelayControl::builder()
.with_inter_word_cs_deassert(0)
@@ -155,7 +163,7 @@ async fn main(spawner: Spawner) -> ! {
}
}
spawner.spawn(logger_task(uart).unwrap());
spawner.spawn(logger_task(uart, log_reader).unwrap());
if BLOCKING {
blocking_application(mio_led, emio_leds, spi).await;
} else {
@@ -164,15 +172,18 @@ async fn main(spawner: Spawner) -> ! {
}
#[embassy_executor::task]
pub async fn logger_task(uart: uart::Uart) {
pub async fn logger_task(
uart: uart::Uart,
reader: embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>,
) -> ! {
let (tx, _) = uart.split();
let mut tx_async = TxAsync::new(tx);
let frame_queue = zynq7000_hal::log::rb::get_frame_queue();
let mut tx_async = TxAsync::new(tx, true);
let mut log_buf: [u8; 2048] = [0; 2048];
loop {
let next_frame_len = frame_queue.receive().await;
zynq7000_hal::log::rb::read_next_frame(next_frame_len, &mut log_buf);
tx_async.write(&log_buf[0..next_frame_len]).await;
let read_bytes = reader.read(&mut log_buf).await;
if read_bytes > 0 {
tx_async.write(&log_buf[0..read_bytes]).unwrap().await;
}
}
}
@@ -237,30 +248,13 @@ pub async fn non_blocking_application(
}
}
#[unsafe(no_mangle)]
pub extern "C" fn _irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(spi_interrupt) => {
if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Spi1 {
on_interrupt(SpiId::Spi1);
} else if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Uart1 {
on_interrupt_tx(zynq7000_hal::uart::UartId::Uart1);
}
}
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[unsafe(no_mangle)]
@@ -0,0 +1,284 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use dummy_pin::DummyPin;
use embassy_executor::Spawner;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_time::{Delay, Duration, Ticker};
use embedded_graphics::{Drawable as _, geometry::Point};
use embedded_hal::digital::StatefulOutputPin;
use embedded_hal_async::delay::DelayNs as _;
use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
use embedded_io::Write;
use log::{error, info};
use ssd1306::{Ssd1306Async, prelude::*};
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::{
BootMode, clocks, generic_interrupt_handler, gpio, gtc,
spi::{self, SpiAsync},
time::Hertz,
uart::{self, TxAsync},
};
use embedded_graphics::image::Image;
use tinybmp::Bmp;
use zynq7000_rt as _;
const INIT_STRING: &str = "-- Zynq 7000 Zedboard OLED example --\n\r";
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
interrupt_config: Some(zynq7000_hal::InteruptConfig::AllInterruptsToCpu0),
})
.unwrap();
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let mut clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
let target_spi_ref_clock = clocks.arm_clocks().cpu_1x_clk() * 2;
// SPI reference clock must be larger than the CPU 1x clock. Also, taking the largest value
// actually seems to be problematic. We take 200 MHz here, which is significantly larger than
// the CPU 1x clock which is around 110 MHz.
spi::configure_spi_ref_clock(&mut clocks, target_spi_ref_clock);
assert!(
clocks.io_clocks().spi_clk().to_raw()
> (clocks.arm_clocks().cpu_1x_clk().to_raw() as f32 * 1.2) as u32,
"SPI reference clock must be larger than CPU 1x clock"
);
let mut gpio_pins = gpio::GpioPins::new(periphs.gpio);
// Set up global timer counter and embassy time driver.
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
// Set up the UART, we are logging with it.
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio_for_uart_1(
periphs.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
uart.flush().unwrap();
// Safety: We are not multi-threaded yet.
let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace)
.expect("Failed to initialize async logger");
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
info!(
"SPI reference clock speed: {:?}",
clocks.io_clocks().spi_clk()
);
spawner.spawn(logger_task(uart, log_reader).unwrap());
let mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low);
let emio_leds: [gpio::Output; 8] = [
gpio::Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), gpio::PinState::Low),
];
spawner.spawn(blinky_task(mio_led, emio_leds).unwrap());
let dc_pin = gpio::Output::new_for_emio(gpio_pins.emio.take(11).unwrap(), gpio::PinState::High);
let mut reset_pin =
gpio::Output::new_for_emio(gpio_pins.emio.take(12).unwrap(), gpio::PinState::High);
let mut oled_vdd_switch =
gpio::Output::new_for_emio(gpio_pins.emio.take(13).unwrap(), gpio::PinState::High);
let mut oled_vbat_switch =
gpio::Output::new_for_emio(gpio_pins.emio.take(14).unwrap(), gpio::PinState::High);
Delay.delay_ms(100).await;
let spi = spi::Spi::new_for_emio(
periphs.spi_0,
spi::Config::calculate_for_io_clock(
Hertz::MHz(8),
clocks.io_clocks(),
embedded_hal::spi::MODE_0,
spi::SlaveSelectConfig::AutoCsManualStart,
),
)
.expect("Failed to initialize SPI");
let spi_asynch = SpiAsync::new(spi);
let exclusive_device = ExclusiveDevice::new(spi_asynch, DummyPin::new_high(), NoDelay)
.expect("Failed to create exclusive SPI device");
let spi_if = SPIInterface::new(exclusive_device, dc_pin);
let mut ssd1306 = Ssd1306Async::new(spi_if, DisplaySize128x32, DisplayRotation::Rotate180);
oled_vdd_switch.set_low();
oled_vbat_switch.set_low();
Delay.delay_ms(100).await;
ssd1306
.reset(&mut reset_pin, &mut embassy_time::Delay {})
.await
.expect("display reset error");
let mut display = ssd1306.into_buffered_graphics_mode();
display.init().await.expect("display init error");
// Include the BMP file data.
let ferris_data = include_bytes!("../../assets/ferris-flat-happy-small.bmp");
let rust_logo_data = include_bytes!("../../assets/rust-logo-single-path.bmp");
// Parse the BMP file.
let bmp_rust = Bmp::from_slice(rust_logo_data).expect("BMP loading error");
let bmp_ferris = Bmp::from_slice(ferris_data).expect("BMP loading error");
// Draw the image with the top left corner at (10, 20) by wrapping it in
// an embedded-graphics `Image`.
Image::new(&bmp_rust, Point::new(0, 0))
.draw(&mut display)
.expect("image drawing error");
Image::new(&bmp_ferris, Point::new(32, 0))
.draw(&mut display)
.expect("image drawing error");
display.flush().await.unwrap();
let mut ticker = Ticker::every(Duration::from_millis(50));
let mut ferris = FerrisMovement::new(32, Direction::Right, 32, 76);
loop {
Image::new(&bmp_ferris, Point::new(ferris.pos as i32, 0))
.draw(&mut display)
.expect("image drawing error");
display.flush().await.expect("flush error");
ferris.step();
ticker.next().await;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Right,
Left,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FerrisMovement {
pub pos: u16,
pub dir: Direction,
pub left_threshold: u16,
pub right_threshold: u16,
}
impl FerrisMovement {
/// Creates a new movement controller.
/// Assumes: left_threshold <= right_threshold and pos is within that range.
pub fn new(pos: u16, dir: Direction, left_threshold: u16, right_threshold: u16) -> Self {
Self {
pos,
dir,
left_threshold,
right_threshold,
}
}
/// Move one tick and "bounce" between thresholds.
pub fn step(&mut self) {
match self.dir {
Direction::Right => {
if self.pos >= self.right_threshold {
self.dir = Direction::Left; // flip at right boundary
} else {
self.pos += 1;
}
}
Direction::Left => {
if self.pos <= self.left_threshold {
self.dir = Direction::Right; // flip at left boundary
} else {
self.pos -= 1;
}
}
}
}
}
#[embassy_executor::task]
pub async fn logger_task(
uart: uart::Uart,
reader: embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>,
) -> ! {
let (tx, _) = uart.split();
let mut tx_async = TxAsync::new(tx, true);
let mut log_buf: [u8; 2048] = [0; 2048];
loop {
let read_bytes = reader.read(&mut log_buf).await;
if read_bytes > 0 {
tx_async.write(&log_buf[0..read_bytes]).unwrap().await;
}
}
}
#[embassy_executor::task]
pub async fn blinky_task(mut mio_led: gpio::Output, mut emio_leds: [gpio::Output; 8]) {
let mut ticker = Ticker::every(Duration::from_millis(200));
loop {
mio_led.toggle().unwrap();
// Create a wave pattern for emio_leds
for led in emio_leds.iter_mut() {
led.toggle().unwrap();
ticker.next().await; // Wait for the next ticker for each toggle
}
ticker.next().await;
}
}
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(Undefined)]
fn undefined_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(PrefetchAbort)]
fn prefetch_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
error!("Panic: {info:?}");
loop {}
}
+279
View File
@@ -0,0 +1,279 @@
#![no_std]
#![no_main]
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use dummy_pin::DummyPin;
use embassy_executor::Spawner;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_time::{Delay, Duration, Ticker};
use embedded_graphics::{Drawable as _, geometry::Point};
use embedded_hal::digital::StatefulOutputPin;
use embedded_hal_async::delay::DelayNs as _;
use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
use embedded_io::Write;
use log::{error, info};
use ssd1306::{Ssd1306, prelude::*};
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::{
BootMode, clocks, generic_interrupt_handler, gpio, gtc, spi,
time::Hertz,
uart::{self, TxAsync},
};
use embedded_graphics::image::Image;
use tinybmp::Bmp;
use zynq7000_rt as _;
const INIT_STRING: &str = "-- Zynq 7000 Zedboard OLED example --\n\r";
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
fn entry_point() -> ! {
main();
}
#[embassy_executor::main]
async fn main(spawner: Spawner) -> ! {
let periphs = zynq7000_hal::init(zynq7000_hal::Config {
init_l2_cache: true,
level_shifter_config: Some(zynq7000_hal::LevelShifterConfig::EnableAll),
interrupt_config: Some(zynq7000_hal::InteruptConfig::AllInterruptsToCpu0),
})
.unwrap();
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let mut clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
let target_spi_ref_clock = clocks.arm_clocks().cpu_1x_clk() * 2;
// SPI reference clock must be larger than the CPU 1x clock. Also, taking the largest value
// actually seems to be problematic. We take 200 MHz here, which is significantly larger than
// the CPU 1x clock which is around 110 MHz.
spi::configure_spi_ref_clock(&mut clocks, target_spi_ref_clock);
assert!(
clocks.io_clocks().spi_clk().to_raw()
> (clocks.arm_clocks().cpu_1x_clk().to_raw() as f32 * 1.2) as u32,
"SPI reference clock must be larger than CPU 1x clock"
);
let mut gpio_pins = gpio::GpioPins::new(periphs.gpio);
// Set up global timer counter and embassy time driver.
let gtc = gtc::GlobalTimerCounter::new(periphs.gtc, clocks.arm_clocks());
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
// Set up the UART, we are logging with it.
let uart_clk_config = uart::ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
.unwrap()
.0;
let mut uart = uart::Uart::new_with_mio_for_uart_1(
periphs.uart_1,
uart::Config::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
uart.flush().unwrap();
// Safety: We are not multi-threaded yet.
let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace)
.expect("Failed to initialize async logger");
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
info!(
"SPI reference clock speed: {:?}",
clocks.io_clocks().spi_clk()
);
spawner.spawn(logger_task(uart, log_reader).unwrap());
let mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low);
let emio_leds: [gpio::Output; 8] = [
gpio::Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), gpio::PinState::Low),
gpio::Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), gpio::PinState::Low),
];
spawner.spawn(blinky_task(mio_led, emio_leds).unwrap());
let dc_pin = gpio::Output::new_for_emio(gpio_pins.emio.take(11).unwrap(), gpio::PinState::High);
let mut reset_pin =
gpio::Output::new_for_emio(gpio_pins.emio.take(12).unwrap(), gpio::PinState::High);
let mut oled_vdd_switch =
gpio::Output::new_for_emio(gpio_pins.emio.take(13).unwrap(), gpio::PinState::High);
let mut oled_vbat_switch =
gpio::Output::new_for_emio(gpio_pins.emio.take(14).unwrap(), gpio::PinState::High);
Delay.delay_ms(100).await;
let spi = spi::Spi::new_for_emio(
periphs.spi_0,
spi::Config::calculate_for_io_clock(
Hertz::MHz(8),
clocks.io_clocks(),
embedded_hal::spi::MODE_0,
spi::SlaveSelectConfig::AutoCsManualStart,
),
)
.expect("Failed to initialize SPI");
let exclusive_device = ExclusiveDevice::new(spi, DummyPin::new_high(), NoDelay)
.expect("Failed to create exclusive SPI device");
let spi_if = SPIInterface::new(exclusive_device, dc_pin);
let mut ssd1306 = Ssd1306::new(spi_if, DisplaySize128x32, DisplayRotation::Rotate180);
oled_vdd_switch.set_low();
oled_vbat_switch.set_low();
Delay.delay_ms(100).await;
ssd1306.reset(&mut reset_pin, &mut embassy_time::Delay {});
let mut display = ssd1306.into_buffered_graphics_mode();
display.init().expect("display init error");
// Include the BMP file data.
let ferris_data = include_bytes!("../../assets/ferris-flat-happy-small.bmp");
let rust_logo_data = include_bytes!("../../assets/rust-logo-single-path.bmp");
// Parse the BMP file.
let bmp_rust = Bmp::from_slice(rust_logo_data).unwrap();
let bmp_ferris = Bmp::from_slice(ferris_data).unwrap();
// Draw the image with the top left corner at (10, 20) by wrapping it in
// an embedded-graphics `Image`.
Image::new(&bmp_rust, Point::new(0, 0))
.draw(&mut display)
.expect("image draw error");
Image::new(&bmp_ferris, Point::new(32, 0))
.draw(&mut display)
.expect("image draw error");
display.flush().expect("display flush error");
let mut ticker = Ticker::every(Duration::from_millis(50));
let mut ferris = FerrisMovement::new(32, Direction::Right, 32, 76);
loop {
Image::new(&bmp_ferris, Point::new(ferris.pos as i32, 0))
.draw(&mut display)
.expect("image draw error");
display.flush().expect("display flush error");
ferris.step();
ticker.next().await;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Right,
Left,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FerrisMovement {
pub pos: u16,
pub dir: Direction,
pub left_threshold: u16,
pub right_threshold: u16,
}
impl FerrisMovement {
/// Creates a new movement controller.
/// Assumes: left_threshold <= right_threshold and pos is within that range.
pub fn new(pos: u16, dir: Direction, left_threshold: u16, right_threshold: u16) -> Self {
Self {
pos,
dir,
left_threshold,
right_threshold,
}
}
/// Move one tick and "bounce" between thresholds.
pub fn step(&mut self) {
match self.dir {
Direction::Right => {
if self.pos >= self.right_threshold {
self.dir = Direction::Left; // flip at right boundary
} else {
self.pos += 1;
}
}
Direction::Left => {
if self.pos <= self.left_threshold {
self.dir = Direction::Right; // flip at left boundary
} else {
self.pos -= 1;
}
}
}
}
}
#[embassy_executor::task]
pub async fn logger_task(
uart: uart::Uart,
reader: embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>,
) -> ! {
let (tx, _) = uart.split();
let mut tx_async = TxAsync::new(tx, true);
let mut log_buf: [u8; 2048] = [0; 2048];
loop {
let read_bytes = reader.read(&mut log_buf).await;
if read_bytes > 0 {
tx_async.write(&log_buf[0..read_bytes]).unwrap().await;
}
}
}
#[embassy_executor::task]
pub async fn blinky_task(mut mio_led: gpio::Output, mut emio_leds: [gpio::Output; 8]) {
let mut ticker = Ticker::every(Duration::from_millis(200));
loop {
mio_led.toggle().unwrap();
// Create a wave pattern for emio_leds
for led in emio_leds.iter_mut() {
led.toggle().unwrap();
ticker.next().await; // Wait for the next ticker for each toggle
}
ticker.next().await;
}
}
#[zynq7000_rt::irq]
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
}
#[zynq7000_rt::exception(DataAbort)]
fn data_abort_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(Undefined)]
fn undefined_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
#[zynq7000_rt::exception(PrefetchAbort)]
fn prefetch_handler(_faulting_addr: usize) -> ! {
loop {
nop();
}
}
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
error!("Panic: {info:?}");
loop {}
}
+8 -17
View File
@@ -11,7 +11,9 @@ use embedded_io::Write;
use log::{error, info};
use zedboard::PS_CLOCK_FREQUENCY;
use zedboard_bsp::qspi_spansion;
use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, prelude::*, qspi, uart};
use zynq7000_hal::{
BootMode, clocks, generic_interrupt_handler, gpio, gtc, prelude::*, qspi, uart,
};
use zynq7000_rt as _;
@@ -193,23 +195,12 @@ async fn main(_spawner: Spawner) -> ! {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = gic::GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
gic::Interrupt::Sgi(_) => (),
gic::Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
gic::Interrupt::Spi(_spi_interrupt) => (),
gic::Interrupt::Invalid(_) => (),
gic::Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
+7 -18
View File
@@ -10,9 +10,9 @@ use embedded_io::Write;
use log::error;
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::gpio::Input;
use zynq7000_hal::prelude::*;
use zynq7000_hal::sd::SdClockConfig;
use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, sd::SdCardUninit, uart};
use zynq7000_hal::{BootMode, clocks, gpio, gtc, sd::SdCardUninit, uart};
use zynq7000_hal::{generic_interrupt_handler, prelude::*};
use zynq7000_rt as _;
@@ -227,23 +227,12 @@ async fn main(_spawner: Spawner) -> ! {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = gic::GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
gic::Interrupt::Sgi(_) => (),
gic::Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
gic::Interrupt::Spi(_spi_interrupt) => (),
gic::Interrupt::Invalid(_) => (),
gic::Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
@@ -9,14 +9,12 @@ use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use fugit::RateExtU32;
use log::{error, info};
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::{
BootMode,
clocks::Clocks,
configure_level_shifter,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
configure_level_shifter, generic_interrupt_handler, gic,
gpio::{GpioPins, Output, PinState},
gtc::GlobalTimerCounter,
l2_cache,
@@ -105,7 +103,7 @@ async fn main(_spawner: Spawner) -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = gic::Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -147,7 +145,8 @@ async fn main(_spawner: Spawner) -> ! {
// TODO: Can we determine/read the clock frequency to the FPGAs as well?
let (clk_config, error) =
axi_uart16550::ClockConfig::new_autocalc_with_error(100.MHz(), 115200).unwrap();
axi_uart16550::ClockConfig::new_autocalc_with_error(fugit_03::HertzU32::MHz(100), 115200)
.unwrap();
assert!(error < 0.02);
let mut uart_16550 = unsafe {
AxiUart16550::new(
@@ -215,23 +214,12 @@ async fn main(_spawner: Spawner) -> ! {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(_spi_interrupt) => (),
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
@@ -41,8 +41,8 @@ use log::{error, info, warn};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
configure_level_shifter,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
configure_level_shifter, generic_interrupt_handler,
gic::{Configurator, Interrupt},
gpio::{GpioPins, Output, PinState},
gtc::GlobalTimerCounter,
l2_cache,
@@ -50,6 +50,7 @@ use zynq7000_hal::{
uart::{ClockConfig, Config, Uart},
};
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum UartMode {
Uart0ToUartlite,
Uart0ToUart16550,
@@ -74,6 +75,14 @@ const AXI_UAR16550_BASE_ADDR: u32 = 0x43C0_0000;
pub const UARTLITE_PL_INT_ID: usize = 0;
pub const UART16550_PL_INT_ID: usize = 1;
pub const UART_SPEED: u32 = 115_200;
// Other common baud rates to test with:
// pub const UART_SPEED: u32 = 9600;
// pub const UART_SPEED: u32 = 230_400;
// pub const UART_SPEED: u32 = 912_600;
const RB_SIZE: usize = 512;
// These queues are used to send all data received in the UART interrupt handlers to the main
@@ -171,7 +180,7 @@ async fn main(spawner: Spawner) -> ! {
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
// Set up the global interrupt controller.
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
let mut gic = Configurator::new_with_init(dp.gicc, dp.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
// AXI UARTLite documentation mentions that a rising-edge sensitive interrupt is generated,
@@ -210,6 +219,20 @@ async fn main(spawner: Spawner) -> ! {
unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) }
}
// Register the interrupts for the PL.
zynq7000_hal::register_interrupt(
Interrupt::Spi(zynq7000_hal::gic::SpiInterrupt::Pl0),
on_interrupt_axi_uartlite,
);
zynq7000_hal::register_interrupt(
Interrupt::Spi(zynq7000_hal::gic::SpiInterrupt::Pl1),
on_interrupt_axi_16550,
);
zynq7000_hal::register_interrupt(
Interrupt::Spi(zynq7000_hal::gic::SpiInterrupt::Uart0),
on_interrupt_uart_0,
);
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
@@ -225,24 +248,42 @@ async fn main(spawner: Spawner) -> ! {
Output::new_for_emio(gpio_pins.emio.take(9).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(10).unwrap(), PinState::Low),
]);
let mut uart_speed = UART_SPEED;
match UART_MODE {
UartMode::Uart0ToUartlite => uart_mux.select(UartSel::Uart0ToUartlite),
UartMode::Uart0ToUart16550 => uart_mux.select(UartSel::Uart0ToUart16550),
UartMode::UartliteToUart16550 => uart_mux.select(UartSel::UartliteToUart16550),
}
if (UART_MODE == UartMode::Uart0ToUartlite || UART_MODE == UartMode::UartliteToUart16550)
&& uart_speed != 115200
{
log::warn!("UARTLITE speed is not configurable. Hardcoding UART speed to 115200");
uart_speed = 115200;
}
let uart0_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), uart_speed)
.unwrap()
.0;
// UART0 routed through EMIO to PL pins.
let uart_0 =
Uart::new_with_emio(dp.uart_0, Config::new_with_clk_config(uart_clk_config)).unwrap();
Uart::new_with_emio(dp.uart_0, Config::new_with_clk_config(uart0_clk_config)).unwrap();
// Safety: Valid address of AXI UARTLITE.
let mut uartlite = unsafe { AxiUartlite::new(AXI_UARTLITE_BASE_ADDR) };
// We need to call this before splitting the structure, because the interrupt signal is
// used for both TX and RX, so the API is only exposed for this structure.
uartlite.enable_interrupt();
let (clk_config, error) =
axi_uart16550::ClockConfig::new_autocalc_with_error(clocks.pl_clocks()[0], 115200).unwrap();
assert!(error < 0.02);
let (clk_config, error) = axi_uart16550::ClockConfig::new_autocalc_with_error(
fugit_03::HertzU32::from_raw(clocks.pl_clocks()[0].to_raw()),
uart_speed,
)
.unwrap();
if error > 0.02 {
log::warn!(
"Calculated clock config for AXI UART16550 has error of {} %, which is higher than 2%. This may lead to incorrect baud rate. Consider changing the input clock or the target baud rate.",
(error * 100.0)
);
}
let _uart_16550 = unsafe {
AxiUart16550::new(
AXI_UAR16550_BASE_ADDR,
@@ -272,7 +313,7 @@ async fn main(spawner: Spawner) -> ! {
let (uartlite_prod, mut uartlite_cons) = QUEUE_UARTLITE.take().split();
let (uart16550_prod, mut uart16550_cons) = QUEUE_UART16550.take().split();
// Use our helper function to start RX handling.
uart_0_rx.start_interrupt_driven_reception();
uart_0_rx.start_interrupt_driven_reception(0xFF);
// Use our helper function to start RX handling.
uart_16550_rx.start_interrupt_driven_reception();
critical_section::with(|cs| {
@@ -387,7 +428,8 @@ async fn uartlite_task(uartlite: axi_uartlite::Tx) {
#[embassy_executor::task]
async fn uart_0_task(uart_tx: zynq7000_hal::uart::Tx) {
let mut ticker = Ticker::every(Duration::from_millis(1000));
let mut tx_async = zynq7000_hal::uart::TxAsync::new(uart_tx);
let mut tx_async = zynq7000_hal::uart::TxAsync::new(uart_tx, false);
let str0 = build_print_string("UART0:", "Hello World");
let str1 = build_print_string(
"UART0:",
@@ -396,7 +438,7 @@ async fn uart_0_task(uart_tx: zynq7000_hal::uart::Tx) {
let mut idx = 0;
let print_strs = [str0.as_bytes(), str1.as_bytes()];
loop {
tx_async.write(print_strs[idx]).await;
tx_async.write(print_strs[idx]).unwrap().await;
idx += 1;
if idx == 2 {
idx = 0;
@@ -429,36 +471,12 @@ async fn uart_16550_task(uart_tx: axi_uart16550::Tx) {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(spi_interrupt) => match spi_interrupt {
zynq7000_hal::gic::SpiInterrupt::Pl0 => {
on_interrupt_axi_uartlite();
}
zynq7000_hal::gic::SpiInterrupt::Pl1 => {
on_interrupt_axi_16550();
}
zynq7000_hal::gic::SpiInterrupt::Uart0 => {
on_interrupt_uart_0();
}
_ => (),
},
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
fn on_interrupt_axi_uartlite() {
@@ -527,8 +545,9 @@ fn on_interrupt_uart_0() {
.on_interrupt(&mut buf, true)
.read_bytes();
});
// Safety: This function is only called once inside the interrupt handler.
// Handle TX next: Handle pending asynchronous TX operations.
zynq7000_hal::uart::on_interrupt_tx(zynq7000_hal::uart::UartId::Uart0);
unsafe { zynq7000_hal::uart::on_interrupt_tx(zynq7000_hal::uart::UartId::Uart0) };
// Send received RX data to main task.
if read_bytes > 0 {
critical_section::with(|cs| {
+6 -17
View File
@@ -9,7 +9,7 @@ use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::{error, info};
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::{BootMode, clocks, gic, gpio, gtc, uart};
use zynq7000_hal::{BootMode, clocks, generic_interrupt_handler, gpio, gtc, uart};
use zynq7000_rt as _;
@@ -88,23 +88,12 @@ async fn main(_spawner: Spawner) -> ! {
}
#[zynq7000_rt::irq]
fn irq_handler() {
let mut gic_helper = gic::GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
gic::Interrupt::Sgi(_) => (),
gic::Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
gic::Interrupt::Spi(_spi_interrupt) => (),
gic::Interrupt::Invalid(_) => (),
gic::Interrupt::Spurious => (),
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
panic!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
+1 -1
View File
@@ -5,4 +5,4 @@ break main
load
continue
# continue
+1 -1
View File
@@ -10,7 +10,7 @@ keywords = ["no-std", "zedboard", "bare-metal", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
zynq7000 = { path = "../zynq7000", version = "0.2" }
zynq7000 = { path = "../zynq7000", version = "0.4" }
zynq7000-hal = { path = "../zynq7000-hal", version = "0.1" }
bitbybit = "2"
log = "0.4"
+2 -2
View File
@@ -9,7 +9,7 @@ repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
license = "MIT OR Apache-2.0"
[dependencies]
aarch32-cpu = { version = "0.2", features = ["critical-section-single-core"] }
aarch32-cpu = { version = "0.3", features = ["critical-section-single-core"] }
zynq7000-rt = { path = "../zynq7000-rt" }
zynq7000 = { path = "../zynq7000" }
zynq7000-hal = { path = "../zynq7000-hal" }
@@ -17,7 +17,7 @@ zynq7000-boot-image = { path = "../../host/zynq7000-boot-image" }
zedboard-bsp = { path = "../zedboard-bsp" }
embedded-io = "0.7"
embedded-hal = "1"
fugit = "0.3"
fugit = "0.4"
log = "0.4"
arbitrary-int = "2"
+6
View File
@@ -25,3 +25,9 @@ SECTIONS
_ebss_uncached = .;
} > UNCACHED
}
PROVIDE(_und_stack_size = 2K);
PROVIDE(_svc_stack_size = 2K);
PROVIDE(_abt_stack_size = 2K);
PROVIDE(_hyp_stack_size = 2K);
PROVIDE(_sys_stack_size = 32K);
+8 -19
View File
@@ -17,7 +17,7 @@ use log::{error, info};
use zedboard_bsp::qspi_spansion::{self, QspiSpansionS25Fl256SLinearMode};
use zynq7000_boot_image::DestinationDevice;
use zynq7000_hal::clocks::ArmClocks;
use zynq7000_hal::priv_tim;
use zynq7000_hal::{generic_interrupt_handler, priv_tim};
use zynq7000_hal::{
BootMode,
clocks::{
@@ -44,7 +44,7 @@ const IO_CLK: Hertz = Hertz::from_raw(1_000_000_000);
const DDR_FREQUENCY: Hertz = Hertz::from_raw(533_333_333);
/// 1067 MHz.
const DDR_CLK: Hertz = Hertz::from_raw(2 * DDR_FREQUENCY.raw());
const DDR_CLK: Hertz = Hertz::from_raw(2 * DDR_FREQUENCY.to_raw());
const PERFORM_DDR_MEMTEST: bool = false;
@@ -119,7 +119,7 @@ fn main() -> ! {
};
// Set up the global interrupt controller.
let mut gic = gic::GicConfigurator::new_with_init(periphs.gicc, periphs.gicd);
let mut gic = gic::Configurator::new_with_init(periphs.gicc, periphs.gicd);
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
@@ -353,23 +353,12 @@ fn qspi_boot(mut qspi: QspiSpansionS25Fl256SLinearMode, _priv_tim: priv_tim::Cpu
}
#[zynq7000_rt::irq]
fn interrupt_handler() {
let mut gic_helper = gic::GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
gic::Interrupt::Sgi(_) => (),
gic::Interrupt::Ppi(ppi_interrupt) => {
log::warn!("unexpected PPI interrupt: {:?}", ppi_interrupt);
}
gic::Interrupt::Spi(spi_interrupt) => {
log::warn!("unexpected SPI interrupt: {:?}", spi_interrupt);
}
gic::Interrupt::Invalid(_) => (),
gic::Interrupt::Spurious => {
log::warn!("spurious interrupt");
}
pub fn irq_handler() {
// Safety: Called here once.
let result = unsafe { generic_interrupt_handler() };
if let Err(e) = result {
log::warn!("Generic interrupt handler failed handling {:?}", e);
}
gic_helper.end_of_interrupt(irq_info);
}
#[zynq7000_rt::exception(DataAbort)]
+11 -1
View File
@@ -66,9 +66,19 @@ impl GtcTimerDriver {
///
/// This has to be called ONCE at system initialization.
pub unsafe fn init(&'static self, arm_clock: &ArmClocks, mut gtc: GlobalTimerCounter) {
fn safe_interrupt_handler() {
// Safety: See safety notes of [zynq7000_hal::generic_interrupt_handler].
unsafe {
on_interrupt();
}
}
zynq7000_hal::register_interrupt(
zynq7000_hal::gic::Interrupt::Ppi(zynq7000_hal::gic::PpiInterrupt::GlobalTimer),
safe_interrupt_handler,
);
CPU_3X2X_CLK.set(arm_clock.cpu_3x2x_clk()).unwrap();
SCALE
.set(arm_clock.cpu_3x2x_clk().raw() as u64 / TICK_HZ)
.set(arm_clock.cpu_3x2x_clk().to_raw() as u64 / TICK_HZ)
.unwrap();
gtc.set_cpu_3x2x_clock(arm_clock.cpu_3x2x_clk());
gtc.set_prescaler(0);
+13
View File
@@ -16,19 +16,32 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Several bugfixes and improvements for GIC module. Some of the registers previously were
completely overwritten instead of only modifying their own bit portions. Also allow targeting
interrupts without clearing other CPU target.
- Do not reset the UART on TX future creation anymore, which lead to glitches and invalid data.
- Robustness improvements for the asynchronous UART TX module.
- SPI1 AMBA clock control bits are now enabled and disabled properly
## Changed
- Increased reliabily of PS UART interrupt reception, which was proven to be buggy for higher baud
rates: Force user to configure RTO value, encouraging non-zero values, and use a RX FIFO trigger
value of FIFO depth divided by 2 by default.
- `devcfg` moved to `pl` module
- Added division by zero check in gtc frequency_to_ticks to avoid runtime panic
- Increased UART type safety by providing dedicated MIO constructors for UART 0 and UART 1
respectively.
- `log::rb` module replaced by `log::asynch` module which uses an asynchronous embassy pipe
for logging.
- GIC data structures: Removed the `Gic` prefix which already is part of the module name.
- Renamed `GicInterruptHelper` to `InterruptGuard`. It acknowledges the end of interrupts on drop.
## Added
- Method to de-assert PL reset.
- ARM clock initialization for the `ArmClocks` structure
- The `ArmClocks` structure now caches the CPU clock ratio
- New generic interrupt registry and generic interrupt handler which uses the registry.
Primary interface is the `crate::generic_interrupt_handler` function and the
`crate::register_interrupt` function.
# [v0.1.1] 2025-10-10
+4 -5
View File
@@ -11,16 +11,15 @@ keywords = ["no-std", "hal", "amd", "zynq7000", "bare-metal"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
aarch32-cpu = { version = "0.2" }
zynq7000 = { path = "../zynq7000", version = "0.2" }
zynq7000-mmu = { path = "../zynq7000-mmu", version = "0.1" }
aarch32-cpu = { version = "0.3" }
zynq7000 = { path = "../zynq7000", version = "0.4" }
zynq7000-mmu = { path = "../zynq7000-mmu", version = "0.2" }
static_assertions = "1.1"
bitbybit = "2"
arbitrary-int = "2"
thiserror = { version = "2", default-features = false }
num_enum = { version = "0.7", default-features = false }
bitflags = "2"
ringbuf = { version = "0.4.8", default-features = false }
embedded-hal-nb = "1"
embedded-io = "0.7"
embedded-hal = "1"
@@ -30,7 +29,7 @@ static_cell = "2"
delegate = "0.13"
pastey = "0.2.1"
nb = "1"
fugit = "0.3"
fugit = "0.4"
critical-section = "1"
libm = "0.2"
log = "0.4"
+1 -1
View File
@@ -38,7 +38,7 @@ impl PllConfig {
ps_clk: Hertz,
target_clk: Hertz,
) -> Result<Self, PllConfigCtorError> {
if ps_clk.raw() == 0 {
if ps_clk.to_raw() == 0 {
return Err(PllConfigCtorError::InvalidInput);
}
let mul = target_clk / ps_clk;
+1 -1
View File
@@ -28,7 +28,7 @@ pub fn calculate_dci_divisors(ddr_clks: &DdrClocks) -> DciClkConfig {
/// Calculate the required DCI divisors for the given DDR clock frequency.
pub fn calculate_dci_divisors_with_ddr_clk(ddr_clk: Hertz) -> DciClkConfig {
let target_div = ddr_clk.raw().div_ceil(DCI_MAX_FREQ.raw());
let target_div = ddr_clk.to_raw().div_ceil(DCI_MAX_FREQ.to_raw());
let mut config = DciClkConfig {
div0: u6::new(u6::MAX.value()),
div1: u6::new(u6::MAX.value()),
+2 -2
View File
@@ -69,8 +69,8 @@ impl ClockDivisors {
let mut best_div_1 = u6::new(0);
for div_1 in 1..=u6::MAX.as_usize() {
for div_0 in 1..=u6::MAX.as_usize() {
let clk_rate = ref_clk.raw() / div_0 as u32 / div_1 as u32;
let diff = (target_speed.raw() as i64 - clk_rate as i64).unsigned_abs() as u32;
let clk_rate = ref_clk.to_raw() / div_0 as u32 / div_1 as u32;
let diff = (target_speed.to_raw() as i64 - clk_rate as i64).unsigned_abs() as u32;
if diff < smallest_diff {
smallest_diff = diff;
best_div_0 = u6::new(div_0 as u8);
+4 -1
View File
@@ -179,7 +179,10 @@ impl Eth1RxData3Pin for Pin<Mio38> {}
/// Calculate the CPU 1x clock divisor required to achieve a clock speed which is below
/// 2.5 MHz, as specified by the 802.3 standard.
pub fn calculate_mdc_clk_div(arm_clks: &ArmClocks) -> Option<MdcClockDivisor> {
let div = arm_clks.cpu_1x_clk().raw().div_ceil(MAX_MDC_SPEED.raw());
let div = arm_clks
.cpu_1x_clk()
.to_raw()
.div_ceil(MAX_MDC_SPEED.to_raw());
match div {
0..8 => Some(MdcClockDivisor::Div8),
8..16 => Some(MdcClockDivisor::Div16),
+53 -19
View File
@@ -1,7 +1,10 @@
//! # Global Interrupt Controller (GIC) module
//!
//! The primary interface to configure and allow handling the interrupts are the
//! [GicConfigurator] and the [GicInterruptHelper] structures.
//! [Configurator] and the [InterruptGuard] structures.
//!
//! The HAL provides a more convenient interface through the [crate::register_interrupt] and
//! [crate::generic_interrupt_handler] functions.
#![deny(missing_docs)]
use arbitrary_int::prelude::*;
@@ -85,8 +88,9 @@ bitflags::bitflags! {
}
/// Private Peripheral Interrupt (PPI) which are private to the CPU.
#[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)]
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, num_enum::TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum PpiInterrupt {
/// Global timer.
@@ -102,8 +106,9 @@ pub enum PpiInterrupt {
}
/// Shared Peripheral Interrupt IDs.
#[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)]
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, num_enum::TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum SpiInterrupt {
/// CPU 0.
@@ -231,8 +236,9 @@ pub enum SpiInterrupt {
}
/// Interrupt ID wrapper.
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Interrupt {
/// Software-generated interrupt (SGI).
Sgi(usize),
@@ -247,8 +253,9 @@ pub enum Interrupt {
}
/// Interrupt information structure.
#[derive(Debug)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InterruptInfo {
raw_reg: InterruptSignalRegister,
interrupt: Interrupt,
@@ -334,16 +341,16 @@ pub struct InvalidSgiInterruptId(pub usize);
/// with [Self::enable] which assumes a certain configuration.
/// 5. Enable interrupts for the Cortex-A core by calling [Self::enable_interrupts].
///
/// For the handling of the interrupts, you can use the [GicInterruptHelper] which assumes a
/// For the handling of the interrupts, you can use the [InterruptGuard] which assumes a
/// properly configured GIC.
pub struct GicConfigurator {
pub struct Configurator {
/// GIC CPU interface registers.
pub gicc: MmioCpuInterfaceRegisters<'static>,
/// GIC Distributor interface registers.
pub gicd: MmioDistributorRegisters<'static>,
}
impl GicConfigurator {
impl Configurator {
/// Create a new GIC controller instance and calls [Self::initialize] to perform
/// strongly recommended initialization routines for the GIC.
#[inline]
@@ -351,7 +358,7 @@ impl GicConfigurator {
gicc: MmioCpuInterfaceRegisters<'static>,
gicd: MmioDistributorRegisters<'static>,
) -> Self {
let mut gic = GicConfigurator { gicc, gicd };
let mut gic = Configurator { gicc, gicd };
gic.initialize();
gic
}
@@ -365,7 +372,7 @@ impl GicConfigurator {
/// used inside the interrupt handler.
#[inline]
pub unsafe fn steal() -> Self {
GicConfigurator {
Configurator {
gicc: unsafe { CpuInterfaceRegisters::new_mmio_fixed() },
gicd: unsafe { DistributorRegisters::new_mmio_fixed() },
}
@@ -648,21 +655,31 @@ impl GicConfigurator {
}
/// Helper structure which should only be used inside the interrupt handler once the GIC has
/// been configured with the [GicConfigurator].
pub struct GicInterruptHelper(MmioCpuInterfaceRegisters<'static>);
/// been configured with the [Configurator].
pub struct InterruptGuard {
regs: MmioCpuInterfaceRegisters<'static>,
interrupt_info: InterruptInfo,
acknowledged: bool,
}
impl GicInterruptHelper {
impl InterruptGuard {
/// Create the interrupt helper with the fixed GICC MMIO instance.
pub const fn new() -> Self {
GicInterruptHelper(unsafe { CpuInterfaceRegisters::new_mmio_fixed() })
pub fn new() -> Self {
let mut regs = unsafe { CpuInterfaceRegisters::new_mmio_fixed() };
let interrupt_info = Self::acknowledge_interrupt(&mut regs);
InterruptGuard {
regs: unsafe { CpuInterfaceRegisters::new_mmio_fixed() },
interrupt_info,
acknowledged: false,
}
}
/// Acknowledges an interrupt by reading the IAR register and returning the interrupt context
/// information structure.
///
/// This should be called at the start of an interrupt handler.
pub fn acknowledge_interrupt(&mut self) -> InterruptInfo {
let iar = self.0.read_iar();
fn acknowledge_interrupt(regs: &mut MmioCpuInterfaceRegisters<'static>) -> InterruptInfo {
let iar = regs.read_iar();
let int_id = iar.ack_int_id().as_u32();
let interrupt = match int_id {
0..=15 => Interrupt::Sgi(int_id as usize),
@@ -678,16 +695,33 @@ impl GicInterruptHelper {
}
}
/// Returns the interrupt information structure which was read from the IAR register.
///
/// This is used to determine the cause of the interrupt.
#[inline]
pub fn interrupt_info(&self) -> InterruptInfo {
self.interrupt_info
}
/// Acknowledges the end of an interrupt by writing the EOIR register of the GICC.
///
/// This should be called at the end of an interrupt handler.
pub fn end_of_interrupt(&mut self, irq_info: InterruptInfo) {
self.0.write_eoir(irq_info.raw_reg())
self.acknowledged = true;
self.regs.write_eoir(irq_info.raw_reg())
}
}
impl Default for GicInterruptHelper {
impl Default for InterruptGuard {
fn default() -> Self {
Self::new()
}
}
impl Drop for InterruptGuard {
fn drop(&mut self) {
if !self.acknowledged {
self.regs.write_eoir(self.interrupt_info.raw_reg());
}
}
}
+11 -11
View File
@@ -18,8 +18,8 @@ unsafe impl Send for GlobalTimerCounter {}
/// Convert a frequency to GTC ticks given a clock frequency.
pub const fn frequency_to_ticks(clock: Hertz, frequency: Hertz) -> u32 {
if frequency.raw() != 0 {
clock.raw().div_ceil(frequency.raw())
if frequency.to_raw() != 0 {
clock.to_raw().div_ceil(frequency.to_raw())
} else {
0
}
@@ -75,13 +75,13 @@ impl GlobalTimerCounter {
/// Set the comparator which can be used to trigger an interrupt in the future.
#[inline]
pub fn set_comparator(&mut self, comparator: u64) {
self.regs.modify_ctrl(|mut ctrl| {
self.regs.modify_control(|mut ctrl| {
ctrl.set_comparator_enable(false);
ctrl
});
self.regs.write_comparator_upper((comparator >> 32) as u32);
self.regs.write_comparator_lower(comparator as u32);
self.regs.modify_ctrl(|mut ctrl| {
self.regs.modify_control(|mut ctrl| {
ctrl.set_comparator_enable(true);
ctrl
});
@@ -112,7 +112,7 @@ impl GlobalTimerCounter {
/// Enable the GTC.
#[inline]
pub fn enable(&mut self) {
self.regs.modify_ctrl(|mut ctrl| {
self.regs.modify_control(|mut ctrl| {
ctrl.set_enable(true);
ctrl
});
@@ -121,7 +121,7 @@ impl GlobalTimerCounter {
/// Enable auto-increment.
#[inline]
pub fn enable_auto_increment(&mut self) {
self.regs.modify_ctrl(|mut ctrl| {
self.regs.modify_control(|mut ctrl| {
ctrl.set_auto_increment(true);
ctrl
});
@@ -130,7 +130,7 @@ impl GlobalTimerCounter {
/// Set a pre-scaler.
#[inline]
pub fn set_prescaler(&mut self, prescaler: u8) {
self.regs.modify_ctrl(|mut ctrl| {
self.regs.modify_control(|mut ctrl| {
ctrl.set_prescaler(prescaler);
ctrl
});
@@ -139,7 +139,7 @@ impl GlobalTimerCounter {
/// Disable the GTC.
#[inline]
pub fn disable(&mut self) {
self.regs.modify_ctrl(|mut ctrl| {
self.regs.modify_control(|mut ctrl| {
ctrl.set_enable(false);
ctrl
});
@@ -148,7 +148,7 @@ impl GlobalTimerCounter {
/// Enable the comparator interrupt.
#[inline]
pub fn enable_interrupt(&mut self) {
self.regs.modify_ctrl(|mut ctrl| {
self.regs.modify_control(|mut ctrl| {
ctrl.set_irq_enable(true);
ctrl
});
@@ -157,7 +157,7 @@ impl GlobalTimerCounter {
/// Disable the comparator interrupt.
#[inline]
pub fn disable_interrupt(&mut self) {
self.regs.modify_ctrl(|mut ctrl| {
self.regs.modify_control(|mut ctrl| {
ctrl.set_irq_enable(false);
ctrl
});
@@ -171,7 +171,7 @@ impl embedded_hal::delay::DelayNs for GlobalTimerCounter {
return;
}
let end_of_delay = self.read_timer()
+ (((ns as u64) * self.cpu_3x2x_clock.unwrap().raw() as u64) / 1_000_000_000);
+ (((ns as u64) * self.cpu_3x2x_clock.unwrap().to_raw() as u64) / 1_000_000_000);
while self.read_timer() < end_of_delay {}
}
}
+29 -29
View File
@@ -266,7 +266,8 @@ pub fn calculate_divisors(
for divisor_a in 1..=4 {
for divisor_b in 1..=64 {
let i2c_clock = cpu_1x_clk / (22 * divisor_a * divisor_b);
let deviation = (target_speed.raw() as i32 - i2c_clock.raw() as i32).unsigned_abs();
let deviation =
(target_speed.to_raw() as i32 - i2c_clock.to_raw() as i32).unsigned_abs();
if deviation < smallest_deviation {
smallest_deviation = deviation;
best_div_a = divisor_a;
@@ -352,8 +353,7 @@ impl I2c {
I2cId::I2c1 => crate::PeriphSelect::I2c1,
};
enable_amba_peripheral_clock(periph_sel);
//reset(id);
regs.write_cr(
regs.write_control(
Control::builder()
.with_div_a(u2::new(clk_cfg.div_a()))
.with_div_b(u6::new(clk_cfg.div_b()))
@@ -378,7 +378,7 @@ impl I2c {
#[inline]
pub fn set_hold_bit(&mut self) {
self.regs.modify_cr(|mut cr| {
self.regs.modify_control(|mut cr| {
cr.set_hold_bus(true);
cr
});
@@ -386,7 +386,7 @@ impl I2c {
#[inline]
pub fn clear_hold_bit(&mut self) {
self.regs.modify_cr(|mut cr| {
self.regs.modify_control(|mut cr| {
cr.set_hold_bus(false);
cr
});
@@ -398,7 +398,7 @@ impl I2c {
data: &[u8],
generate_stop: bool,
) -> Result<(), I2cTxError> {
self.regs.modify_cr(|mut cr| {
self.regs.modify_control(|mut cr| {
cr.set_acken(true);
cr.set_mode(zynq7000::i2c::Mode::Master);
cr.set_clear_fifo(true);
@@ -412,7 +412,7 @@ impl I2c {
let mut addr_set = false;
let mut written = 0;
// Clear the interrupt status register before using it to monitor the transfer.
self.regs.modify_isr(|isr| isr);
self.regs.modify_interrupt_status(|isr| isr);
loop {
let bytes_to_write = core::cmp::min(
FIFO_DEPTH - self.regs.read_transfer_size().size() as usize,
@@ -429,13 +429,13 @@ impl I2c {
self.start_transfer(addr);
addr_set = true;
}
let mut status = self.regs.read_sr();
let mut status = self.regs.read_status();
// While the hardware is busy sending out data, we poll for errors.
while status.tx_busy() {
let isr = self.regs.read_isr();
let isr = self.regs.read_interrupt_status();
self.check_and_handle_tx_errors(isr, first_write_cycle, bytes_to_write)?;
// Re-read for next check.
status = self.regs.read_sr();
status = self.regs.read_status();
}
first_write_cycle = false;
// Just need to poll to completion now.
@@ -444,8 +444,8 @@ impl I2c {
}
}
// Poll to completion.
while !self.regs.read_isr().complete() {
let isr = self.regs.read_isr();
while !self.regs.read_interrupt_status().complete() {
let isr = self.regs.read_interrupt_status();
self.check_and_handle_tx_errors(isr, first_write_cycle, data.len())?;
}
if generate_stop {
@@ -489,7 +489,7 @@ impl I2c {
}
pub fn clean_up_after_transfer_or_on_error(&mut self) {
self.regs.modify_cr(|mut cr| {
self.regs.modify_control(|mut cr| {
cr.set_acken(false);
cr.set_clear_fifo(true);
cr
@@ -497,7 +497,7 @@ impl I2c {
}
pub fn read_transfer_blocking(&mut self, addr: u8, data: &mut [u8]) -> Result<(), I2cRxError> {
self.regs.modify_cr(|mut cr| {
self.regs.modify_control(|mut cr| {
cr.set_acken(true);
cr.set_mode(zynq7000::i2c::Mode::Master);
cr.set_clear_fifo(true);
@@ -512,23 +512,23 @@ impl I2c {
return Err(I2cRxError::ReadDataLenTooLarge);
}
// Clear the interrupt status register before using it to monitor the transfer.
self.regs.modify_isr(|isr| isr);
self.regs.modify_interrupt_status(|isr| isr);
self.regs
.write_transfer_size(TransferSize::new_with_raw_value(data.len() as u32));
self.start_transfer(addr);
loop {
let mut status = self.regs.read_sr();
let mut status = self.regs.read_status();
loop {
let isr = self.regs.read_isr();
let isr = self.regs.read_interrupt_status();
self.check_and_handle_rx_errors(read, isr)?;
if status.rx_valid() {
break;
}
// Re-read for next check.
status = self.regs.read_sr();
status = self.regs.read_status();
}
// Data to be read.
while self.regs.read_sr().rx_valid() {
while self.regs.read_status().rx_valid() {
data[read] = self.regs.read_data().data();
read += 1;
}
@@ -544,8 +544,8 @@ impl I2c {
}
// Poll to completion.
while !self.regs.read_isr().complete() {
let isr = self.regs.read_isr();
while !self.regs.read_interrupt_status().complete() {
let isr = self.regs.read_interrupt_status();
self.check_and_handle_rx_errors(read, isr)?
}
self.clear_hold_bit();
@@ -678,8 +678,8 @@ mod tests {
assert_eq!(clk_cfg.div_a(), 0);
assert_eq!(clk_cfg.div_b(), 55);
let speed = calculate_i2c_speed(111.MHz(), clk_cfg);
assert!(speed.raw() < 100_000);
assert!(speed.raw() > 85_000);
assert!(speed.to_raw() < 100_000);
assert!(speed.to_raw() > 85_000);
}
#[test]
@@ -688,8 +688,8 @@ mod tests {
assert_eq!(clk_cfg.div_a(), 0);
assert_eq!(clk_cfg.div_b(), 12);
let speed = calculate_i2c_speed(111.MHz(), clk_cfg);
assert!(speed.raw() < 400_000);
assert!(speed.raw() > 360_000);
assert!(speed.to_raw() < 400_000);
assert!(speed.to_raw() > 360_000);
}
#[test]
@@ -698,8 +698,8 @@ mod tests {
assert_eq!(clk_cfg.div_a(), 1);
assert_eq!(clk_cfg.div_b(), 33);
let speed = calculate_i2c_speed(133.MHz(), clk_cfg);
assert!(speed.raw() < 100_000);
assert!(speed.raw() > 85_000);
assert!(speed.to_raw() < 100_000);
assert!(speed.to_raw() > 85_000);
}
#[test]
@@ -708,7 +708,7 @@ mod tests {
assert_eq!(clk_cfg.div_a(), 0);
assert_eq!(clk_cfg.div_b(), 15);
let speed = calculate_i2c_speed(133.MHz(), clk_cfg);
assert!(speed.raw() < 400_000);
assert!(speed.raw() > 360_000);
assert!(speed.to_raw() < 400_000);
assert!(speed.to_raw() > 360_000);
}
}
+54
View File
@@ -0,0 +1,54 @@
use crate::gic::{Interrupt, InterruptGuard};
pub type InterruptMap = heapless::index_map::FnvIndexMap<Interrupt, unsafe fn(), 128>;
static INTERRUPT_MAP: critical_section::Mutex<core::cell::RefCell<InterruptMap>> =
critical_section::Mutex::new(core::cell::RefCell::new(
heapless::index_map::FnvIndexMap::new(),
));
/// Register an interrupt handler for a specific [Interrupt].
///
/// It should be noted that the current implementation only allows one function for each interrupts.
/// If the HAL provided interrupt handler does not fulfill all your requirements, you need
/// to define your own interrupt handler and register it.
/// For example, you might need to handle both UART RX and TX, and the HAL handler only handles TX.
pub fn register_interrupt(interrupt: Interrupt, handler: unsafe fn()) {
critical_section::with(|cs| {
let mut map = INTERRUPT_MAP.borrow(cs).borrow_mut();
map.insert(interrupt, handler).ok();
});
}
/// Generic interrupt handler which retrieves the interrupt handler for individual [Interrupt]s
/// from a registry and calls it.
///
/// If no interrupt was registered or the number is [Interrupt::Invalid], returns the [Interrupt]
/// ID as an error. In any case, the generic handler acknowledges the interrupt in the GIC.
///
/// # Safety
///
/// This needs to be called ONCE in the interrupt handler function, which is any function annotated
/// with the `irq` attribute provided by `aarch32-rt`.
pub unsafe fn generic_interrupt_handler() -> Result<(), Interrupt> {
let mut gic_guard = InterruptGuard::new();
let irq_info = gic_guard.interrupt_info();
let interrupt = irq_info.interrupt();
if let Interrupt::Invalid(_) = interrupt {
gic_guard.end_of_interrupt(irq_info);
return Err(interrupt);
}
let opt_interrupt_handler = critical_section::with(|cs| {
let map = INTERRUPT_MAP.borrow(cs).borrow_mut();
map.get(&interrupt).copied()
});
if let Some(interrupt_handler) = opt_interrupt_handler {
// Safety: The user made sure that this is only called once in the interrupt handler
// function.
unsafe {
interrupt_handler();
}
}
gic_guard.end_of_interrupt(irq_info);
opt_interrupt_handler.ok_or(interrupt)?;
Ok(())
}
+7
View File
@@ -88,3 +88,10 @@ pub fn disable() {
let mut l2c_mmio = unsafe { zynq7000::l2_cache::Registers::new_mmio_fixed() };
l2c_mmio.write_control(Control::new_disabled());
}
/// Function to invalidate l2 cache
pub fn invalidate_all(l2c_mmio: &mut MmioRegisters<'static>) {
l2c_mmio.write_clean_invalidate_by_way(0xffff);
while l2c_mmio.read_cache_sync().busy() {}
compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
+7 -4
View File
@@ -10,7 +10,7 @@
//!
//! ## Examples
//!
//! All exaples can be found inside the [examples folder](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware/examples)
//! All examples can be found inside the [examples folder](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware/examples)
//! and [firmware folder](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/firmware) of the project
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
@@ -32,6 +32,7 @@ pub mod gic;
pub mod gpio;
pub mod gtc;
pub mod i2c;
pub mod interrupt;
pub mod l2_cache;
pub mod log;
pub mod pl;
@@ -45,6 +46,8 @@ pub mod time;
pub mod ttc;
pub mod uart;
pub use gic::{Interrupt, PpiInterrupt, SpiInterrupt};
pub use interrupt::{generic_interrupt_handler, register_interrupt};
pub use zynq7000 as pac;
pub use zynq7000::slcr::LevelShifterConfig;
@@ -80,7 +83,7 @@ pub fn init(config: Config) -> Result<zynq7000::Peripherals, InitError> {
configure_level_shifter(config);
}
if let Some(interrupt_config) = config.interrupt_config {
let mut gic = gic::GicConfigurator::new_with_init(periphs.gicc, periphs.gicd);
let mut gic = gic::Configurator::new_with_init(periphs.gicc, periphs.gicd);
match interrupt_config {
InteruptConfig::AllInterruptsToCpu0 => {
gic.enable_all_interrupts();
@@ -208,7 +211,7 @@ pub fn enable_amba_peripheral_clock(select: PeriphSelect) {
PeriphSelect::Can1 => val.set_can_1_1x_clk_act(true),
PeriphSelect::Can0 => val.set_can_0_1x_clk_act(true),
PeriphSelect::Spi1 => val.set_spi_1_1x_clk_act(true),
PeriphSelect::Spi0 => val.set_spi_1_1x_clk_act(true),
PeriphSelect::Spi0 => val.set_spi_0_1x_clk_act(true),
PeriphSelect::Sdio1 => val.set_sdio_1_1x_clk_act(true),
PeriphSelect::Sdio0 => val.set_sdio_0_1x_clk_act(true),
PeriphSelect::Gem1 => val.set_gem_1_1x_clk_act(true),
@@ -241,7 +244,7 @@ pub fn disable_amba_periph_clk(select: PeriphSelect) {
PeriphSelect::Can1 => val.set_can_1_1x_clk_act(false),
PeriphSelect::Can0 => val.set_can_0_1x_clk_act(false),
PeriphSelect::Spi1 => val.set_spi_1_1x_clk_act(false),
PeriphSelect::Spi0 => val.set_spi_1_1x_clk_act(false),
PeriphSelect::Spi0 => val.set_spi_0_1x_clk_act(false),
PeriphSelect::Sdio1 => val.set_sdio_1_1x_clk_act(false),
PeriphSelect::Sdio0 => val.set_sdio_0_1x_clk_act(false),
PeriphSelect::Gem1 => val.set_gem_1_1x_clk_act(false),
+41 -57
View File
@@ -165,96 +165,80 @@ pub mod uart_blocking {
}
}
/// Logger module which logs into a ring buffer to allow asynchronous logging handling.
pub mod rb {
use core::cell::RefCell;
/// Logger module which logs into a pipe to allow asynchronous logging handling.
pub mod asynch {
use core::fmt::Write as _;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use log::{LevelFilter, set_logger, set_max_level};
use ringbuf::{
StaticRb,
traits::{Consumer, Producer},
};
/// Logger implementation which logs frames via a ring buffer and sends the frame sizes
/// as messages.
///
/// The logger does not require allocation and reserved a generous amount of 4096 bytes for
/// both data buffer and ring buffer. This should be sufficient for most logging needs.
/// The logger does not require allocation and reserves a generous amount of 4096 bytes for
/// log data. This should be sufficient for most logging needs.
pub struct Logger {
frame_queue: embassy_sync::channel::Channel<CriticalSectionRawMutex, usize, 32>,
data_buf: critical_section::Mutex<RefCell<heapless::String<4096>>>,
ring_buf: critical_section::Mutex<RefCell<Option<StaticRb<u8, 4096>>>>,
pipe: core::cell::RefCell<
Option<embassy_sync::pipe::Writer<'static, CriticalSectionRawMutex, 4096>>,
>,
buf: critical_section::Mutex<core::cell::RefCell<heapless::String<4096>>>,
}
unsafe impl Send for Logger {}
unsafe impl Sync for Logger {}
static LOGGER_RB: Logger = Logger {
frame_queue: embassy_sync::channel::Channel::new(),
data_buf: critical_section::Mutex::new(RefCell::new(heapless::String::new())),
ring_buf: critical_section::Mutex::new(RefCell::new(None)),
static LOGGER: Logger = Logger {
pipe: core::cell::RefCell::new(None),
buf: critical_section::Mutex::new(core::cell::RefCell::new(heapless::String::new())),
};
static PIPE: static_cell::ConstStaticCell<
embassy_sync::pipe::Pipe<CriticalSectionRawMutex, 4096>,
> = static_cell::ConstStaticCell::new(embassy_sync::pipe::Pipe::new());
impl log::Log for Logger {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}
fn log(&self, record: &log::Record) {
if self.pipe.borrow().is_none() {
return;
}
critical_section::with(|cs| {
let ref_buf = self.data_buf.borrow(cs);
let mut buf = ref_buf.borrow_mut();
let mut buf = self.buf.borrow(cs).borrow_mut();
buf.clear();
let _ = writeln!(buf, "{} - {}\r", record.level(), record.args());
let rb_ref = self.ring_buf.borrow(cs);
let mut rb_opt = rb_ref.borrow_mut();
if rb_opt.is_none() {
panic!("log call on uninitialized logger");
let mut written = 0;
let pipe_writer_ref = self.pipe.borrow();
let pipe_writer = pipe_writer_ref.as_ref().unwrap();
while let Ok(written_in_this_call) =
pipe_writer.try_write(&buf.as_bytes()[written..])
{
written += written_in_this_call;
if written >= buf.len() {
break;
}
}
rb_opt.as_mut().unwrap().push_slice(buf.as_bytes());
let _ = self.frame_queue.try_send(buf.len());
});
}
fn flush(&self) {
while !self.frame_queue().is_empty() {}
}
fn flush(&self) {}
}
impl Logger {
pub fn frame_queue(
&self,
) -> &embassy_sync::channel::Channel<CriticalSectionRawMutex, usize, 32> {
&self.frame_queue
}
}
pub fn init(level: LevelFilter) {
pub fn init(
level: LevelFilter,
//writer: embassy_sync::pipe::Writer<'static, CriticalSectionRawMutex, 4096>,
) -> Option<embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>> {
if super::LOGGER_INIT_DONE.swap(true, core::sync::atomic::Ordering::Relaxed) {
return;
return None;
}
critical_section::with(|cs| {
let rb = StaticRb::<u8, 4096>::default();
let rb_ref = LOGGER_RB.ring_buf.borrow(cs);
rb_ref.borrow_mut().replace(rb);
});
set_logger(&LOGGER_RB).unwrap();
let (reader, writer) = PIPE.take().split();
LOGGER.pipe.borrow_mut().replace(writer);
set_logger(&LOGGER).unwrap();
set_max_level(level); // Adjust as needed
}
pub fn read_next_frame(frame_len: usize, buf: &mut [u8]) {
let read_len = core::cmp::min(frame_len, buf.len());
critical_section::with(|cs| {
let rb_ref = LOGGER_RB.ring_buf.borrow(cs);
let mut rb = rb_ref.borrow_mut();
rb.as_mut().unwrap().pop_slice(&mut buf[0..read_len]);
})
}
pub fn get_frame_queue()
-> &'static embassy_sync::channel::Channel<CriticalSectionRawMutex, usize, 32> {
LOGGER_RB.frame_queue()
Some(reader)
}
}
+1 -1
View File
@@ -76,7 +76,7 @@ impl embedded_hal::delay::DelayNs for CpuPrivateTimer {
fn delay_ns(&mut self, ns: u32) {
// Even for a value of 1000 MHz for CPU 3x2x and u32::MAX for nanoseconds, this will
// never overflow.
let ticks = (ns as u64 * self.cpu_3x2x_clock.raw() as u64) / 1_000_000_000;
let ticks = (ns as u64 * self.cpu_3x2x_clock.to_raw() as u64) / 1_000_000_000;
// Split the total delay into manageable chunks (u32::MAX ticks max).
let mut remaining = ticks;
+9 -9
View File
@@ -212,7 +212,7 @@ impl ClockConfig {
SrcSelIo::ArmPll => clocks.arm_clocks().ref_clk(),
SrcSelIo::DdrPll => clocks.ddr_clocks().ref_clk(),
};
let ref_clk_div = ref_clk.raw().div_ceil(target_ref_clock.raw());
let ref_clk_div = ref_clk.to_raw().div_ceil(target_ref_clock.to_raw());
if ref_clk_div > u6::MAX.as_u32() {
return Err(ClockCalculationError::RefDivOutOfRange);
}
@@ -239,24 +239,24 @@ impl ClockConfig {
clocks
.io_clocks()
.ref_clk()
.raw()
.div_ceil(target_qspi_ref_clock.raw()),
.to_raw()
.div_ceil(target_qspi_ref_clock.to_raw()),
clocks.io_clocks().ref_clk(),
),
SrcSelIo::ArmPll => (
clocks
.arm_clocks()
.ref_clk()
.raw()
.div_ceil(target_qspi_ref_clock.raw()),
.to_raw()
.div_ceil(target_qspi_ref_clock.to_raw()),
clocks.arm_clocks().ref_clk(),
),
SrcSelIo::DdrPll => (
clocks
.ddr_clocks()
.ref_clk()
.raw()
.div_ceil(target_qspi_ref_clock.raw()),
.to_raw()
.div_ceil(target_qspi_ref_clock.to_raw()),
clocks.ddr_clocks().ref_clk(),
),
};
@@ -268,8 +268,8 @@ impl ClockConfig {
return Err(ClockCalculationError::RefClockSmallerThanCpu1xClock);
}
let qspi_baud_rate_div = qspi_ref_clk
.raw()
.div_ceil(target_qspi_interface_clock.raw());
.to_raw()
.div_ceil(target_qspi_interface_clock.to_raw());
let baud_rate_div = match qspi_baud_rate_div {
0..=2 => BaudRateDivisor::_2,
3..=4 => BaudRateDivisor::_4,
+5 -3
View File
@@ -308,8 +308,10 @@ impl SdioDivisors {
129.. => SdClockDivisor::Div256,
};
Self {
divisor_init_phase: divisor_select_from_value(ref_clk.raw().div_ceil(INIT_CLOCK_HZ)),
divisor_normal: divisor_select_from_value(ref_clk.raw().div_ceil(target_speed.raw())),
divisor_init_phase: divisor_select_from_value(ref_clk.to_raw().div_ceil(INIT_CLOCK_HZ)),
divisor_normal: divisor_select_from_value(
ref_clk.to_raw().div_ceil(target_speed.to_raw()),
),
}
}
@@ -354,7 +356,7 @@ impl SdClockConfig {
target_sd_speed: Hertz,
) -> Option<Self> {
let ref_clk = io_clocks.ref_clk();
let io_ref_clock_divisor = ref_clk.raw().div_ceil(target_ref_clock.raw());
let io_ref_clock_divisor = ref_clk.to_raw().div_ceil(target_ref_clock.to_raw());
if io_ref_clock_divisor > u6::MAX.as_u32() {
return None;
}
+263 -164
View File
@@ -1,11 +1,10 @@
//! Asynchronous PS SPI driver.
use core::{cell::RefCell, convert::Infallible, sync::atomic::AtomicBool};
use core::{cell::RefCell, sync::atomic::AtomicBool};
use critical_section::Mutex;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_hal_async::spi::SpiBus;
use raw_slice::{RawBufSlice, RawBufSliceMut};
use zynq7000::spi::InterruptStatus;
use super::{ChipSelect, FIFO_DEPTH, Spi, SpiId, SpiLowLevel};
@@ -16,119 +15,137 @@ static TRANSFER_CONTEXTS: [Mutex<RefCell<TransferContext>>; 2] =
// critical section.
static DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
#[derive(Debug, Clone, Copy, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("SPI RX FIFO overrun")]
pub struct RxOverrunError;
impl embedded_hal_async::spi::Error for RxOverrunError {
fn kind(&self) -> embedded_hal::spi::ErrorKind {
embedded_hal::spi::ErrorKind::Overrun
}
}
/// This is a generic interrupt handler to handle asynchronous SPI operations for a given
/// SPI peripheral.
///
/// The user has to call this once in the interrupt handler responsible for the SPI interrupts on
/// # Safety
///
/// This needs to be called once in the interrupt handler responsible for the SPI interrupts on
/// the given SPI bank.
pub fn on_interrupt(peripheral: SpiId) {
pub unsafe fn on_interrupt(peripheral: SpiId) {
let mut spi = unsafe { SpiLowLevel::steal(peripheral) };
let idx = peripheral as usize;
let imr = spi.read_imr();
// IRQ is not related.
if !imr.tx_trig() && !imr.tx_full() && !imr.tx_underflow() && !imr.rx_ovr() && !imr.rx_full() {
return;
}
let index = peripheral as usize;
let enabled_irqs = spi.read_enabled_interrupts();
// Prevent spurious interrupts from messing with out logic here.
spi.disable_interrupts();
let isr = spi.read_isr();
let interrupt_status = spi.read_interrupt_status();
spi.clear_interrupts();
// IRQ is not related.
if !enabled_irqs.tx_below_threshold()
&& !enabled_irqs.tx_full()
&& !enabled_irqs.tx_underflow()
&& !enabled_irqs.rx_ovr()
&& !enabled_irqs.rx_full()
{
return;
}
if interrupt_status.rx_overrun() {
// Not sure how to otherwise handle this cleanly..
return handle_rx_overrun(&mut spi, index);
}
let mut context = critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
let context_ref = TRANSFER_CONTEXTS[index].borrow(cs);
*context_ref.borrow()
});
// No transfer active.
if context.transfer_type.is_none() {
return;
}
// Write the trigger to one, we want to empty the whole FIFO in the transfer handlers.
// The trigger might have been set to a higher value, and the NOT FULL status bit will be
// cleared when the FIFO falls below the threshold.
spi.write_rx_trig(0x1);
let transfer_type = context.transfer_type.unwrap();
match transfer_type {
TransferType::Read => on_interrupt_read(idx, &mut context, &mut spi, isr),
TransferType::Write => on_interrupt_write(idx, &mut context, &mut spi, isr),
TransferType::Transfer => on_interrupt_transfer(idx, &mut context, &mut spi, isr),
TransferType::Read => on_interrupt_read(index, &mut context, &mut spi),
TransferType::Write => on_interrupt_write(index, &mut context, &mut spi),
TransferType::Transfer => on_interrupt_transfer(index, &mut context, &mut spi),
TransferType::TransferInPlace => {
on_interrupt_transfer_in_place(idx, &mut context, &mut spi, isr)
on_interrupt_transfer_in_place(index, &mut context, &mut spi)
}
};
}
fn on_interrupt_read(
idx: usize,
context: &mut TransferContext,
spi: &mut SpiLowLevel,
mut isr: InterruptStatus,
) {
fn handle_rx_overrun(spi: &mut SpiLowLevel, idx: usize) {
critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
context_ref.borrow_mut().rx_overrun = true;
});
// Clean state is re-configured by drop handler.
reset_trigger_levels(spi);
// At the very least disable the peripheral.
spi.disable();
// Interrupts were already disabled and cleared.
DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
WAKERS[idx].wake();
}
#[inline]
fn reset_trigger_levels(spi: &mut SpiLowLevel) {
spi.write_rx_trig(0x1);
spi.write_tx_trig(0x1);
}
fn on_interrupt_read(idx: usize, context: &mut TransferContext, spi: &mut SpiLowLevel) {
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
let transfer_len = read_slice.len();
// Read data from RX FIFO first.
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
(0..read_len).for_each(|_| {
while spi.read_interrupt_status().rx_not_empty() {
read_slice[context.rx_progress] = spi.read_fifo_unchecked();
context.rx_progress += 1;
});
}
// The FIFO still needs to be pumped.
while context.tx_progress < read_slice.len() && !isr.tx_full() {
while context.tx_progress < read_slice.len() && !spi.read_interrupt_status().tx_full() {
spi.write_fifo_unchecked(0);
context.tx_progress += 1;
isr = spi.read_isr();
}
isr_finish_handler(idx, spi, context, transfer_len)
}
fn on_interrupt_write(
idx: usize,
context: &mut TransferContext,
spi: &mut SpiLowLevel,
mut isr: InterruptStatus,
) {
fn on_interrupt_write(idx: usize, context: &mut TransferContext, spi: &mut SpiLowLevel) {
let write_slice = unsafe { context.tx_slice.get().unwrap() };
let transfer_len = write_slice.len();
// Read data from RX FIFO first.
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
(0..read_len).for_each(|_| {
while spi.read_interrupt_status().rx_not_empty() {
spi.read_fifo_unchecked();
context.rx_progress += 1;
});
}
// Data still needs to be sent
while context.tx_progress < transfer_len && !isr.tx_full() {
while context.tx_progress < transfer_len && !spi.read_interrupt_status().tx_full() {
spi.write_fifo_unchecked(write_slice[context.tx_progress]);
context.tx_progress += 1;
isr = spi.read_isr();
}
isr_finish_handler(idx, spi, context, transfer_len)
}
fn on_interrupt_transfer(
idx: usize,
context: &mut TransferContext,
spi: &mut SpiLowLevel,
mut isr: InterruptStatus,
) {
fn on_interrupt_transfer(idx: usize, context: &mut TransferContext, spi: &mut SpiLowLevel) {
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
let read_len = read_slice.len();
let write_slice = unsafe { context.tx_slice.get().unwrap() };
let write_len = write_slice.len();
let transfer_len = core::cmp::max(read_len, write_len);
// Read data from RX FIFO first.
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
(0..read_len).for_each(|_| {
if context.rx_progress < read_len {
read_slice[context.rx_progress] = spi.read_fifo_unchecked();
} else {
spi.read_fifo_unchecked();
}
context.rx_progress += 1;
});
// Data still needs to be sent
while context.tx_progress < transfer_len && !isr.tx_full() {
while context.tx_progress < transfer_len && !spi.read_interrupt_status().tx_full() {
if context.tx_progress < write_len {
spi.write_fifo_unchecked(write_slice[context.tx_progress]);
} else {
@@ -136,7 +153,16 @@ fn on_interrupt_transfer(
spi.write_fifo_unchecked(0);
}
context.tx_progress += 1;
isr = spi.read_isr();
}
// Read data from RX FIFO first.
while spi.read_interrupt_status().rx_not_empty() {
if context.rx_progress < read_len {
read_slice[context.rx_progress] = spi.read_fifo_unchecked();
} else {
spi.read_fifo_unchecked();
}
context.rx_progress += 1;
}
isr_finish_handler(idx, spi, context, transfer_len)
@@ -146,43 +172,25 @@ fn on_interrupt_transfer_in_place(
idx: usize,
context: &mut TransferContext,
spi: &mut SpiLowLevel,
mut isr: InterruptStatus,
) {
let transfer_slice = unsafe { context.rx_slice.get_mut().unwrap() };
let transfer_len = transfer_slice.len();
// Read data from RX FIFO first.
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
(0..read_len).for_each(|_| {
transfer_slice[context.rx_progress] = spi.read_fifo_unchecked();
context.rx_progress += 1;
});
// Data still needs to be sent
while context.tx_progress < transfer_len && !isr.tx_full() {
// Send data first to avoid overwriting data that still needs to be sent.
while context.tx_progress < transfer_len && !spi.read_interrupt_status().tx_full() {
spi.write_fifo_unchecked(transfer_slice[context.tx_progress]);
context.tx_progress += 1;
isr = spi.read_isr();
}
// Read data from RX FIFO.
while spi.read_interrupt_status().rx_not_empty() {
transfer_slice[context.rx_progress] = spi.read_fifo_unchecked();
context.rx_progress += 1;
}
isr_finish_handler(idx, spi, context, transfer_len)
}
fn calculate_read_len(
spi: &mut SpiLowLevel,
isr: InterruptStatus,
total_read_len: usize,
rx_progress: usize,
) -> usize {
if isr.rx_full() {
core::cmp::min(FIFO_DEPTH, total_read_len - rx_progress)
} else if isr.rx_not_empty() {
let trigger = spi.read_rx_not_empty_threshold();
core::cmp::min(total_read_len - rx_progress, trigger as usize)
} else {
0
}
}
/// Generic handler after RX FIFO and TX FIFO were handled. Checks and handles finished
/// and unfinished conditions.
fn isr_finish_handler(
@@ -197,7 +205,7 @@ fn isr_finish_handler(
return;
}
unfinished_transfer(spi, transfer_len, context.rx_progress);
unfinished_transfer(spi, transfer_len, context);
// If the transfer is done, the context structure was already written back.
// Write back updated context structure.
@@ -213,6 +221,7 @@ fn finish_transfer(idx: usize, context: &mut TransferContext, spi: &mut SpiLowLe
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
*context_ref.borrow_mut() = *context;
});
// Default reset values.
spi.set_rx_fifo_trigger(1).unwrap();
spi.set_tx_fifo_trigger(1).unwrap();
// Interrupts were already disabled and cleared.
@@ -220,11 +229,30 @@ fn finish_transfer(idx: usize, context: &mut TransferContext, spi: &mut SpiLowLe
WAKERS[idx].wake();
}
fn unfinished_transfer(spi: &mut SpiLowLevel, transfer_len: usize, rx_progress: usize) {
let new_trig_level = core::cmp::min(FIFO_DEPTH, transfer_len - rx_progress);
spi.set_rx_fifo_trigger(new_trig_level as u32).unwrap();
// Re-enable interrupts with the new RX FIFO trigger level.
spi.enable_interrupts();
fn unfinished_transfer(spi: &mut SpiLowLevel, transfer_len: usize, context: &TransferContext) {
// Unwraps okay, checks ensure number is below FIFO depth.
// By using the FIFO depth divided by two, give the software some time and allow more
// latency in the system.
spi.set_rx_fifo_trigger(
core::cmp::min(FIFO_DEPTH / 2, transfer_len - context.rx_progress) as u32,
)
.unwrap();
let tx_pending = context.tx_progress < transfer_len;
if tx_pending {
let remaining_tx = transfer_len - context.tx_progress;
let tx_trigger = if remaining_tx < FIFO_DEPTH / 2 {
FIFO_DEPTH as u32 - remaining_tx as u32
} else {
FIFO_DEPTH as u32 / 2
};
spi.set_tx_fifo_trigger(tx_trigger).unwrap();
} else {
spi.set_tx_fifo_trigger(1).unwrap();
}
// Re-enable interrupts with the new RX FIFO trigger level. Only enable TX threshold interrupt
// if we are not done yet with pushing all bytes to the FIFO.
spi.enable_interrupts(tx_pending);
}
#[derive(Debug, Clone, Copy)]
@@ -242,6 +270,7 @@ pub struct TransferContext {
rx_progress: usize,
tx_slice: RawBufSlice,
rx_slice: RawBufSliceMut,
rx_overrun: bool,
}
#[allow(clippy::new_without_default)]
@@ -253,173 +282,198 @@ impl TransferContext {
rx_progress: 0,
tx_slice: RawBufSlice::new_nulled(),
rx_slice: RawBufSliceMut::new_nulled(),
rx_overrun: false,
}
}
}
pub struct SpiFuture {
pub struct SpiFuture<'spi> {
id: super::SpiId,
spi: super::SpiLowLevel,
spi: &'spi mut super::SpiLowLevel,
config: super::Config,
finished_regularly: core::cell::Cell<bool>,
}
impl SpiFuture {
fn new_for_read(spi: &mut Spi, spi_id: SpiId, words: &mut [u8]) -> Self {
impl<'spi> SpiFuture<'spi> {
fn new_for_read(spi: &'spi mut Spi, spi_id: SpiId, words: &mut [u8]) -> Self {
if words.is_empty() {
panic!("words length unexpectedly 0");
}
let idx = spi_id as usize;
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
spi.inner.disable_interrupts();
Self::generic_init_transfer(spi, spi_id);
let write_idx = core::cmp::min(super::FIFO_DEPTH, words.len());
let write_index = core::cmp::min(super::FIFO_DEPTH, words.len());
// Send dummy bytes.
(0..write_idx).for_each(|_| {
(0..write_index).for_each(|_| {
spi.inner.write_fifo_unchecked(0);
});
Self::set_triggers(spi, write_idx, words.len());
Self::set_triggers(spi, write_index, words.len());
// We assume that the slave select configuration was already performed, but we take
// care of issuing a start if necessary.
spi.issue_manual_start_for_manual_cfg();
critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
let context_ref = TRANSFER_CONTEXTS[spi_id as usize].borrow(cs);
let mut context = context_ref.borrow_mut();
context.transfer_type = Some(TransferType::Read);
unsafe {
context.rx_slice.set(words);
}
context.tx_slice.set_null();
context.tx_progress = write_idx;
context.tx_progress = write_index;
context.rx_progress = 0;
spi.inner.clear_interrupts();
spi.inner.enable_interrupts();
spi.inner.enable_interrupts(write_index < words.len());
spi.inner.enable();
});
Self {
id: spi_id,
config: spi.config,
spi: unsafe { spi.inner.clone() },
spi: &mut spi.inner,
finished_regularly: core::cell::Cell::new(false),
}
}
fn new_for_write(spi: &mut Spi, spi_id: SpiId, words: &[u8]) -> Self {
fn new_for_write(spi: &'spi mut Spi, spi_id: SpiId, words: &[u8]) -> Self {
if words.is_empty() {
panic!("words length unexpectedly 0");
}
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, words);
let write_index = Self::generic_init_transfer_write_transfer_in_place(spi, spi_id, words);
critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
let context_ref = TRANSFER_CONTEXTS[spi_id as usize].borrow(cs);
let mut context = context_ref.borrow_mut();
context.transfer_type = Some(TransferType::Write);
unsafe {
context.tx_slice.set(words);
}
context.rx_slice.set_null();
context.tx_progress = write_idx;
context.tx_progress = write_index;
context.rx_progress = 0;
spi.inner.clear_interrupts();
spi.inner.enable_interrupts();
spi.inner.enable_interrupts(write_index < words.len());
spi.inner.enable();
});
Self {
id: spi_id,
config: spi.config,
spi: unsafe { spi.inner.clone() },
spi: &mut spi.inner,
finished_regularly: core::cell::Cell::new(false),
}
}
fn new_for_transfer(spi: &mut Spi, spi_id: SpiId, read: &mut [u8], write: &[u8]) -> Self {
fn new_for_transfer(spi: &'spi mut Spi, spi_id: SpiId, read: &mut [u8], write: &[u8]) -> Self {
if read.is_empty() || write.is_empty() {
panic!("read or write buffer unexpectedly empty");
}
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, write);
let full_write_len = core::cmp::max(read.len(), write.len());
let fifo_prefill = core::cmp::min(super::FIFO_DEPTH, full_write_len);
Self::generic_init_transfer(spi, spi_id);
for write_index in 0..fifo_prefill {
let value = write.get(write_index).copied().unwrap_or(0);
spi.inner.write_fifo_unchecked(value);
}
Self::set_triggers(spi, fifo_prefill, full_write_len);
critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
let context_ref = TRANSFER_CONTEXTS[spi_id as usize].borrow(cs);
let mut context = context_ref.borrow_mut();
context.transfer_type = Some(TransferType::Transfer);
unsafe {
context.tx_slice.set(write);
context.rx_slice.set(read);
}
context.tx_progress = write_idx;
context.tx_progress = fifo_prefill;
context.rx_progress = 0;
spi.inner.clear_interrupts();
spi.inner.enable_interrupts();
spi.inner.enable_interrupts(fifo_prefill < write.len());
spi.inner.enable();
});
Self {
id: spi_id,
config: spi.config,
spi: unsafe { spi.inner.clone() },
spi: &mut spi.inner,
finished_regularly: core::cell::Cell::new(false),
}
}
fn new_for_transfer_in_place(spi: &mut Spi, spi_id: SpiId, words: &mut [u8]) -> Self {
fn new_for_transfer_in_place(spi: &'spi mut Spi, spi_id: SpiId, words: &mut [u8]) -> Self {
if words.is_empty() {
panic!("read and write buffer unexpectedly empty");
}
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, words);
let write_index = Self::generic_init_transfer_write_transfer_in_place(spi, spi_id, words);
critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
let context_ref = TRANSFER_CONTEXTS[spi_id as usize].borrow(cs);
let mut context = context_ref.borrow_mut();
context.transfer_type = Some(TransferType::TransferInPlace);
unsafe {
context.rx_slice.set(words);
}
context.tx_slice.set_null();
context.tx_progress = write_idx;
context.tx_progress = write_index;
context.rx_progress = 0;
spi.inner.clear_interrupts();
spi.inner.enable_interrupts();
spi.inner.enable_interrupts(write_index < words.len());
spi.inner.enable();
});
Self {
id: spi_id,
config: spi.config,
spi: unsafe { spi.inner.clone() },
spi: &mut spi.inner,
finished_regularly: core::cell::Cell::new(false),
}
}
fn generic_init_transfer(spi: &mut Spi, spi_id: SpiId, write: &[u8]) -> (usize, usize) {
let idx = spi_id as usize;
fn generic_init_transfer(spi: &mut Spi, id: SpiId) {
let idx = id as usize;
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
spi.inner.disable();
spi.inner.disable_interrupts();
}
// Returns amount of bytes written to FIFO.
fn generic_init_transfer_write_transfer_in_place(
spi: &mut Spi,
id: SpiId,
write: &[u8],
) -> usize {
Self::generic_init_transfer(spi, id);
let write_idx = core::cmp::min(super::FIFO_DEPTH, write.len());
Self::set_triggers(spi, write_idx, write.len());
(0..write_idx).for_each(|idx| {
spi.inner.write_fifo_unchecked(write[idx]);
});
Self::set_triggers(spi, write_idx, write.len());
// We assume that the slave select configuration was already performed, but we take
// care of issuing a start if necessary.
spi.issue_manual_start_for_manual_cfg();
(idx, write_idx)
write_idx
}
fn set_triggers(spi: &mut Spi, write_idx: usize, write_len: usize) {
// This should never fail because it is never larger than the FIFO depth.
spi.inner.set_rx_fifo_trigger(write_idx as u32).unwrap();
spi.inner
.set_rx_fifo_trigger(core::cmp::min(
super::FIFO_DEPTH as u32 / 2,
write_idx as u32,
))
.unwrap();
// We want to re-fill the TX FIFO before it is completely empty if the full transfer size
// is larger than the FIFO depth. I am not sure whether the default value of 1 ensures
// this because the TMR says that this interrupt is triggered when the FIFO has less than
// threshold entries.
// is larger than the FIFO depth. Otherwise, set it to 1. Not exactly sure what that does,
// but we do not enable interrupts anyway.
if write_len > super::FIFO_DEPTH {
spi.inner.set_tx_fifo_trigger(2).unwrap();
spi.inner
.set_tx_fifo_trigger(super::FIFO_DEPTH as u32 / 2)
.unwrap();
} else {
spi.inner.set_tx_fifo_trigger(1).unwrap();
}
}
}
impl Future for SpiFuture {
type Output = ();
impl Future for SpiFuture<'_> {
type Output = Result<(), RxOverrunError>;
fn poll(
self: core::pin::Pin<&mut Self>,
@@ -427,18 +481,23 @@ impl Future for SpiFuture {
) -> core::task::Poll<Self::Output> {
WAKERS[self.id as usize].register(cx.waker());
if DONE[self.id as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
critical_section::with(|cs| {
let rx_overrun = critical_section::with(|cs| {
let mut ctx = TRANSFER_CONTEXTS[self.id as usize].borrow(cs).borrow_mut();
let overrun = ctx.rx_overrun;
*ctx = TransferContext::default();
overrun
});
self.finished_regularly.set(true);
return core::task::Poll::Ready(());
self.finished_regularly.set(!rx_overrun);
if rx_overrun {
return core::task::Poll::Ready(Err(RxOverrunError));
}
return core::task::Poll::Ready(Ok(()));
}
core::task::Poll::Pending
}
}
impl Drop for SpiFuture {
impl Drop for SpiFuture<'_> {
fn drop(&mut self) {
if !self.finished_regularly.get() {
// It might be sufficient to disable and enable the SPI.. But this definitely
@@ -456,68 +515,100 @@ pub struct SpiAsync(pub Spi);
impl SpiAsync {
pub fn new(spi: Spi) -> Self {
match spi.inner.id {
SpiId::Spi0 => {
unsafe fn spi0_interrupt_handler() {
unsafe {
on_interrupt(SpiId::Spi0);
}
}
crate::register_interrupt(
crate::gic::Interrupt::Spi(crate::gic::SpiInterrupt::Spi0),
spi0_interrupt_handler,
)
}
SpiId::Spi1 => {
unsafe fn spi1_interrupt_handler() {
unsafe {
on_interrupt(SpiId::Spi1);
}
}
crate::register_interrupt(
crate::gic::Interrupt::Spi(crate::gic::SpiInterrupt::Spi1),
spi1_interrupt_handler,
)
}
}
Self(spi)
}
async fn read(&mut self, words: &mut [u8]) {
pub fn read(&mut self, words: &mut [u8]) -> Option<SpiFuture<'_>> {
if words.is_empty() {
return;
return None;
}
let id = self.0.inner.id;
let spi_fut = SpiFuture::new_for_read(&mut self.0, id, words);
spi_fut.await;
Some(SpiFuture::new_for_read(&mut self.0, id, words))
}
async fn write(&mut self, words: &[u8]) {
pub fn write(&mut self, words: &[u8]) -> Option<SpiFuture<'_>> {
if words.is_empty() {
return;
return None;
}
let id = self.0.inner.id;
let spi_fut = SpiFuture::new_for_write(&mut self.0, id, words);
spi_fut.await;
Some(SpiFuture::new_for_write(&mut self.0, id, words))
}
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) {
pub fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Option<SpiFuture<'_>> {
if read.is_empty() || write.is_empty() {
return;
return None;
}
let id = self.0.inner.id;
let spi_fut = SpiFuture::new_for_transfer(&mut self.0, id, read, write);
spi_fut.await;
Some(SpiFuture::new_for_transfer(&mut self.0, id, read, write))
}
async fn transfer_in_place(&mut self, words: &mut [u8]) {
pub fn transfer_in_place(&mut self, words: &mut [u8]) -> Option<SpiFuture<'_>> {
if words.is_empty() {
return;
return None;
}
let id = self.0.inner.id;
let spi_fut = SpiFuture::new_for_transfer_in_place(&mut self.0, id, words);
spi_fut.await;
Some(SpiFuture::new_for_transfer_in_place(&mut self.0, id, words))
}
}
impl embedded_hal_async::spi::ErrorType for SpiAsync {
type Error = Infallible;
type Error = RxOverrunError;
}
impl embedded_hal_async::spi::SpiBus for SpiAsync {
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.read(words).await;
if words.is_empty() {
return Ok(());
}
self.read(words).unwrap().await?;
Ok(())
}
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.write(words).await;
if words.is_empty() {
return Ok(());
}
self.write(words).unwrap().await?;
Ok(())
}
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
self.transfer(read, write).await;
if read.is_empty() && write.is_empty() {
return Ok(());
}
self.transfer(read, write).unwrap().await?;
Ok(())
}
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.transfer_in_place(words).await;
if words.is_empty() {
return Ok(());
}
self.transfer_in_place(words).unwrap().await?;
Ok(())
}
@@ -547,7 +638,7 @@ impl<Delay: embedded_hal_async::delay::DelayNs> SpiWithHwCsAsync<Delay> {
impl<Delay: embedded_hal_async::delay::DelayNs> embedded_hal_async::spi::ErrorType
for SpiWithHwCsAsync<Delay>
{
type Error = Infallible;
type Error = RxOverrunError;
}
impl<Delay: embedded_hal_async::delay::DelayNs> embedded_hal_async::spi::SpiDevice
@@ -561,16 +652,24 @@ impl<Delay: embedded_hal_async::delay::DelayNs> embedded_hal_async::spi::SpiDevi
for op in operations {
match op {
embedded_hal::spi::Operation::Read(items) => {
self.spi.read(items).await;
if let Some(fut) = self.spi.read(items) {
fut.await?;
}
}
embedded_hal::spi::Operation::Write(items) => {
self.spi.write(items).await;
if let Some(fut) = self.spi.write(items) {
fut.await?;
}
}
embedded_hal::spi::Operation::Transfer(read, write) => {
self.spi.transfer(read, write).await;
if let Some(fut) = self.spi.transfer(read, write) {
fut.await?;
}
}
embedded_hal::spi::Operation::TransferInPlace(items) => {
self.spi.transfer_in_place(items).await;
if let Some(fut) = self.spi.transfer_in_place(items) {
fut.await?;
}
}
embedded_hal::spi::Operation::DelayNs(delay) => {
self.delay.delay_ns(*delay).await;
+175 -115
View File
@@ -19,9 +19,10 @@ use arbitrary_int::{prelude::*, u3, u4, u6};
use embedded_hal::delay::DelayNs;
pub use embedded_hal::spi::Mode;
use zynq7000::slcr::reset::DualRefAndClockResetSpiUart;
pub use zynq7000::spi::DelayControl;
use zynq7000::spi::{
BaudDivSel, DelayControl, FifoWrite, InterruptControl, InterruptMask, InterruptStatus,
MmioRegisters, SPI_0_BASE_ADDR, SPI_1_BASE_ADDR,
BaudDivSel, FifoWrite, InterruptControl, InterruptEnabled, InterruptStatus, MmioRegisters,
SPI_0_BASE_ADDR, SPI_1_BASE_ADDR,
};
pub const FIFO_DEPTH: usize = 128;
@@ -352,13 +353,16 @@ impl ChipSelect {
/// Slave select configuration.
pub enum SlaveSelectConfig {
/// User must take care of controlling slave select lines as well as issuing a start command.
ManualWithManualStart = 0b11,
ManualAutoStart = 0b10,
ManualCsManualStart = 0b11,
/// Software controls the slave select, but the controller hardware automatically starts to
/// serialize data when there is data in the TxFIFO.
ManualCsAutoStart = 0b10,
/// Hardware slave select, but start needs to be issued manually.
AutoWithManualStart = 0b01,
/// Hardware slave select, auto serialiation if there is data in the TX FIFO.
AutoCsManualStart = 0b01,
/// Hardware slave select, auto serialiation if there is data in the TX FIFO. Might be
/// problematic for higher SPI speeds, where the processor can not fill the TX FIFO fast enough.
#[default]
AutoWithAutoStart = 0b00,
AutoCsAutoStart = 0b00,
}
#[derive(Debug, Copy, Clone)]
@@ -379,6 +383,31 @@ impl Config {
}
}
pub fn calculate_for_io_clock(
target_clock: Hertz,
io_clock: &IoClocks,
init_mode: Mode,
ss_config: SlaveSelectConfig,
) -> Self {
let divisor_raw = io_clock.spi_clk().to_raw().div_ceil(target_clock.to_raw());
let baud_div_sel = match divisor_raw {
0..=4 => BaudDivSel::By4,
5..=8 => BaudDivSel::By8,
9..=16 => BaudDivSel::By16,
17..=32 => BaudDivSel::By32,
33..=64 => BaudDivSel::By64,
65..=128 => BaudDivSel::By128,
129..=256 => BaudDivSel::By256,
_ => BaudDivSel::By256,
};
Self {
baud_div: baud_div_sel,
init_mode,
ss_config,
with_ext_decoding: false,
}
}
pub fn enable_external_decoding(&mut self) {
self.with_ext_decoding = true;
}
@@ -422,6 +451,18 @@ impl SpiLowLevel {
}
}
pub fn enable_ref_clock(&mut self) {
// Safety: We only touch register bits of the specified peripheral to enable the clock.
unsafe {
Slcr::with(|slcr| {
slcr.clk_ctrl().modify_spi_clk_ctrl(|val| match self.id {
SpiId::Spi0 => val.with_clk_0_act(true),
SpiId::Spi1 => val.with_clk_1_act(true),
});
});
}
}
pub fn id(&self) -> SpiId {
self.id
}
@@ -447,7 +488,7 @@ impl SpiLowLevel {
/// the external decoding was enabled via the [Config::enable_external_decoding] option.
#[inline]
pub fn select_hw_cs(&mut self, chip_select: ChipSelect) {
self.regs.modify_cr(|mut val| {
self.regs.modify_config(|mut val| {
val.set_cs_raw(chip_select.raw_reg());
val
});
@@ -457,7 +498,7 @@ impl SpiLowLevel {
#[inline]
pub fn configure_mode(&mut self, mode: Mode) {
let (cpol, cpha) = spi_mode_const_to_cpol_cpha(mode);
self.regs.modify_cr(|mut val| {
self.regs.modify_config(|mut val| {
val.set_cpha(cpha);
val.set_cpol(cpol);
val
@@ -474,14 +515,14 @@ impl SpiLowLevel {
pub fn reconfigure(&mut self, config: Config) {
self.regs.write_enable(0);
let (man_ss, man_start) = match config.ss_config {
SlaveSelectConfig::ManualWithManualStart => (true, true),
SlaveSelectConfig::ManualAutoStart => (true, false),
SlaveSelectConfig::AutoWithManualStart => (false, true),
SlaveSelectConfig::AutoWithAutoStart => (false, false),
SlaveSelectConfig::ManualCsManualStart => (true, true),
SlaveSelectConfig::ManualCsAutoStart => (true, false),
SlaveSelectConfig::AutoCsManualStart => (false, true),
SlaveSelectConfig::AutoCsAutoStart => (false, false),
};
let (cpol, cpha) = spi_mode_const_to_cpol_cpha(config.init_mode);
self.regs.write_cr(
self.regs.write_config(
zynq7000::spi::Config::builder()
.with_modefail_gen_en(false)
.with_manual_start(false)
@@ -530,20 +571,17 @@ impl SpiLowLevel {
#[inline]
pub fn issue_manual_start(&mut self) {
self.regs.modify_cr(|mut val| {
val.set_manual_start(true);
val
});
self.regs.modify_config(|val| val.with_manual_start(true));
}
#[inline]
pub fn read_isr(&self) -> InterruptStatus {
self.regs.read_isr()
pub fn read_interrupt_status(&self) -> InterruptStatus {
self.regs.read_interrupt_status()
}
#[inline]
pub fn read_imr(&self) -> InterruptMask {
self.regs.read_imr()
pub fn read_enabled_interrupts(&self) -> InterruptEnabled {
self.regs.read_enabled_interrupts()
}
#[inline]
@@ -569,17 +607,22 @@ impl SpiLowLevel {
Ok(())
}
#[inline]
pub fn write_delay_control(&mut self, delay_control: DelayControl) {
self.regs.write_delay_control(delay_control);
}
/// This disables all interrupts relevant for non-blocking interrupt driven SPI operation
/// in SPI master mode.
#[inline]
pub fn disable_interrupts(&mut self) {
self.regs.write_idr(
self.regs.write_interupt_disable(
InterruptControl::builder()
.with_tx_underflow(true)
.with_rx_full(true)
.with_rx_not_empty(true)
.with_tx_full(false)
.with_tx_trig(true)
.with_tx_below_threshold(true)
.with_mode_fault(false)
.with_rx_ovr(true)
.build(),
@@ -589,14 +632,14 @@ impl SpiLowLevel {
/// This enables all interrupts relevant for non-blocking interrupt driven SPI operation
/// in SPI master mode.
#[inline]
pub fn enable_interrupts(&mut self) {
self.regs.write_ier(
pub fn enable_interrupts(&mut self, tx_below_threshold: bool) {
self.regs.write_interrupt_enable(
InterruptControl::builder()
.with_tx_underflow(true)
.with_rx_full(true)
.with_rx_not_empty(true)
.with_tx_full(false)
.with_tx_trig(true)
.with_tx_below_threshold(tx_below_threshold)
.with_mode_fault(false)
.with_rx_ovr(true)
.build(),
@@ -607,26 +650,38 @@ impl SpiLowLevel {
/// in SPI master mode.
#[inline]
pub fn clear_interrupts(&mut self) {
self.regs.write_isr(
self.regs.write_interrupt_status(
InterruptStatus::builder()
.with_tx_underflow(true)
.with_rx_full(true)
.with_rx_not_empty(true)
.with_tx_full(false)
.with_tx_not_full(true)
.with_tx_below_threshold(true)
.with_mode_fault(false)
.with_rx_ovr(true)
.with_rx_overrun(true)
.build(),
);
}
}
impl core::ops::Deref for SpiLowLevel {
type Target = MmioRegisters<'static>;
fn deref(&self) -> &Self::Target {
&self.regs
}
}
impl core::ops::DerefMut for SpiLowLevel {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.regs
}
}
/// Blocking Driver for the PS SPI peripheral in master mode.
pub struct Spi {
inner: SpiLowLevel,
sclk: Hertz,
config: Config,
outstanding_rx: bool,
}
#[derive(Debug, thiserror::Error)]
@@ -655,7 +710,6 @@ pub enum SpiConstructionError {
impl Spi {
pub fn new_no_hw_ss<Sck: SckPin, Mosi: MosiPin, Miso: MisoPin>(
spi: impl PsSpi,
clocks: &IoClocks,
config: Config,
spi_pins: (Sck, Mosi, Miso),
) -> Result<Self, SpiConstructionError> {
@@ -673,17 +727,11 @@ impl Spi {
IoPeriphPin::new(spi_pins.0, SPI_MUX_CONF, Some(false));
IoPeriphPin::new(spi_pins.1, SPI_MUX_CONF, Some(false));
IoPeriphPin::new(spi_pins.2, SPI_MUX_CONF, Some(false));
Ok(Self::new_generic_unchecked(
spi_id,
spi.reg_block(),
clocks,
config,
))
Ok(Self::new_generic_unchecked(spi_id, spi.reg_block(), config))
}
pub fn new_one_hw_cs<Sck: SckPin, Mosi: MosiPin, Miso: MisoPin, Ss: SsPin>(
spi: impl PsSpi,
clocks: &IoClocks,
config: Config,
spi_pins: (Sck, Mosi, Miso),
ss_pin: Ss,
@@ -703,17 +751,11 @@ impl Spi {
IoPeriphPin::new(spi_pins.1, SPI_MUX_CONF, Some(false));
IoPeriphPin::new(spi_pins.2, SPI_MUX_CONF, Some(false));
IoPeriphPin::new(ss_pin, SPI_MUX_CONF, Some(false));
Ok(Self::new_generic_unchecked(
spi_id,
spi.reg_block(),
clocks,
config,
))
Ok(Self::new_generic_unchecked(spi_id, spi.reg_block(), config))
}
pub fn new_with_two_hw_cs<Sck: SckPin, Mosi: MosiPin, Miso: MisoPin, Ss0: SsPin, Ss1: SsPin>(
spi: impl PsSpi,
clocks: &IoClocks,
config: Config,
spi_pins: (Sck, Mosi, Miso),
ss_pins: (Ss0, Ss1),
@@ -743,12 +785,7 @@ impl Spi {
IoPeriphPin::new(spi_pins.2, SPI_MUX_CONF, Some(false));
IoPeriphPin::new(ss_pins.0, SPI_MUX_CONF, Some(false));
IoPeriphPin::new(ss_pins.1, SPI_MUX_CONF, Some(false));
Ok(Self::new_generic_unchecked(
spi_id,
spi.reg_block(),
clocks,
config,
))
Ok(Self::new_generic_unchecked(spi_id, spi.reg_block(), config))
}
pub fn new_with_three_hw_cs<
@@ -760,7 +797,6 @@ impl Spi {
Ss2: SsPin,
>(
spi: impl PsSpi,
clocks: &IoClocks,
config: Config,
spi_pins: (Sck, Mosi, Miso),
ss_pins: (Ss0, Ss1, Ss2),
@@ -793,36 +829,40 @@ impl Spi {
IoPeriphPin::new(ss_pins.0, SPI_MUX_CONF, Some(false));
IoPeriphPin::new(ss_pins.1, SPI_MUX_CONF, Some(false));
IoPeriphPin::new(ss_pins.2, SPI_MUX_CONF, Some(false));
Ok(Self::new_generic_unchecked(spi_id, spi.reg_block(), config))
}
/// Constructor for usage with EMIO pins.
pub fn new_for_emio(spi: impl PsSpi, config: Config) -> Result<Self, InvalidPsSpiError> {
let spi_id = spi.id();
if spi_id.is_none() {
return Err(InvalidPsSpiError);
}
Ok(Self::new_generic_unchecked(
spi_id,
spi_id.unwrap(),
spi.reg_block(),
clocks,
config,
))
}
pub fn new_generic_unchecked(
id: SpiId,
regs: MmioRegisters<'static>,
clocks: &IoClocks,
config: Config,
) -> Self {
pub fn new_generic_unchecked(id: SpiId, regs: MmioRegisters<'static>, config: Config) -> Self {
let periph_sel = match id {
SpiId::Spi0 => crate::PeriphSelect::Spi0,
SpiId::Spi1 => crate::PeriphSelect::Spi1,
};
let mut ll = SpiLowLevel { id, regs };
ll.enable_ref_clock();
enable_amba_peripheral_clock(periph_sel);
let sclk = clocks.spi_clk() / config.baud_div.div_value() as u32;
let mut spi = Self {
inner: SpiLowLevel { regs, id },
sclk,
config,
outstanding_rx: false,
};
let mut spi = Self { inner: ll, config };
spi.reset_and_reconfigure();
spi
}
pub fn write_delay_control(&mut self, delay_control: DelayControl) {
self.inner.write_delay_control(delay_control);
}
/// Re-configures the SPI peripheral with the initial configuration.
pub fn reconfigure(&mut self) {
self.inner.reconfigure(self.config);
@@ -838,19 +878,13 @@ impl Spi {
#[inline]
pub fn issue_manual_start_for_manual_cfg(&mut self) {
if self.config.ss_config == SlaveSelectConfig::AutoWithManualStart
|| self.config.ss_config == SlaveSelectConfig::ManualWithManualStart
if self.config.ss_config == SlaveSelectConfig::AutoCsManualStart
|| self.config.ss_config == SlaveSelectConfig::ManualCsManualStart
{
self.inner.issue_manual_start();
}
}
/// Retrieve SCLK clock frequency currently configured for this SPI.
#[inline]
pub const fn sclk(&self) -> Hertz {
self.sclk
}
/// Retrieve inner low-level helper.
#[inline]
pub const fn inner(&mut self) -> &mut SpiLowLevel {
@@ -862,7 +896,7 @@ impl Spi {
&mut self.inner.regs
}
fn initial_fifo_fill(&mut self, words: &[u8]) -> usize {
fn prefill_fifo(&mut self, words: &[u8]) -> usize {
let write_len = core::cmp::min(FIFO_DEPTH, words.len());
(0..write_len).for_each(|idx| {
self.inner.write_fifo_unchecked(words[idx]);
@@ -878,7 +912,7 @@ impl Spi {
self.inner.regs.write_rx_trig(1);
// Fill the FIFO with initial data.
let written = self.initial_fifo_fill(words);
let written = self.prefill_fifo(words);
// We assume that the slave select configuration was already performed, but we take
// care of issuing a start if necessary.
@@ -886,7 +920,7 @@ impl Spi {
written
}
fn read(&mut self, words: &mut [u8]) {
pub fn read(&mut self, words: &mut [u8]) {
if words.is_empty() {
return;
}
@@ -908,7 +942,7 @@ impl Spi {
let mut read_idx = 0;
while read_idx < words.len() {
let status = self.regs().read_isr();
let status = self.regs().read_interrupt_status();
if status.rx_not_empty() {
words[read_idx] = self.inner.read_fifo_unchecked();
read_idx += 1;
@@ -921,33 +955,38 @@ impl Spi {
}
}
fn write(&mut self, words: &[u8]) {
pub fn write(&mut self, words: &[u8]) {
if words.is_empty() {
return;
}
let mut written = self.prepare_generic_blocking_transfer(words);
let mut read_idx = 0;
if words.len() > FIFO_DEPTH {
self.inner.regs.write_tx_trig(FIFO_DEPTH as u32 / 2);
}
while written < words.len() {
let status = self.regs().read_isr();
loop {
let status = self.regs().read_interrupt_status();
let rx_pending = read_idx < words.len();
let tx_pending = written < words.len();
// We empty the FIFO to prevent it filling up completely, as long as we have to write
// bytes
if status.rx_not_empty() {
if status.rx_not_empty() && rx_pending {
self.inner.read_fifo_unchecked();
read_idx += 1;
}
if !status.tx_full() {
if !status.tx_full() && tx_pending {
self.inner.write_fifo_unchecked(words[written]);
written += 1;
}
if !rx_pending && !tx_pending {
break;
}
}
// We exit once all bytes have been written, so some bytes to read might be outstanding.
// We use the FIFO trigger mechanism to determine when we can read all the remaining bytes.
self.regs().write_rx_trig((words.len() - read_idx) as u32);
self.outstanding_rx = true;
self.inner.regs.write_tx_trig(1);
}
fn transfer(&mut self, read: &mut [u8], write: &[u8]) {
pub fn transfer(&mut self, read: &mut [u8], write: &[u8]) {
if read.is_empty() {
return;
}
@@ -958,7 +997,7 @@ impl Spi {
let mut writes_finished = write_idx == max_idx;
let mut reads_finished = false;
while !writes_finished || !reads_finished {
let status = self.regs().read_isr();
let status = self.regs().read_interrupt_status();
if status.rx_not_empty() && !reads_finished {
if read_idx < read.len() {
read[read_idx] = self.inner.read_fifo_unchecked();
@@ -983,7 +1022,7 @@ impl Spi {
}
}
fn transfer_in_place(&mut self, words: &mut [u8]) {
pub fn transfer_in_place(&mut self, words: &mut [u8]) {
if words.is_empty() {
return;
}
@@ -993,7 +1032,7 @@ impl Spi {
let mut writes_finished = write_idx == words.len();
let mut reads_finished = false;
while !writes_finished || !reads_finished {
let status = self.inner.read_isr();
let status = self.inner.read_interrupt_status();
if status.rx_not_empty() && !reads_finished {
words[read_idx] = self.inner.read_fifo_unchecked();
read_idx += 1;
@@ -1010,16 +1049,16 @@ impl Spi {
/// Blocking flush implementation.
fn flush(&mut self) {
if !self.outstanding_rx {
return;
}
let rx_trig = self.inner.read_rx_not_empty_threshold();
while !self.inner.read_isr().rx_not_empty() {}
(0..rx_trig).for_each(|_| {
self.inner.write_tx_trig(1);
let status = self.inner.read_interrupt_status();
while self.inner.read_interrupt_status().rx_not_empty() {
self.inner.read_fifo_unchecked();
});
self.inner.set_rx_fifo_trigger(1).unwrap();
self.outstanding_rx = false;
}
while status.tx_full() {
while self.inner.read_interrupt_status().rx_not_empty() {
self.inner.read_fifo_unchecked();
}
}
}
}
@@ -1131,7 +1170,7 @@ pub fn reset(id: SpiId) {
regs.reset_ctrl().write_spi(assert_reset);
// Keep it in reset for some cycles.. The TMR just mentions some small delay,
// no idea what is meant with that.
for _ in 0..5 {
for _ in 0..10 {
aarch32_cpu::asm::nop();
}
regs.reset_ctrl()
@@ -1145,23 +1184,23 @@ pub fn reset(id: SpiId) {
/// The Zynq7000 SPI peripheral has the following requirement for the SPI reference clock:
/// It must be larger than the CPU 1X clock. Therefore, the divisor used to calculate the reference
/// clock has a maximum value, which can be calculated with this function.
/// [configure_spi_ref_clock_with_divisor] can be used to configure the SPI reference clock with a
/// divisor.
///
/// [configure_spi_ref_clk] can be used to configure the SPI reference clock with the calculated
/// value.
/// *NOTE* - It is recommended to avoid the largest theoretical value which was proven to be
/// problematic for driving certain sensors and instead take a smaller value! Reduce the divisor
/// calculated by this function subtracting a small value to get a functioning SPI clock.
pub fn calculate_largest_allowed_spi_ref_clk_divisor(clks: &Clocks) -> Option<u6> {
let slcr = unsafe { Slcr::steal() };
let spi_clk_ctrl = slcr.regs().clk_ctrl_shared().read_spi_clk_ctrl();
let div = match spi_clk_ctrl.srcsel() {
let ref_clock = match spi_clk_ctrl.srcsel() {
zynq7000::slcr::clocks::SrcSelIo::IoPll | zynq7000::slcr::clocks::SrcSelIo::IoPllAlt => {
clks.io_clocks().ref_clk() / clks.arm_clocks().cpu_1x_clk()
}
zynq7000::slcr::clocks::SrcSelIo::ArmPll => {
clks.arm_clocks().ref_clk() / clks.arm_clocks().cpu_1x_clk()
}
zynq7000::slcr::clocks::SrcSelIo::DdrPll => {
clks.ddr_clocks().ref_clk() / clks.arm_clocks().cpu_1x_clk()
clks.io_clocks().ref_clk().to_raw()
}
zynq7000::slcr::clocks::SrcSelIo::ArmPll => clks.arm_clocks().ref_clk().to_raw(),
zynq7000::slcr::clocks::SrcSelIo::DdrPll => clks.ddr_clocks().ref_clk().to_raw(),
};
let div = ref_clock.div_ceil(clks.arm_clocks().cpu_1x_clk().to_raw());
if div > u6::MAX.value() as u32 {
return None;
}
@@ -1169,7 +1208,28 @@ pub fn calculate_largest_allowed_spi_ref_clk_divisor(clks: &Clocks) -> Option<u6
Some(u6::new(div as u8))
}
pub fn configure_spi_ref_clk(clks: &mut Clocks, divisor: u6) {
/// Configures the SPI reference clock.
///
/// It is strongly advised to take a clock value which is substantially higher than the CPU 1x
/// clock. It was proven that taking values which are only slightly larger than the CPU 1x
/// clock are problematic for driving ceratin devices.
pub fn configure_spi_ref_clock(clks: &mut Clocks, target_clock: Hertz) {
let slcr = unsafe { Slcr::steal() };
let spi_clk_ctrl = slcr.regs().clk_ctrl_shared().read_spi_clk_ctrl();
let ref_clk = match spi_clk_ctrl.srcsel() {
zynq7000::slcr::clocks::SrcSelIo::IoPll | zynq7000::slcr::clocks::SrcSelIo::IoPllAlt => {
clks.io_clocks().ref_clk().to_raw()
}
zynq7000::slcr::clocks::SrcSelIo::ArmPll => clks.arm_clocks().ref_clk().to_raw(),
zynq7000::slcr::clocks::SrcSelIo::DdrPll => clks.ddr_clocks().ref_clk().to_raw(),
};
let div = ref_clk.div_ceil(target_clock.to_raw());
if div > u6::MAX.value() as u32 {
configure_spi_ref_clock_with_divisor(clks, u6::new(div as u8));
}
}
pub fn configure_spi_ref_clock_with_divisor(clks: &mut Clocks, divisor: u6) {
let mut slcr = unsafe { Slcr::steal() };
let spi_clk_ctrl = slcr.regs().clk_ctrl_shared().read_spi_clk_ctrl();
slcr.modify(|regs| {
+2 -2
View File
@@ -275,7 +275,7 @@ impl Pwm {
ref_clk: Hertz,
freq: Hertz,
) -> Result<Self, FrequencyIsZeroError> {
if freq.raw() == 0 {
if freq.to_raw() == 0 {
return Err(FrequencyIsZeroError);
}
let (prescaler_reg, tick_val) = calc_prescaler_reg_and_interval_ticks(ref_clk, freq);
@@ -289,7 +289,7 @@ impl Pwm {
///
/// This resets the duty cycle to 0%.
pub fn set_frequency(&mut self, freq: Hertz) -> Result<(), FrequencyIsZeroError> {
if freq.raw() == 0 {
if freq.to_raw() == 0 {
return Err(FrequencyIsZeroError);
}
let id = self.channel.id() as usize;
+31 -26
View File
@@ -254,8 +254,8 @@ pub fn calculate_viable_configs(
}
let mut current_clk_config = ClockConfig::default();
for bdiv in 4..u8::MAX {
let cd =
round(uart_clk.raw() as f64 / ((bdiv as u32 + 1) as f64 * target_baud as f64)) as u64;
let cd = round(uart_clk.to_raw() as f64 / ((bdiv as u32 + 1) as f64 * target_baud as f64))
as u64;
if cd > u16::MAX as u64 {
continue;
}
@@ -290,8 +290,8 @@ pub fn calculate_raw_baud_cfg_smallest_error(
let mut best_clk_config = ClockConfig::default();
let mut smallest_error: f64 = 100.0;
for bdiv in 4..u8::MAX {
let cd =
round(uart_clk.raw() as f64 / ((bdiv as u32 + 1) as f64 * target_baud as f64)) as u64;
let cd = round(uart_clk.to_raw() as f64 / ((bdiv as u32 + 1) as f64 * target_baud as f64))
as u64;
if cd > u16::MAX as u64 {
continue;
}
@@ -369,7 +369,7 @@ impl ClockConfig {
/// Actual baudrate.
#[inline]
pub fn actual_baud(&self, sel_clk: Hertz) -> f64 {
sel_clk.raw() as f64 / (self.cd as f64 * (self.bdiv + 1) as f64)
sel_clk.to_raw() as f64 / (self.cd as f64 * (self.bdiv + 1) as f64)
}
}
@@ -565,35 +565,40 @@ impl Uart {
UartId::Uart0 => crate::PeriphSelect::Uart0,
UartId::Uart1 => crate::PeriphSelect::Uart1,
};
// Safety: We only touch register bits of the specified peripheral to enable the clock.
unsafe {
Slcr::with(|slcr| {
slcr.clk_ctrl().modify_uart_clk_ctrl(|val| match uart_id {
UartId::Uart0 => val.with_clk_0_act(true),
UartId::Uart1 => val.with_clk_1_act(true),
});
});
}
enable_amba_peripheral_clock(periph_sel);
reset(uart_id);
reg_block.modify_cr(|mut v| {
v.set_tx_dis(true);
v.set_rx_dis(true);
v
});
reg_block.modify_control(|v| v.with_tx_disable(true).with_rx_disable(true));
// Disable all interrupts.
reg_block.write_idr(InterruptControl::new_with_raw_value(0xFFFF_FFFF));
reg_block.write_interrupt_disable(InterruptControl::new_with_raw_value(0xFFFF_FFFF));
let mode = Mode::builder()
.with_chmode(cfg.chmode)
.with_nbstop(match cfg.stopbits {
.with_stopbits(match cfg.stopbits {
Stopbits::One => zynq7000::uart::Stopbits::One,
Stopbits::OnePointFive => zynq7000::uart::Stopbits::OnePointFive,
Stopbits::Two => zynq7000::uart::Stopbits::Two,
})
.with_par(match cfg.parity {
.with_parity(match cfg.parity {
Parity::Even => zynq7000::uart::Parity::Even,
Parity::Odd => zynq7000::uart::Parity::Odd,
Parity::None => zynq7000::uart::Parity::NoParity,
})
.with_chrl(match cfg.chrl {
.with_charlen(match cfg.chrl {
CharLen::SixBits => zynq7000::uart::CharLen::SixBits,
CharLen::SevenBits => zynq7000::uart::CharLen::SevenBits,
CharLen::EightBits => zynq7000::uart::CharLen::EightBits,
})
.with_clksel(cfg.clk_sel)
.with_clock_select(cfg.clk_sel)
.build();
reg_block.write_mr(mode);
reg_block.write_mode(mode);
reg_block.write_baudgen(
Baudgen::builder()
.with_cd(cfg.raw_clk_config().cd())
@@ -605,9 +610,9 @@ impl Uart {
.build(),
);
// Soft reset for both TX and RX.
reg_block.modify_cr(|mut v| {
v.set_tx_rst(true);
v.set_rx_rst(true);
reg_block.modify_control(|mut v| {
v.set_tx_reset(true);
v.set_rx_reset(true);
v
});
@@ -617,11 +622,11 @@ impl Uart {
));
// Enable TX and RX.
reg_block.modify_cr(|mut v| {
v.set_tx_dis(false);
v.set_rx_dis(false);
v.set_tx_en(true);
v.set_rx_en(true);
reg_block.modify_control(|mut v| {
v.set_tx_disable(false);
v.set_rx_disable(false);
v.set_tx_enable(true);
v.set_rx_enable(true);
v
});
@@ -631,7 +636,7 @@ impl Uart {
},
tx: Tx {
regs: reg_block,
idx: uart_id,
id: uart_id,
},
cfg,
}
@@ -640,7 +645,7 @@ impl Uart {
/// Set character mode.
#[inline]
pub fn set_mode(&mut self, mode: ChMode) {
self.regs().modify_mr(|mut mr| {
self.regs().modify_mode(|mut mr| {
mr.set_chmode(mode);
mr
});
+32 -26
View File
@@ -2,7 +2,7 @@
use core::convert::Infallible;
use arbitrary_int::prelude::*;
use zynq7000::uart::{InterruptControl, InterruptStatus, MmioRegisters};
use zynq7000::uart::{FifoTrigger, InterruptControl, InterruptStatus, MmioRegisters};
use super::FIFO_DEPTH;
@@ -73,7 +73,7 @@ impl Rx {
/// Read one byte from the FIFO in a non-blocking manner.
#[inline]
pub fn read_fifo(&mut self) -> nb::Result<u8, Infallible> {
if self.regs.read_sr().rx_empty() {
if self.regs.read_status().rx_empty() {
return Err(nb::Error::WouldBlock);
}
Ok(self.regs.read_fifo().fifo())
@@ -93,17 +93,17 @@ impl Rx {
/// bit clock, so this value times 4 is the number of UART clock ticks until a timeout occurs.
#[inline]
pub fn set_rx_timeout_value(&mut self, rto: u8) {
self.regs.write_rx_tout(rto as u32);
self.regs.write_rx_timeout(rto as u32);
}
/// Perform a soft-reset of the RX side of the UART.
#[inline]
pub fn soft_reset(&mut self) {
self.regs.modify_cr(|mut cr| {
cr.set_rx_rst(true);
self.regs.modify_control(|mut cr| {
cr.set_rx_reset(true);
cr
});
while self.regs.read_cr().rx_rst() {}
while self.regs.read_control().rx_reset() {}
}
/// Helper function to start the interrupt driven reception of data.
@@ -114,22 +114,34 @@ impl Rx {
///
/// This should be called once at system start-up. After that, you only need to call
/// [Self::on_interrupt] in the interrupt handler for the UART peripheral.
pub fn start_interrupt_driven_reception(&mut self) {
///
/// You can also configure a RX timeout by setting the RX timeout value `rto` which has a unit
/// of bit periods times 4. Setting a value of 0 disables the timeout feature of the hardware,
/// but this is strongly discouraged.
pub fn start_interrupt_driven_reception(&mut self, rto: u8) {
self.soft_reset();
self.set_rx_fifo_trigger_level((FIFO_DEPTH / 2) as u8);
self.set_rx_timeout_value(rto);
self.clear_interrupts();
self.enable_interrupts();
}
/// Sets the RX FIFO trigger level.
pub fn set_rx_fifo_trigger_level(&mut self, level: u8) {
self.regs
.write_rx_fifo_trigger(FifoTrigger::new_with_raw_value(level as u32));
}
/// Enables all interrupts relevant for the RX side of the UART.
///
/// It is recommended to also clear all interrupts immediately after enabling them.
#[inline]
pub fn enable_interrupts(&mut self) {
self.regs.write_ier(
self.regs.write_interrupt_enable(
InterruptControl::builder()
.with_tx_over(false)
.with_tx_near_full(false)
.with_tx_trig(false)
.with_tx_trigger(false)
.with_rx_dms(false)
.with_rx_timeout(true)
.with_rx_parity(true)
@@ -139,7 +151,7 @@ impl Rx {
.with_tx_empty(false)
.with_rx_full(true)
.with_rx_empty(false)
.with_rx_trg(true)
.with_rx_trigger(true)
.build(),
);
}
@@ -153,9 +165,9 @@ impl Rx {
reset_rx_timeout: bool,
) -> RxInterruptResult {
let mut result = RxInterruptResult::default();
let imr = self.regs.read_imr();
let imr = self.regs.read_enabled_interrupts();
if !imr.rx_full()
&& !imr.rx_trg()
&& !imr.rx_trigger()
&& !imr.rx_parity()
&& !imr.rx_framing()
&& !imr.rx_over()
@@ -163,16 +175,10 @@ impl Rx {
{
return result;
}
let isr = self.regs.read_isr();
if isr.rx_full() {
// Read all bytes in the full RX fifo.
for byte in buf.iter_mut() {
*byte = self.read_fifo_unchecked();
}
result.read_bytes = FIFO_DEPTH;
} else if isr.rx_trg() {
let isr = self.regs.read_interrupt_status();
if self.regs.read_interrupt_status().rx_trigger() {
// It is guaranteed that we can read the FIFO level amount of data
let fifo_trigger = self.regs.read_rx_fifo_trigger().trig().as_usize();
let fifo_trigger = self.regs.read_rx_fifo_trigger().trigger().as_usize();
(0..fifo_trigger).for_each(|i| {
buf[i] = self.read_fifo_unchecked();
});
@@ -197,8 +203,8 @@ impl Rx {
}
// Handle timeout event.
if isr.rx_timeout() && reset_rx_timeout {
self.regs.modify_cr(|mut cr| {
cr.set_rstto(true);
self.regs.modify_control(|mut cr| {
cr.set_restart_timeout(true);
cr
});
}
@@ -209,7 +215,7 @@ impl Rx {
/// This clears all RX related interrupts.
#[inline]
pub fn clear_interrupts(&mut self) {
self.regs.write_isr(
self.regs.write_interrupt_status(
InterruptStatus::builder()
.with_tx_over(false)
.with_tx_near_full(false)
@@ -223,7 +229,7 @@ impl Rx {
.with_tx_empty(false)
.with_rx_full(true)
.with_rx_empty(true)
.with_rx_trg(true)
.with_rx_trigger(true)
.build(),
);
}
@@ -256,7 +262,7 @@ impl embedded_io::Read for Rx {
}
let mut read = 0;
loop {
if !self.regs.read_sr().rx_empty() {
if !self.regs.read_status().rx_empty() {
break;
}
}
+33 -33
View File
@@ -8,12 +8,12 @@ use super::UartId;
/// Transmitter (TX) driver.
pub struct Tx {
pub(crate) regs: MmioRegisters<'static>,
pub(crate) idx: UartId,
pub(crate) id: UartId,
}
impl core::fmt::Debug for Tx {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Tx").field("idx", &self.idx).finish()
f.debug_struct("Tx").field("idx", &self.id).finish()
}
}
@@ -24,17 +24,17 @@ impl Tx {
///
/// Circumvents safety guarantees provided by the compiler.
#[inline]
pub const unsafe fn steal(idx: UartId) -> Self {
pub const unsafe fn steal(id: UartId) -> Self {
Tx {
regs: unsafe { idx.regs() },
idx,
regs: unsafe { id.regs() },
id,
}
}
/// UART index.
/// UART ID.
#[inline]
pub const fn uart_idx(&self) -> UartId {
self.idx
pub const fn uart_id(&self) -> UartId {
self.id
}
/// Direct access to the UART MMIO registers.
@@ -48,7 +48,7 @@ impl Tx {
/// [nb] API which returns [nb::Error::WouldBlock] if the FIFO is full.
#[inline]
pub fn write_fifo(&mut self, word: u8) -> nb::Result<(), Infallible> {
if self.regs.read_sr().tx_full() {
if self.regs.read_status().tx_full() {
return Err(nb::Error::WouldBlock);
}
self.write_fifo_unchecked(word);
@@ -61,9 +61,9 @@ impl Tx {
if with_reset {
self.soft_reset();
}
self.regs.modify_cr(|mut val| {
val.set_tx_en(true);
val.set_tx_dis(false);
self.regs.modify_control(|mut val| {
val.set_tx_enable(true);
val.set_tx_disable(false);
val
});
}
@@ -71,9 +71,9 @@ impl Tx {
/// Disables TX side of the UART.
#[inline]
pub fn disable(&mut self) {
self.regs.modify_cr(|mut val| {
val.set_tx_en(false);
val.set_tx_dis(true);
self.regs.modify_control(|mut val| {
val.set_tx_enable(false);
val.set_tx_disable(true);
val
});
}
@@ -81,12 +81,12 @@ impl Tx {
/// Performs a soft-reset of the TX side of the UART.
#[inline]
pub fn soft_reset(&mut self) {
self.regs.modify_cr(|mut val| {
val.set_tx_rst(true);
self.regs.modify_control(|mut val| {
val.set_tx_reset(true);
val
});
loop {
if !self.regs.read_cr().tx_rst() {
if !self.regs.read_control().tx_reset() {
break;
}
}
@@ -94,7 +94,7 @@ impl Tx {
/// Flushes the TX FIFO by blocking until it is empty.
pub fn flush(&mut self) {
while !self.regs.read_sr().tx_empty() {}
while !self.regs.read_status().tx_empty() {}
}
/// Write a byte to the TX FIFO without checking if there is space available.
@@ -105,12 +105,12 @@ impl Tx {
/// Enables interrupts relevant for the TX side of the UART except the TX trigger interrupt.
#[inline]
pub fn enable_interrupts(&mut self) {
self.regs.write_ier(
pub fn enable_interrupts(&mut self, tx_trig: bool) {
self.regs.write_interrupt_enable(
InterruptControl::builder()
.with_tx_over(true)
.with_tx_near_full(true)
.with_tx_trig(false)
.with_tx_near_full(false)
.with_tx_trigger(tx_trig)
.with_rx_dms(false)
.with_rx_timeout(false)
.with_rx_parity(false)
@@ -120,7 +120,7 @@ impl Tx {
.with_tx_empty(true)
.with_rx_full(false)
.with_rx_empty(false)
.with_rx_trg(false)
.with_rx_trigger(false)
.build(),
);
}
@@ -128,11 +128,11 @@ impl Tx {
/// Disable interrupts relevant for the TX side of the UART except the TX trigger interrupt.
#[inline]
pub fn disable_interrupts(&mut self) {
self.regs.write_idr(
self.regs.write_interrupt_disable(
InterruptControl::builder()
.with_tx_over(true)
.with_tx_near_full(true)
.with_tx_trig(false)
.with_tx_near_full(false)
.with_tx_trigger(true)
.with_rx_dms(false)
.with_rx_timeout(false)
.with_rx_parity(false)
@@ -142,7 +142,7 @@ impl Tx {
.with_tx_empty(true)
.with_rx_full(false)
.with_rx_empty(false)
.with_rx_trg(false)
.with_rx_trigger(false)
.build(),
);
}
@@ -150,11 +150,11 @@ impl Tx {
/// Clears interrupts relevant for the TX side of the UART except the TX trigger interrupt.
#[inline]
pub fn clear_interrupts(&mut self) {
self.regs.write_isr(
self.regs.write_interrupt_status(
InterruptStatus::builder()
.with_tx_over(true)
.with_tx_near_full(true)
.with_tx_trig(false)
.with_tx_trig(true)
.with_rx_dms(false)
.with_rx_timeout(false)
.with_rx_parity(false)
@@ -164,7 +164,7 @@ impl Tx {
.with_tx_empty(true)
.with_rx_full(false)
.with_rx_empty(false)
.with_rx_trg(false)
.with_rx_trigger(false)
.build(),
);
}
@@ -181,7 +181,7 @@ impl embedded_hal_nb::serial::Write for Tx {
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
if self.regs.read_sr().tx_empty() {
if self.regs.read_status().tx_empty() {
return Ok(());
}
Err(nb::Error::WouldBlock)
@@ -199,7 +199,7 @@ impl embedded_io::Write for Tx {
}
let mut written = 0;
loop {
if !self.regs.read_sr().tx_full() {
if !self.regs.read_status().tx_full() {
break;
}
}
+93 -25
View File
@@ -1,9 +1,11 @@
//! Asynchronous UART transmitter (TX) implementation.
use core::{cell::RefCell, convert::Infallible, sync::atomic::AtomicBool};
use core::{cell::RefCell, convert::Infallible, marker::PhantomData, sync::atomic::AtomicBool};
use arbitrary_int::u6;
use critical_section::Mutex;
use embassy_sync::waitqueue::AtomicWaker;
use raw_slice::RawBufSlice;
use zynq7000::uart::FifoTrigger;
use crate::uart::{FIFO_DEPTH, Tx, UartId};
@@ -17,20 +19,30 @@ static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given
/// UART peripheral.
///
/// # Safety
///
/// The user has to call this once in the interrupt handler responsible for the TX interrupts on
/// the given UART bank.
pub fn on_interrupt_tx(peripheral: UartId) {
pub unsafe fn on_interrupt_tx(peripheral: UartId) {
let mut tx_with_irq = unsafe { Tx::steal(peripheral) };
let idx = peripheral as usize;
let imr = tx_with_irq.regs().read_imr();
let enabled_irqs = tx_with_irq.regs().read_enabled_interrupts();
// IRQ is not related to TX.
if !imr.tx_over() && !imr.tx_near_full() && !imr.tx_full() && !imr.tx_empty() && !imr.tx_full()
if !enabled_irqs.tx_over()
&& !enabled_irqs.tx_near_full()
&& !enabled_irqs.tx_full()
&& !enabled_irqs.tx_empty()
&& !enabled_irqs.tx_full()
{
return;
}
let isr = tx_with_irq.regs().read_isr();
let unexpected_overrun = isr.tx_over();
let interrupt_status = tx_with_irq.regs().read_interrupt_status();
// Disable interrupts, re-enable them later.
tx_with_irq.disable_interrupts();
// Clear interrupts.
tx_with_irq.clear_interrupts();
let unexpected_overrun = interrupt_status.tx_over();
let mut context = critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs);
*context_ref.borrow()
@@ -41,7 +53,7 @@ pub fn on_interrupt_tx(peripheral: UartId) {
}
let slice_len = context.slice.len().unwrap();
context.tx_overrun = unexpected_overrun;
if (context.progress >= slice_len && isr.tx_empty()) || slice_len == 0 {
if (context.progress >= slice_len && interrupt_status.tx_empty()) || slice_len == 0 {
// Write back updated context structure.
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs);
@@ -57,8 +69,10 @@ pub fn on_interrupt_tx(peripheral: UartId) {
// Safety: We documented that the user provided slice must outlive the future, so we convert
// the raw pointer back to the slice here.
let slice = unsafe { context.slice.get() }.expect("slice is invalid");
// Pump the FIFO.
while context.progress < slice_len {
if tx_with_irq.regs().read_sr().tx_full() {
if tx_with_irq.regs().read_status().tx_full() {
break;
}
// Safety: TX structure is owned by the future which does not write into the the data
@@ -66,14 +80,22 @@ pub fn on_interrupt_tx(peripheral: UartId) {
tx_with_irq.write_fifo_unchecked(slice[context.progress]);
context.progress += 1;
}
let remaining = slice_len - context.progress;
if remaining > FIFO_DEPTH {
tx_with_irq.regs.write_tx_fifo_trigger(
FifoTrigger::builder()
.with_trigger(u6::new((FIFO_DEPTH / 2) as u8))
.build(),
);
}
// Write back updated context structure.
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[idx].borrow(cs);
*context_ref.borrow_mut() = context;
});
// Clear interrupts.
tx_with_irq.clear_interrupts();
tx_with_irq.enable_interrupts(remaining > FIFO_DEPTH);
}
#[derive(Debug, Copy, Clone)]
@@ -95,17 +117,18 @@ impl TxContext {
}
/// Transmission future for UART TX.
pub struct TxFuture {
pub struct TxFuture<'uart> {
id: UartId,
marker: core::marker::PhantomData<&'uart ()>,
}
impl TxFuture {
impl<'uart> TxFuture<'uart> {
/// # Safety
///
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
/// that the slice outlives the data structure.
pub unsafe fn new(tx_with_irq: &mut Tx, data: &[u8]) -> Self {
let idx = tx_with_irq.uart_idx() as usize;
pub unsafe fn new(tx_with_irq: &'uart mut Tx, data: &[u8]) -> TxFuture<'uart> {
let idx = tx_with_irq.uart_id() as usize;
TX_DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
tx_with_irq.disable_interrupts();
tx_with_irq.disable();
@@ -119,19 +142,29 @@ impl TxFuture {
}
context.progress = init_fill_count; // We fill the FIFO.
});
tx_with_irq.enable(true);
// Apparently, we need to enable the UART before we are able to write something into
// the FIFO.
tx_with_irq.enable(false);
if data.len() > FIFO_DEPTH {
tx_with_irq.regs.write_tx_fifo_trigger(
FifoTrigger::builder()
.with_trigger(u6::new((FIFO_DEPTH / 2) as u8))
.build(),
);
}
for data in data.iter().take(init_fill_count) {
tx_with_irq.write_fifo_unchecked(*data);
}
tx_with_irq.enable_interrupts();
tx_with_irq.enable_interrupts(data.len() > FIFO_DEPTH);
Self {
id: tx_with_irq.uart_idx(),
id: tx_with_irq.uart_id(),
marker: PhantomData,
}
}
}
impl Future for TxFuture {
impl Future for TxFuture<'_> {
type Output = usize;
fn poll(
@@ -151,7 +184,7 @@ impl Future for TxFuture {
}
}
impl Drop for TxFuture {
impl Drop for TxFuture<'_> {
fn drop(&mut self) {
let mut tx = unsafe { Tx::steal(self.id) };
tx.disable_interrupts();
@@ -165,20 +198,52 @@ pub struct TxAsync {
impl TxAsync {
/// Constructor.
pub fn new(tx: Tx) -> Self {
///
/// The second argument specifies whether the [on_interrupt_tx] function will be registered
/// in the HAL interrupt map. You might need to skip this in case you have your own
/// interrupt handler which also handles RX interrupts.
pub fn new(tx: Tx, register_interrupt_handler: bool) -> Self {
if register_interrupt_handler {
match tx.uart_id() {
UartId::Uart0 => {
unsafe fn uart0_interrupt_handler() {
unsafe {
on_interrupt_tx(UartId::Uart0);
}
}
crate::register_interrupt(
crate::gic::Interrupt::Spi(crate::gic::SpiInterrupt::Uart0),
uart0_interrupt_handler,
)
}
UartId::Uart1 => {
unsafe fn uart1_interrupt_handler() {
unsafe {
on_interrupt_tx(UartId::Uart1);
}
}
crate::register_interrupt(
crate::gic::Interrupt::Spi(crate::gic::SpiInterrupt::Uart1),
uart1_interrupt_handler,
)
}
}
}
Self { tx }
}
/// Write a buffer asynchronously.
///
/// Returns [None] if the passed buffer is empty.
///
/// This implementation is not side effect free, and a started future might have already
/// written part of the passed buffer.
pub async fn write(&mut self, buf: &[u8]) -> usize {
pub fn write(&mut self, buf: &[u8]) -> Option<TxFuture<'_>> {
if buf.is_empty() {
return 0;
return None;
}
let fut = unsafe { TxFuture::new(&mut self.tx, buf) };
fut.await
Some(unsafe { TxFuture::new(&mut self.tx, buf) })
}
/// Release the underlying blocking TX driver.
@@ -197,7 +262,10 @@ impl embedded_io_async::Write for TxAsync {
/// This implementation is not side effect free, and a started future might have already
/// written part of the passed buffer.
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
Ok(self.write(buf).await)
if buf.is_empty() {
return Ok(0);
}
Ok(self.write(buf).unwrap().await)
}
/// This implementation does not do anything.
+7 -2
View File
@@ -8,9 +8,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
# [v0.2.0] 2026-05-08
- Bumped `aarch32-cpu` to v0.3
# [v0.1.2] 2026-02-14
Bumped `aarch32-cpu` to v0.2
- Bumped `aarch32-cpu` to v0.2
# [v0.1.1] 2025-10-10
@@ -20,7 +24,8 @@ Documentation fixes.
Initial release
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-mmu-v0.1.2...HEAD
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-mmu-v0.2.0...HEAD
[v0.2.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-mmu-v0.1.2...zynq7000-mmu-v0.2.0
[v0.1.2]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-mmu-v0.1.1...zynq7000-mmu-v0.1.2
[v0.1.1]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-mmu-v0.1.0...zynq7000-mmu-v0.1.1
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/tag/zynq7000-mmu-v0.1.0
+2 -2
View File
@@ -1,7 +1,7 @@
[package]
name = "zynq7000-mmu"
description = "Zynq7000 MMU structures"
version = "0.1.2"
version = "0.2.0"
edition = "2024"
license = "MIT OR Apache-2.0"
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
@@ -11,7 +11,7 @@ categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
thiserror = { version = "2", default-features = false }
aarch32-cpu = { version = "0.2" }
aarch32-cpu = { version = "0.3" }
[build-dependencies]
arm-targets = { version = "0.4" }
-3
View File
@@ -6,7 +6,6 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
use aarch32_cpu::mmu::L1Section;
#[cfg(all(not(feature = "tools"), arm_profile = "a"))]
use aarch32_cpu::{
asm::{dsb, isb},
cache::clean_and_invalidate_l1_data_cache,
@@ -39,7 +38,6 @@ impl L1TableRaw {
self.0.as_mut_ptr() as *mut _
}
#[cfg(all(not(feature = "tools"), arm_profile = "a"))]
pub fn update(
&mut self,
addr: u32,
@@ -92,7 +90,6 @@ impl<'a> L1TableWrapper<'a> {
}
impl L1TableWrapper<'_> {
#[cfg(all(not(feature = "tools"), arm_profile = "a"))]
pub fn update(
&mut self,
addr: u32,
+6 -1
View File
@@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
# [v0.3.0] 2026-05-08
Bumped `aarch32-rt` and `aarch32-cpu` to v0.3.
# [v0.2.0] 2026-02-14
Bugfixes in startup assembler code.
@@ -35,7 +39,8 @@ Documentation fixes.
Initial release
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.2.0...HEAD
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.3.0...HEAD
[v0.3.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.2.0...zynq7000-rt-v0.3.0
[v0.2.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.1.1...zynq7000-rt-v0.2.0
[v0.1.1]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-rt-v0.1.0...zynq7000-rt-v0.1.1
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/tag/zynq7000-rt-v0.1.0
+4 -4
View File
@@ -1,6 +1,6 @@
[package]
name = "zynq7000-rt"
version = "0.2.0"
version = "0.3.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2024"
description = "Run-time support for the Zynq7000 family of SoCs for running bare-metal applications"
@@ -11,10 +11,10 @@ keywords = ["no-std", "rt", "cortex-a", "amd", "zynq7000"]
categories = ["embedded", "no-std", "hardware-support"]
[dependencies]
aarch32-rt = { version = "0.2", optional = true, features = ["fpu-d32"] }
aarch32-cpu = { version = "0.2" }
aarch32-rt = { version = "0.3", optional = true, features = ["fpu-d32"] }
aarch32-cpu = { version = "0.3" }
arbitrary-int = "2"
zynq7000-mmu = { path = "../zynq7000-mmu", version = "0.1" }
zynq7000-mmu = { path = "../zynq7000-mmu", version = "0.2" }
[build-dependencies]
arm-targets = { version = "0.4" }
+5 -5
View File
@@ -85,7 +85,7 @@ pub mod segments {
pub mod section_attrs {
use aarch32_cpu::mmu::{
AccessPermissions, CacheableMemoryAttribute, MemoryRegionAttributes, SectionAttributes,
AccessPermissions, CachePolicy, MemoryRegionAttributes, SectionAttributes,
};
use arbitrary_int::u4;
@@ -103,8 +103,8 @@ pub mod section_attrs {
domain: DDR_DOMAIN,
execute_never: false,
memory_attrs: MemoryRegionAttributes::CacheableMemory {
inner: CacheableMemoryAttribute::WriteBackWriteAlloc,
outer: CacheableMemoryAttribute::WriteBackWriteAlloc,
inner: CachePolicy::WriteBackWriteAlloc,
outer: CachePolicy::WriteBackWriteAlloc,
}
.as_raw(),
};
@@ -157,8 +157,8 @@ pub mod section_attrs {
domain: DEFAULT_DOMAIN,
execute_never: false,
memory_attrs: MemoryRegionAttributes::CacheableMemory {
inner: CacheableMemoryAttribute::WriteThroughNoWriteAlloc,
outer: CacheableMemoryAttribute::NonCacheable,
inner: CachePolicy::WriteThroughNoWriteAlloc,
outer: CachePolicy::NonCacheable,
}
.as_raw(),
};
+2
View File
@@ -94,6 +94,8 @@ initialize:
bic r0, r0, #0x1 /* clear bit 0 */
mcr p15, 0, r0, c1, c0, 0 /* write value back */
/* We must set the core number for this function */
mov r0,#0
bl _stack_setup_preallocated
// set scu enable bit in scu
+12 -1
View File
@@ -8,6 +8,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased]
# [v0.4.0] 2026-05-15
- Better names for `uart` registers and register fields. Replaced various abbreviations.
- Update `gic` module: Add some better type for SGIR field.
# [v0.3.0] 2026-05-08
- Better names for various registers. Replaced abbreviations like SR, MR, CR, IER, IMR etc.
# [v0.2.0] 2026-04-01
- Renamed all register blocks to `Registers` to subblocks to `<Subblock>Registers`.
@@ -26,7 +35,9 @@ Documentation fix
Initial release
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-v0.2.0...HEAD
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-v0.4.0...HEAD
[v0.4.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-v0.3.0...zynq7000-v0.4.0
[v0.3.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-v0.2.0...zynq7000-v0.3.0
[v0.2.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-v0.1.0...zynq7000-v0.2.0
[v0.1.1]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-v0.1.0...zynq7000-v0.1.1
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/tag/zynq7000-v0.1.0
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "zynq7000"
version = "0.2.0"
version = "0.4.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2024"
description = "Peripheral Access Crate (PAC) for the Zynq7000 family of SoCs"
+43 -8
View File
@@ -1,6 +1,6 @@
//! # GIC (Generic Interrupt Controller) register module.
pub use crate::mpcore::{GICC_BASE_ADDR, GICD_BASE_ADDR};
use arbitrary_int::{u2, u3, u5, u10};
use arbitrary_int::{u2, u3, u4, u5, u10, u11};
use static_assertions::const_assert_eq;
/// Distributor Control Register
@@ -55,9 +55,6 @@ pub struct InterruptProcessorTargetRegister {
targets: [u2; 4],
}
#[deprecated(note = "Use DistributorRegisters instead")]
pub type GicDistributorTyper = DistributorRegisters;
/// GIC Distributor registers.
#[derive(derive_mmio::Mmio)]
#[repr(C, align(8))]
@@ -92,7 +89,7 @@ pub struct DistributorRegisters {
/// Interrupt Priority Registers
pub ipr: [u32; 0x18],
_reserved_11: [u32; 0xE8],
/// Interrupt Processor Targes Registers
/// Interrupt Processor Targets Registers
pub iptr_sgi: [InterruptProcessorTargetRegister; 0x4],
/// These are read-only because they always target their private CPU.
#[mmio(PureRead)]
@@ -102,6 +99,7 @@ pub struct DistributorRegisters {
_reserved_12: [u32; 0xE8],
/// Interrupt Configuration Registers
/// Interupt sensitivity register for software generated interrupts (SGI)
#[mmio(PureRead)]
pub icfr_0_sgi: u32,
/// Interupt sensitivity register for private peripheral interrupts (PPI)
pub icfr_1_ppi: u32,
@@ -115,7 +113,7 @@ pub struct DistributorRegisters {
pub spi_status_1: u32,
_reserved_14: [u32; 0x7D],
/// Software Generated Interrupt Register.
pub sgir: u32,
pub sgir: SoftwareGeneratedInterruptRegister,
_reserved_15: [u32; 0x33],
pub pidr_4: u32,
pub pidr_5: u32,
@@ -182,8 +180,45 @@ pub struct InterruptSignalRegister {
ack_int_id: u10,
}
#[deprecated(note = "Use DistributorRegisters instead")]
pub type GicCpuInterfaceIar = CpuInterfaceRegisters;
#[bitbybit::bitenum(u1, exhaustive = true)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, PartialEq, Eq)]
pub enum SecurityCondition {
IfConfiguredAsSecure = 0,
IfConfiguredAsNonSecure = 1,
}
#[bitbybit::bitenum(u2, exhaustive = true)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, PartialEq, Eq)]
pub enum TargetListFilter {
SendToCpusInTargetList = 0b00,
SendToAllOtherCpus = 0b01,
SendToSelf = 0b10,
Reserved = 0b11,
}
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
pub struct SoftwareGeneratedInterruptRegister {
#[bits(24..=25, rw)]
target_list_filter: TargetListFilter,
#[bits(16..=23, rw)]
cpu_target_list: u8,
/// SATT field.
#[bit(15, rw)]
security_condition: SecurityCondition,
/// Should be zero.
#[bits(4..=14, rw)]
sbz: u11,
#[bits(0..=3, rw)]
interrupt_id: u4,
}
/// GIC CPU interface registers.
#[derive(derive_mmio::Mmio)]
+3 -3
View File
@@ -3,7 +3,7 @@
pub const GTC_BASE_ADDR: usize = super::mpcore::MPCORE_BASE_ADDR + 0x0000_0200;
#[bitbybit::bitfield(u32, debug, forbid_overlaps, defmt_bitfields(feature = "defmt"))]
pub struct GtcControl {
pub struct Control {
#[bits(8..=15, rw)]
prescaler: u8,
#[bit(3, rw)]
@@ -31,10 +31,10 @@ pub struct Registers {
/// Count register 1, upper 32 bits
count_upper: u32,
/// Control register
ctrl: GtcControl,
control: Control,
/// Interrupt status register
#[mmio(PureRead, Write)]
isr: InterruptStatus,
interrupt_status: InterruptStatus,
/// Comparator 0, lower 32 bits
comparator_lower: u32,
/// Comparator 1, upper 32 bits
+6 -6
View File
@@ -162,23 +162,23 @@ pub struct TransferSize {
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Registers {
cr: Control,
control: Control,
#[mmio(PureRead)]
sr: Status,
status: Status,
addr: Address,
#[mmio(Read, Write)]
data: Fifo,
#[mmio(PureRead, Write, Modify)]
isr: InterruptStatus,
interrupt_status: InterruptStatus,
transfer_size: TransferSize,
slave_pause: u32,
timeout: Timeout,
#[mmio(PureRead)]
imr: InterruptMask,
enabled_interrupts: InterruptMask,
#[mmio(Write)]
ier: InterruptControl,
interrupt_enable: InterruptControl,
#[mmio(Write)]
idr: InterruptControl,
interrupt_disable: InterruptControl,
}
static_assertions::const_assert_eq!(core::mem::size_of::<Registers>(), 0x2C);
+18 -11
View File
@@ -6,7 +6,7 @@ pub use crate::{SpiClockPhase, SpiClockPolarity};
pub const SPI_0_BASE_ADDR: usize = 0xE000_6000;
pub const SPI_1_BASE_ADDR: usize = 0xE000_7000;
/// The SPI reference block will be divided by a divisor value.
/// The SPI reference clock will be divided by a divisor value.
#[bitbybit::bitenum(u3)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -87,15 +87,18 @@ pub struct InterruptStatus {
rx_full: bool,
#[bit(4, rw)]
rx_not_empty: bool,
/// Switches to 1 when the FIFO becomes full and then remains asserted until the FIFO falls
/// below the configured threshold level.
#[bit(3, rw)]
tx_full: bool,
/// TX FIFO level below configured threshold.
#[bit(2, rw)]
tx_not_full: bool,
tx_below_threshold: bool,
#[bit(1, rw)]
mode_fault: bool,
/// Receiver overflow interrupt.
#[bit(0, rw)]
rx_ovr: bool,
rx_overrun: bool,
}
#[bitbybit::bitfield(
@@ -114,8 +117,9 @@ pub struct InterruptControl {
rx_not_empty: bool,
#[bit(3, w)]
tx_full: bool,
/// Interrupt when TX FIFO level below configured threshold.
#[bit(2, w)]
tx_trig: bool,
tx_below_threshold: bool,
#[bit(1, w)]
mode_fault: bool,
/// Receiver overflow interrupt.
@@ -124,17 +128,20 @@ pub struct InterruptControl {
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
pub struct InterruptMask {
pub struct InterruptEnabled {
#[bit(6, r)]
tx_underflow: bool,
#[bit(5, r)]
rx_full: bool,
#[bit(4, r)]
rx_not_empty: bool,
/// Switches to 1 when the FIFO becomes full and then remains asserted until the FIFO falls
/// below the configured threshold level.
#[bit(3, r)]
tx_full: bool,
/// Interrupt when TX FIFO level below configured threshold.
#[bit(2, r)]
tx_trig: bool,
tx_below_threshold: bool,
#[bit(1, r)]
mode_fault: bool,
/// Receiver overflow interrupt.
@@ -206,18 +213,18 @@ pub struct DelayControl {
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Registers {
cr: Config,
config: Config,
#[mmio(PureRead, Write)]
isr: InterruptStatus,
interrupt_status: InterruptStatus,
/// Interrupt Enable Register.
#[mmio(Write)]
ier: InterruptControl,
interrupt_enable: InterruptControl,
/// Interrupt Disable Register.
#[mmio(Write)]
idr: InterruptControl,
interupt_disable: InterruptControl,
/// Interrupt Mask Register.
#[mmio(PureRead)]
imr: InterruptMask,
enabled_interrupts: InterruptEnabled,
enable: u32,
delay_control: DelayControl,
#[mmio(Write)]
+3 -3
View File
@@ -189,9 +189,9 @@ pub struct Registers {
match_value_1: [RwValue; 3],
match_value_2: [RwValue; 3],
#[mmio(Read)]
isr: [InterruptStatus; 3],
ier: [InterruptControl; 3],
event_cntrl: [EventControl; 3],
interrupt_status: [InterruptStatus; 3],
interrupt_enable: [InterruptControl; 3],
event_control: [EventControl; 3],
#[mmio(PureRead)]
event_reg: [EventCount; 3],
}
+31 -31
View File
@@ -77,25 +77,25 @@ pub struct Control {
startbrk: bool,
/// Restart receiver timeout counter.
#[bit(6, rw)]
rstto: bool,
restart_timeout: bool,
/// TX disable. If this is 1, TX is disabled, regardless of TXEN.
#[bit(5, rw)]
tx_dis: bool,
tx_disable: bool,
/// TX enable. TX will be enabled if this bit is 1 and the TXDIS is 0.
#[bit(4, rw)]
tx_en: bool,
tx_enable: bool,
/// RX disable. If this is 1, RX is disabled, regardless of RXEN.
#[bit(3, rw)]
rx_dis: bool,
rx_disable: bool,
/// RX enable. RX will be enabled if this bit is 1 and the RXDIS is 0.
#[bit(2, rw)]
rx_en: bool,
rx_enable: bool,
/// TX soft reset.
#[bit(1, rw)]
tx_rst: bool,
tx_reset: bool,
/// RX soft reset.
#[bit(0, rw)]
rx_rst: bool,
rx_reset: bool,
}
#[bitbybit::bitfield(
@@ -109,14 +109,14 @@ pub struct Mode {
#[bits(8..=9, rw)]
chmode: ChMode,
#[bits(6..=7, rw)]
nbstop: Option<Stopbits>,
stopbits: Option<Stopbits>,
#[bits(3..=5, rw)]
par: Parity,
parity: Parity,
/// Char length.
#[bits(1..=2, rw)]
chrl: CharLen,
charlen: CharLen,
#[bit(0, rw)]
clksel: ClockSelect,
clock_select: ClockSelect,
}
#[bitbybit::bitfield(
@@ -162,7 +162,7 @@ pub struct Status {
#[bit(14, r)]
tx_near_full: bool,
#[bit(13, r)]
tx_trig: Ttrig,
tx_trigger: Ttrig,
#[bit(12, r)]
flowdel: bool,
/// Transmitter state machine active.
@@ -181,7 +181,7 @@ pub struct Status {
rx_empty: bool,
/// RX FIFO trigger level was reached.
#[bit(0, r)]
rx_trg: bool,
rx_trigger: bool,
}
#[bitbybit::bitfield(
@@ -197,7 +197,7 @@ pub struct InterruptControl {
#[bit(11, w)]
tx_near_full: bool,
#[bit(10, w)]
tx_trig: bool,
tx_trigger: bool,
#[bit(9, w)]
rx_dms: bool,
/// Receiver timeout error interrupt.
@@ -218,15 +218,15 @@ pub struct InterruptControl {
#[bit(1, w)]
rx_empty: bool,
#[bit(0, w)]
rx_trg: bool,
rx_trigger: bool,
}
#[bitbybit::bitfield(u32)]
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FifoTrigger {
#[bits(0..=5, rw)]
trig: u6,
trigger: u6,
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"))]
@@ -237,7 +237,7 @@ pub struct InterruptMask {
#[bit(11, r)]
tx_near_full: bool,
#[bit(10, r)]
tx_trig: bool,
tx_trigger: bool,
#[bit(9, r)]
rx_dms: bool,
/// Receiver timeout error interrupt.
@@ -259,7 +259,7 @@ pub struct InterruptMask {
rx_empty: bool,
/// RX FIFO trigger level reached.
#[bit(0, r)]
rx_trg: bool,
rx_trigger: bool,
}
#[bitbybit::bitfield(
@@ -297,7 +297,7 @@ pub struct InterruptStatus {
rx_empty: bool,
/// RX FIFO trigger level reached.
#[bit(0, rw)]
rx_trg: bool,
rx_trigger: bool,
}
impl InterruptStatus {
@@ -315,7 +315,7 @@ impl InterruptStatus {
.with_tx_empty(false)
.with_rx_full(false)
.with_rx_empty(false)
.with_rx_trg(false)
.with_rx_trigger(false)
.build()
}
}
@@ -325,34 +325,34 @@ impl InterruptStatus {
#[repr(C)]
pub struct Registers {
/// Control Register
cr: Control,
control: Control,
/// Mode register
mr: Mode,
mode: Mode,
/// Interrupt enable register
#[mmio(Write)]
ier: InterruptControl,
interrupt_enable: InterruptControl,
/// Interrupt disable register
#[mmio(Write)]
idr: InterruptControl,
interrupt_disable: InterruptControl,
/// Interrupt mask register, showing enabled interrupts.
#[mmio(PureRead)]
imr: InterruptMask,
enabled_interrupts: InterruptMask,
/// Interrupt status register
#[mmio(PureRead, Write)]
isr: InterruptStatus,
interrupt_status: InterruptStatus,
/// Baudgen register
baudgen: Baudgen,
/// RX timeout register
rx_tout: u32,
rx_timeout: u32,
/// RX FIFO trigger level register
rx_fifo_trigger: FifoTrigger,
/// Modem control register
modem_cr: u32,
modem_control: u32,
/// Modem status register
modem_sr: u32,
modem_status: u32,
/// Channel status register
#[mmio(PureRead)]
sr: Status,
status: Status,
/// FIFO register
#[mmio(Read, Write)]
fifo: Fifo,
+1 -64
View File
@@ -2,19 +2,6 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "aarch32-cpu"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1417bbf608824a44cb2fa2ad74b5ec28c0ae4c83df62a4bd2b532bf04c241ade"
dependencies = [
"arbitrary-int 2.0.0",
"arm-targets",
"bitbybit 1.4.0",
"num_enum",
"thiserror",
]
[[package]]
name = "aho-corasick"
version = "1.1.3"
@@ -86,12 +73,6 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c858caffa49edfc4ecc45a4bec37abd3e88041a2903816f10f990b7b41abc281"
[[package]]
name = "arm-targets"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d29a37f252452446b67d5e50dee82a6ce12781218b915244bb6507c10b72812"
[[package]]
name = "bitbybit"
version = "1.4.0"
@@ -254,10 +235,6 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "mmu-table-gen"
version = "0.1.0"
dependencies = [
"zynq7000-mmu",
"zynq7000-rt",
]
[[package]]
name = "num-conv"
@@ -265,27 +242,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num_enum"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c"
dependencies = [
"num_enum_derive",
"rustversion",
]
[[package]]
name = "num_enum_derive"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "num_threads"
version = "0.1.7"
@@ -691,7 +647,7 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "zynq7000"
version = "0.2.0"
version = "0.4.0"
dependencies = [
"arbitrary-int 2.0.0",
"bitbybit 2.0.0",
@@ -711,15 +667,6 @@ dependencies = [
"thiserror",
]
[[package]]
name = "zynq7000-mmu"
version = "0.1.2"
dependencies = [
"aarch32-cpu",
"arm-targets",
"thiserror",
]
[[package]]
name = "zynq7000-ps7init-extract"
version = "0.1.0"
@@ -733,13 +680,3 @@ dependencies = [
"syn",
"zynq7000",
]
[[package]]
name = "zynq7000-rt"
version = "0.2.0"
dependencies = [
"aarch32-cpu",
"arbitrary-int 2.0.0",
"arm-targets",
"zynq7000-mmu",
]
-2
View File
@@ -4,5 +4,3 @@ version = "0.1.0"
edition = "2024"
[dependencies]
zynq7000-rt = { path = "../../firmware/zynq7000-rt", default-features = false }
zynq7000-mmu = { path = "../../firmware/zynq7000-mmu", features = ["tools"] }
+65 -2
View File
@@ -1,8 +1,71 @@
use std::fs::File;
use std::io::Write;
use std::process::Command;
use zynq7000_rt::mmu::ONE_MB;
pub use zynq7000_rt::mmu::segments::*;
//use zynq7000_rt::mmu::ONE_MB;
//pub use zynq7000_rt::mmu::segments::*;
pub const MAX_DDR_SIZE: usize = 0x4000_0000;
pub const ONE_MB: usize = 0x10_0000;
pub mod offsets {
pub const OFFSET_DDR: usize = 0;
pub const OFFSET_DDR_ALL_ACCESSIBLE: usize = 0x10_0000;
pub const OFFSET_FPGA_SLAVE_0: usize = 0x4000_0000;
pub const OFFSET_FPGA_SLAVE_1_START: usize = 0x8000_0000;
pub const OFFSET_FPGA_SLAVE_1_END: usize = 0xC000_0000;
pub const OFFSET_IO_PERIPHERALS_START: usize = 0xE000_0000;
pub const OFFSET_IO_PERIPHERALS_END: usize = 0xE030_0000;
pub const OFFSET_NAND_MEMORY: usize = 0xE100_0000;
pub const OFFSET_NOR_MEMORY: usize = 0xE200_0000;
pub const OFFSET_SRAM_MEMORY: usize = 0xE400_0000;
pub const OFFSET_SMC_MEMORIES_END: usize = 0xE600_0000;
/// 0xf8000c00 to 0xf8000fff, 0xf8010000 to 0xf88fffff and
/// 0xf8f03000 to 0xf8ffffff are reserved but due to granual size of
/// 1MB, it is not possible to define separate regions for them.
pub const OFFSET_AMBA_APB_START: usize = 0xF800_0000;
pub const OFFSET_AMBA_APB_END: usize = 0xF900_0000;
pub const OFFSET_QSPI_XIP_START: usize = 0xFC00_0000;
pub const OFFSET_QSPI_XIP_END: usize = 0xFE00_0000;
/// 0xfff00000 to 0xfffb0000 is reserved but due to granual size of
/// 1MB, it is not possible to define separate region for it
pub const OFFSET_OCM_MAPPED_HIGH_START: usize = 0xFFF0_0000;
pub const OFFSET_OCM_MAPPED_HIGH_END: u64 = 0x1_0000_0000;
}
pub mod segments {
pub use super::offsets::*;
use super::{MAX_DDR_SIZE, ONE_MB};
/// First 1 MB of DDR has special treatment, access is dependant on SCU/OCM state.
/// Refer to Zynq TRM UG585 p.106 for more details.
pub const DDR_FULL_ACCESSIBLE: usize = (MAX_DDR_SIZE - ONE_MB) / ONE_MB;
pub const FPGA_SLAVE: usize = (OFFSET_FPGA_SLAVE_1_START - OFFSET_FPGA_SLAVE_0) / ONE_MB;
pub const UNASSIGNED_0: usize =
(OFFSET_IO_PERIPHERALS_START - OFFSET_FPGA_SLAVE_1_END) / ONE_MB;
pub const IO_PERIPHS: usize =
(OFFSET_IO_PERIPHERALS_END - OFFSET_IO_PERIPHERALS_START) / ONE_MB;
pub const UNASSIGNED_1: usize = (OFFSET_NAND_MEMORY - OFFSET_IO_PERIPHERALS_END) / ONE_MB;
pub const NAND: usize = (OFFSET_NOR_MEMORY - OFFSET_NAND_MEMORY) / ONE_MB;
pub const NOR: usize = (OFFSET_SRAM_MEMORY - OFFSET_NOR_MEMORY) / ONE_MB;
pub const SRAM: usize = (OFFSET_SMC_MEMORIES_END - OFFSET_SRAM_MEMORY) / ONE_MB;
pub const SEGMENTS_UNASSIGNED_2: usize =
(OFFSET_AMBA_APB_START - OFFSET_SMC_MEMORIES_END) / ONE_MB;
pub const AMBA_APB: usize = (OFFSET_AMBA_APB_END - OFFSET_AMBA_APB_START) / ONE_MB;
pub const UNASSIGNED_3: usize = (OFFSET_QSPI_XIP_START - OFFSET_AMBA_APB_END) / ONE_MB;
pub const QSPI_XIP: usize = (OFFSET_QSPI_XIP_END - OFFSET_QSPI_XIP_START) / ONE_MB;
pub const UNASSIGNED_4: usize = (OFFSET_OCM_MAPPED_HIGH_START - OFFSET_QSPI_XIP_END) / ONE_MB;
pub const OCM_MAPPED_HIGH: usize = ((OFFSET_OCM_MAPPED_HIGH_END
- OFFSET_OCM_MAPPED_HIGH_START as u64)
/ ONE_MB as u64) as usize;
}
use segments::*;
macro_rules! write_l1_section {
($writer:expr, $offset:expr, $attr:expr) => {
+15 -6
View File
@@ -1,5 +1,8 @@
all: check build check-fmt clippy docs-zynq
# Common paths (single source of truth)
INIT_SCRIPT := justfile_directory() / "scripts/zynq7000-init.py"
GDB_CMD := justfile_directory() / "firmware/gdb.gdb"
all: check build check-fmt clippy docs-zynq
check: (check-dir "firmware") (check-dir "host")
clean: (clean-dir "firmware") (clean-dir "host")
build: build-zynq (build-dir "host")
@@ -52,13 +55,19 @@ bootgen:
bootgen -arch zynq -image boot.bif -o boot.bin -w on
echo "Generated boot.bin at zynq-boot-image/staging"
# Internal helper to start GDB after running init.
# Pass init flags (if any) via `init_args`.
[no-cd]
run binary:
# Run the initialization script. It needs to be run inside the justfile directory.
python3 {{justfile_directory()}}/scripts/zynq7000-init.py
run_generic binary init_args="":
python3 {{INIT_SCRIPT}} {{init_args}}
gdb-multiarch -q -x {{GDB_CMD}} {{binary}} -tui
# Run the GDB debugger in GUI mode.
gdb-multiarch -q -x {{justfile_directory()}}/firmware/gdb.gdb {{binary}} -tui
# Public targets
[no-cd]
run binary: (run_generic binary)
[no-cd]
run-no-reset binary: (run_generic binary "-s")
flash-nor-zedboard boot_binary:
cd {{justfile_directory()}}/firmware/zedboard-qspi-flasher && cargo build --release
+21 -18
View File
@@ -33,10 +33,11 @@ proc ps7_clock_init_data_3_0 {} {
mask_write 0XF800014C 0x00003F31 0x00000501
mask_write 0XF8000150 0x00003F33 0x00001401
mask_write 0XF8000154 0x00003F33 0x00000A03
mask_write 0XF8000158 0x00003F33 0x00000601
mask_write 0XF8000168 0x00003F31 0x00000501
mask_write 0XF8000170 0x03F03F30 0x00200500
mask_write 0XF80001C4 0x00000001 0x00000001
mask_write 0XF800012C 0x01FFCCCD 0x01FC044D
mask_write 0XF800012C 0x01FFCCCD 0x01FC444D
mwr -force 0XF8000004 0x0000767B
}
proc ps7_ddr_init_data_3_0 {} {
@@ -149,7 +150,7 @@ proc ps7_mio_init_data_3_0 {} {
mask_write 0XF8000714 0x00003FFF 0x00000702
mask_write 0XF8000718 0x00003FFF 0x00000702
mask_write 0XF800071C 0x00003FFF 0x00000600
mask_write 0XF8000720 0x00003FFF 0x00000702
mask_write 0XF8000720 0x00003FFF 0x00000700
mask_write 0XF8000724 0x00003FFF 0x00001600
mask_write 0XF8000728 0x00003FFF 0x00001600
mask_write 0XF800072C 0x00003FFF 0x00001600
@@ -188,14 +189,14 @@ proc ps7_mio_init_data_3_0 {} {
mask_write 0XF80007B0 0x00003FFF 0x00000380
mask_write 0XF80007B4 0x00003FFF 0x00000380
mask_write 0XF80007B8 0x00003FFF 0x00001200
mask_write 0XF80007BC 0x00003F01 0x00000201
mask_write 0XF80007BC 0x00003FFF 0x00000200
mask_write 0XF80007C0 0x00003FFF 0x000002E0
mask_write 0XF80007C4 0x00003FFF 0x000002E1
mask_write 0XF80007C8 0x00003FFF 0x00000200
mask_write 0XF80007CC 0x00003FFF 0x00000200
mask_write 0XF80007D0 0x00003FFF 0x00000200
mask_write 0XF80007D4 0x00003FFF 0x00000200
mask_write 0XF8000830 0x003F003F 0x002F0037
mask_write 0XF80007D0 0x00003FFF 0x00000280
mask_write 0XF80007D4 0x00003FFF 0x00000280
mask_write 0XF8000830 0x003F003F 0x00380037
mwr -force 0XF8000004 0x0000767B
}
proc ps7_peripherals_init_data_3_0 {} {
@@ -268,10 +269,11 @@ proc ps7_clock_init_data_2_0 {} {
mask_write 0XF800014C 0x00003F31 0x00000501
mask_write 0XF8000150 0x00003F33 0x00001401
mask_write 0XF8000154 0x00003F33 0x00000A03
mask_write 0XF8000158 0x00003F33 0x00000601
mask_write 0XF8000168 0x00003F31 0x00000501
mask_write 0XF8000170 0x03F03F30 0x00200500
mask_write 0XF80001C4 0x00000001 0x00000001
mask_write 0XF800012C 0x01FFCCCD 0x01FC044D
mask_write 0XF800012C 0x01FFCCCD 0x01FC444D
mwr -force 0XF8000004 0x0000767B
}
proc ps7_ddr_init_data_2_0 {} {
@@ -385,7 +387,7 @@ proc ps7_mio_init_data_2_0 {} {
mask_write 0XF8000714 0x00003FFF 0x00000702
mask_write 0XF8000718 0x00003FFF 0x00000702
mask_write 0XF800071C 0x00003FFF 0x00000600
mask_write 0XF8000720 0x00003FFF 0x00000702
mask_write 0XF8000720 0x00003FFF 0x00000700
mask_write 0XF8000724 0x00003FFF 0x00001600
mask_write 0XF8000728 0x00003FFF 0x00001600
mask_write 0XF800072C 0x00003FFF 0x00001600
@@ -424,14 +426,14 @@ proc ps7_mio_init_data_2_0 {} {
mask_write 0XF80007B0 0x00003FFF 0x00000380
mask_write 0XF80007B4 0x00003FFF 0x00000380
mask_write 0XF80007B8 0x00003FFF 0x00001200
mask_write 0XF80007BC 0x00003F01 0x00000201
mask_write 0XF80007BC 0x00003FFF 0x00000200
mask_write 0XF80007C0 0x00003FFF 0x000002E0
mask_write 0XF80007C4 0x00003FFF 0x000002E1
mask_write 0XF80007C8 0x00003FFF 0x00000200
mask_write 0XF80007CC 0x00003FFF 0x00000200
mask_write 0XF80007D0 0x00003FFF 0x00000200
mask_write 0XF80007D4 0x00003FFF 0x00000200
mask_write 0XF8000830 0x003F003F 0x002F0037
mask_write 0XF80007D0 0x00003FFF 0x00000280
mask_write 0XF80007D4 0x00003FFF 0x00000280
mask_write 0XF8000830 0x003F003F 0x00380037
mwr -force 0XF8000004 0x0000767B
}
proc ps7_peripherals_init_data_2_0 {} {
@@ -504,10 +506,11 @@ proc ps7_clock_init_data_1_0 {} {
mask_write 0XF800014C 0x00003F31 0x00000501
mask_write 0XF8000150 0x00003F33 0x00001401
mask_write 0XF8000154 0x00003F33 0x00000A03
mask_write 0XF8000158 0x00003F33 0x00000601
mask_write 0XF8000168 0x00003F31 0x00000501
mask_write 0XF8000170 0x03F03F30 0x00200500
mask_write 0XF80001C4 0x00000001 0x00000001
mask_write 0XF800012C 0x01FFCCCD 0x01FC044D
mask_write 0XF800012C 0x01FFCCCD 0x01FC444D
mwr -force 0XF8000004 0x0000767B
}
proc ps7_ddr_init_data_1_0 {} {
@@ -619,7 +622,7 @@ proc ps7_mio_init_data_1_0 {} {
mask_write 0XF8000714 0x00003FFF 0x00000702
mask_write 0XF8000718 0x00003FFF 0x00000702
mask_write 0XF800071C 0x00003FFF 0x00000600
mask_write 0XF8000720 0x00003FFF 0x00000702
mask_write 0XF8000720 0x00003FFF 0x00000700
mask_write 0XF8000724 0x00003FFF 0x00001600
mask_write 0XF8000728 0x00003FFF 0x00001600
mask_write 0XF800072C 0x00003FFF 0x00001600
@@ -658,14 +661,14 @@ proc ps7_mio_init_data_1_0 {} {
mask_write 0XF80007B0 0x00003FFF 0x00000380
mask_write 0XF80007B4 0x00003FFF 0x00000380
mask_write 0XF80007B8 0x00003FFF 0x00001200
mask_write 0XF80007BC 0x00003F01 0x00000201
mask_write 0XF80007BC 0x00003FFF 0x00000200
mask_write 0XF80007C0 0x00003FFF 0x000002E0
mask_write 0XF80007C4 0x00003FFF 0x000002E1
mask_write 0XF80007C8 0x00003FFF 0x00000200
mask_write 0XF80007CC 0x00003FFF 0x00000200
mask_write 0XF80007D0 0x00003FFF 0x00000200
mask_write 0XF80007D4 0x00003FFF 0x00000200
mask_write 0XF8000830 0x003F003F 0x002F0037
mask_write 0XF80007D0 0x00003FFF 0x00000280
mask_write 0XF80007D4 0x00003FFF 0x00000280
mask_write 0XF8000830 0x003F003F 0x00380037
mwr -force 0XF8000004 0x0000767B
}
proc ps7_peripherals_init_data_1_0 {} {
+11 -7
View File
@@ -13,13 +13,15 @@ set bitstream ""
proc usage {} {
puts "Usage: xsct xsct-helper.tcl <init.tcl> \[-a|--app app.elf\] \[-b|--bit design.bit]"
puts "Options:"
puts " -a, --app Path to application ELF to download"
puts " -b, --bit Path to FPGA bitstream (.bit) to program"
puts " -h, --help Show this help"
puts " -a, --app Path to application ELF to download"
puts " -b, --bit Path to FPGA bitstream (.bit) to program"
puts " -s, --skip-reset Skip reset"
puts " -h, --help Show this help"
}
# Compact, robust parser
set expecting ""
set skip_reset 0
set endopts 0
foreach arg $argv {
# If previous option expects a value, take this arg
@@ -34,6 +36,7 @@ foreach arg $argv {
if {$arg eq "--"} { set endopts 1; continue }
if {$arg eq "-h" || $arg eq "--help"} { usage; exit 0 }
if {$arg eq "-a" || $arg eq "--app"} { set expecting app; continue }
if {$arg eq "-s" || $arg eq "--skip-reset"} { set skip_reset 1; continue }
if {$arg eq "-b" || $arg eq "--bit"} { set expecting bitstream; continue }
puts "error: unknown option: $arg"; usage; exit 1
}
@@ -100,10 +103,11 @@ target $apu_device_num
# Resetting the target involved problems when an image is stored on the flash.
# It has turned out that it is not essential to reset the system before loading
# the software components into the device.
puts "Reset target"
# TODO: Make the reset optional/configurable via input argument.
# Reset the target
rst
if {!$skip_reset} {
puts "Reset target"
# Reset the target
rst
}
# Check if bitstream is set and the file exists before programming FPGA
if {$bitstream eq ""} {
+8
View File
@@ -40,6 +40,12 @@ def main():
action="store_true",
help="No bitstream flashing for initialization with SDT.",
)
parser.add_argument(
"-s",
"--skip-reset",
action="store_true",
help="Skip device reset",
)
parser.add_argument("-a", "--app", dest="app", help="Path to the app to program")
default_ip = os.getenv("HW_SERVER_IP")
if not default_ip:
@@ -141,6 +147,8 @@ def main():
if args.app:
cmd_list.append("--app")
cmd_list.append(args.app)
if args.skip_reset:
cmd_list.append("-s")
# Join safely for shell execution
xsct_cmd = shlex.join(cmd_list)
+6
View File
@@ -39,6 +39,12 @@ vivado zedboard-rust.xpr
You can perform all the steps specified in the Vivado GUI as well using `Execute TCL script` and
`Load Project`.
# Updating the project
If you add custom RTL code, you might have to edit the `zedboard-rust.tcl` project file and
add the source files there. This file was created using the `write_project_tcl` but was
optimized, so it might be easier to manually update the file.
# Generating the SDT folder from a hardware description
You can generate a hardware description by building the block design by using `Generate Bitstream`
+250 -136
View File
@@ -140,9 +140,10 @@ xilinx.com:ip:processing_system7:5.5\
xilinx.com:ip:axi_uartlite:2.0\
xilinx.com:ip:proc_sys_reset:5.0\
xilinx.com:ip:axi_uart16550:2.0\
xilinx.com:ip:xlconcat:2.1\
xilinx.com:ip:xlslice:1.0\
xilinx.com:ip:xlconstant:1.1\
xilinx.com:inline_hdl:ilslice:1.0\
xilinx.com:inline_hdl:ilconcat:1.0\
xilinx.com:inline_hdl:ilconstant:1.0\
xilinx.com:ip:smartconnect:1.0\
"
set list_ips_missing ""
@@ -243,6 +244,12 @@ proc create_root_design { parentCell } {
set UART_txd [ create_bd_port -dir O UART_txd ]
set UART_rxd [ create_bd_port -dir I UART_rxd ]
set TTC0_WAVEOUT [ create_bd_port -dir O TTC0_WAVEOUT ]
set OLED_SCLK [ create_bd_port -dir O OLED_SCLK ]
set OLED_SDIN [ create_bd_port -dir O OLED_SDIN ]
set OLED_DC [ create_bd_port -dir O -from 0 -to 0 OLED_DC ]
set OLED_RESET [ create_bd_port -dir O -from 0 -to 0 OLED_RESET ]
set OLED_VDD [ create_bd_port -dir O -from 0 -to 0 OLED_VDD ]
set OLED_VBAT [ create_bd_port -dir O -from 0 -to 0 OLED_VBAT ]
# Create instance: processing_system7_0, and set properties
set processing_system7_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7:5.5 processing_system7_0 ]
@@ -260,7 +267,7 @@ proc create_root_design { parentCell } {
CONFIG.PCW_ACT_QSPI_PERIPHERAL_FREQMHZ {200.000000} \
CONFIG.PCW_ACT_SDIO_PERIPHERAL_FREQMHZ {50.000000} \
CONFIG.PCW_ACT_SMC_PERIPHERAL_FREQMHZ {10.000000} \
CONFIG.PCW_ACT_SPI_PERIPHERAL_FREQMHZ {10.000000} \
CONFIG.PCW_ACT_SPI_PERIPHERAL_FREQMHZ {166.666672} \
CONFIG.PCW_ACT_TPIU_PERIPHERAL_FREQMHZ {200.000000} \
CONFIG.PCW_ACT_TTC0_CLK0_PERIPHERAL_FREQMHZ {108.333336} \
CONFIG.PCW_ACT_TTC0_CLK1_PERIPHERAL_FREQMHZ {108.333336} \
@@ -280,21 +287,27 @@ proc create_root_design { parentCell } {
CONFIG.PCW_DDR_RAM_HIGHADDR {0x1FFFFFFF} \
CONFIG.PCW_ENET0_ENET0_IO {MIO 16 .. 27} \
CONFIG.PCW_ENET0_GRP_MDIO_ENABLE {1} \
CONFIG.PCW_ENET0_GRP_MDIO_IO {EMIO} \
CONFIG.PCW_ENET0_GRP_MDIO_IO {MIO 52 .. 53} \
CONFIG.PCW_ENET0_PERIPHERAL_CLKSRC {IO PLL} \
CONFIG.PCW_ENET0_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_ENET0_PERIPHERAL_FREQMHZ {1000 Mbps} \
CONFIG.PCW_ENET0_RESET_ENABLE {0} \
CONFIG.PCW_ENET_RESET_ENABLE {1} \
CONFIG.PCW_ENET_RESET_SELECT {Share reset pin} \
CONFIG.PCW_EN_EMIO_ENET0 {0} \
CONFIG.PCW_EN_EMIO_GPIO {1} \
CONFIG.PCW_EN_EMIO_SPI0 {1} \
CONFIG.PCW_EN_EMIO_SPI1 {0} \
CONFIG.PCW_EN_EMIO_TTC0 {1} \
CONFIG.PCW_EN_EMIO_TTC1 {0} \
CONFIG.PCW_EN_EMIO_UART0 {1} \
CONFIG.PCW_EN_EMIO_WP_SDIO0 {1} \
CONFIG.PCW_EN_EMIO_WP_SDIO0 {0} \
CONFIG.PCW_EN_ENET0 {1} \
CONFIG.PCW_EN_GPIO {1} \
CONFIG.PCW_EN_QSPI {1} \
CONFIG.PCW_EN_SDIO0 {1} \
CONFIG.PCW_EN_SPI0 {1} \
CONFIG.PCW_EN_SPI1 {0} \
CONFIG.PCW_EN_TTC0 {1} \
CONFIG.PCW_EN_TTC1 {0} \
CONFIG.PCW_EN_UART0 {1} \
@@ -465,9 +478,9 @@ proc create_root_design { parentCell } {
CONFIG.PCW_MIO_9_PULLUP {enabled} \
CONFIG.PCW_MIO_9_SLEW {slow} \
CONFIG.PCW_MIO_TREE_PERIPHERALS {GPIO#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#Quad SPI Flash#GPIO#Quad SPI Flash#GPIO#GPIO#GPIO#GPIO#GPIO#GPIO#GPIO#Enet 0#Enet 0#Enet\
0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#SD 0#SD 0#SD 0#SD 0#SD 0#SD 0#USB Reset#SD 0#UART 1#UART 1#GPIO#GPIO#GPIO#GPIO}\
\
CONFIG.PCW_MIO_TREE_SIGNALS {gpio[0]#qspi0_ss_b#qspi0_io[0]#qspi0_io[1]#qspi0_io[2]#qspi0_io[3]/HOLD_B#qspi0_sclk#gpio[7]#qspi_fbclk#gpio[9]#gpio[10]#gpio[11]#gpio[12]#gpio[13]#gpio[14]#gpio[15]#tx_clk#txd[0]#txd[1]#txd[2]#txd[3]#tx_ctl#rx_clk#rxd[0]#rxd[1]#rxd[2]#rxd[3]#rx_ctl#data[4]#dir#stp#nxt#data[0]#data[1]#data[2]#data[3]#clk#data[5]#data[6]#data[7]#clk#cmd#data[0]#data[1]#data[2]#data[3]#reset#cd#tx#rx#gpio[50]#gpio[51]#gpio[52]#gpio[53]}\
0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#Enet 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#USB 0#SD 0#SD 0#SD 0#SD 0#SD 0#SD 0#USB Reset#GPIO#UART 1#UART 1#GPIO#GPIO#Enet\
0#Enet 0} \
CONFIG.PCW_MIO_TREE_SIGNALS {gpio[0]#qspi0_ss_b#qspi0_io[0]#qspi0_io[1]#qspi0_io[2]#qspi0_io[3]/HOLD_B#qspi0_sclk#gpio[7]#qspi_fbclk#gpio[9]#gpio[10]#gpio[11]#gpio[12]#gpio[13]#gpio[14]#gpio[15]#tx_clk#txd[0]#txd[1]#txd[2]#txd[3]#tx_ctl#rx_clk#rxd[0]#rxd[1]#rxd[2]#rxd[3]#rx_ctl#data[4]#dir#stp#nxt#data[0]#data[1]#data[2]#data[3]#clk#data[5]#data[6]#data[7]#clk#cmd#data[0]#data[1]#data[2]#data[3]#reset#gpio[47]#tx#rx#gpio[50]#gpio[51]#mdc#mdio}\
\
CONFIG.PCW_PRESET_BANK1_VOLTAGE {LVCMOS 1.8V} \
CONFIG.PCW_QSPI_GRP_FBCLK_ENABLE {1} \
@@ -479,16 +492,19 @@ proc create_root_design { parentCell } {
CONFIG.PCW_QSPI_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_QSPI_PERIPHERAL_FREQMHZ {200} \
CONFIG.PCW_QSPI_QSPI_IO {MIO 1 .. 6} \
CONFIG.PCW_SD0_GRP_CD_ENABLE {1} \
CONFIG.PCW_SD0_GRP_CD_IO {MIO 47} \
CONFIG.PCW_SD0_GRP_CD_ENABLE {0} \
CONFIG.PCW_SD0_GRP_POW_ENABLE {0} \
CONFIG.PCW_SD0_GRP_WP_ENABLE {1} \
CONFIG.PCW_SD0_GRP_WP_IO {EMIO} \
CONFIG.PCW_SD0_GRP_WP_ENABLE {0} \
CONFIG.PCW_SD0_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_SD0_SD0_IO {MIO 40 .. 45} \
CONFIG.PCW_SDIO_PERIPHERAL_FREQMHZ {50} \
CONFIG.PCW_SDIO_PERIPHERAL_VALID {1} \
CONFIG.PCW_SINGLE_QSPI_DATA_MODE {x4} \
CONFIG.PCW_SPI0_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_SPI0_SPI0_IO {EMIO} \
CONFIG.PCW_SPI1_PERIPHERAL_ENABLE {0} \
CONFIG.PCW_SPI_PERIPHERAL_FREQMHZ {166.666666} \
CONFIG.PCW_SPI_PERIPHERAL_VALID {1} \
CONFIG.PCW_TTC0_PERIPHERAL_ENABLE {1} \
CONFIG.PCW_TTC0_TTC0_IO {EMIO} \
CONFIG.PCW_TTC1_PERIPHERAL_ENABLE {0} \
@@ -502,14 +518,14 @@ proc create_root_design { parentCell } {
CONFIG.PCW_UART_PERIPHERAL_FREQMHZ {100} \
CONFIG.PCW_UART_PERIPHERAL_VALID {1} \
CONFIG.PCW_UIPARAM_ACT_DDR_FREQ_MHZ {533.333374} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY0 {0.176} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY1 {0.159} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY2 {0.162} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY3 {0.187} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_0 {-0.073} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_1 {-0.034} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_2 {-0.03} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_3 {-0.082} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY0 {0.410} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY1 {0.411} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY2 {0.341} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY3 {0.358} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_0 {0.025} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_1 {0.028} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_2 {-0.009} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_3 {-0.061} \
CONFIG.PCW_UIPARAM_DDR_FREQ_MHZ {525} \
CONFIG.PCW_UIPARAM_DDR_PARTNO {MT41K128M16 JT-125} \
CONFIG.PCW_UIPARAM_DDR_TRAIN_DATA_EYE {1} \
@@ -534,11 +550,6 @@ proc create_root_design { parentCell } {
] $axi_uartlite_0
# Create instance: ps7_0_axi_periph, and set properties
set ps7_0_axi_periph [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_interconnect:2.1 ps7_0_axi_periph ]
set_property CONFIG.NUM_MI {2} $ps7_0_axi_periph
# Create instance: rst_ps7_0_100M, and set properties
set rst_ps7_0_100M [ create_bd_cell -type ip -vlnv xilinx.com:ip:proc_sys_reset:5.0 rst_ps7_0_100M ]
@@ -550,71 +561,6 @@ proc create_root_design { parentCell } {
] $axi_uart16550_0
# Create instance: IRQ_F2P, and set properties
set IRQ_F2P [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconcat:2.1 IRQ_F2P ]
# Create instance: LEDS, and set properties
set LEDS [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlslice:1.0 LEDS ]
set_property -dict [list \
CONFIG.DIN_FROM {7} \
CONFIG.DIN_WIDTH {16} \
] $LEDS
# Create instance: EMIO_O_0, and set properties
set EMIO_O_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlslice:1.0 EMIO_O_0 ]
set_property -dict [list \
CONFIG.DIN_FROM {15} \
CONFIG.DIN_WIDTH {64} \
] $EMIO_O_0
# Create instance: EMIO_O_1, and set properties
set EMIO_O_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlslice:1.0 EMIO_O_1 ]
set_property -dict [list \
CONFIG.DIN_FROM {47} \
CONFIG.DIN_TO {32} \
CONFIG.DIN_WIDTH {64} \
] $EMIO_O_1
# Create instance: EMIO_I, and set properties
set EMIO_I [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconcat:2.1 EMIO_I ]
set_property -dict [list \
CONFIG.IN0_WIDTH {16} \
CONFIG.IN1_WIDTH {16} \
CONFIG.IN2_WIDTH {16} \
CONFIG.IN3_WIDTH {16} \
CONFIG.NUM_PORTS {4} \
] $EMIO_I
# Create instance: EMIO_I_0, and set properties
set EMIO_I_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconcat:2.1 EMIO_I_0 ]
set_property -dict [list \
CONFIG.IN0_WIDTH {8} \
CONFIG.IN1_WIDTH {5} \
CONFIG.IN2_WIDTH {3} \
CONFIG.NUM_PORTS {3} \
] $EMIO_I_0
# Create instance: xlconstant_0, and set properties
set xlconstant_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 xlconstant_0 ]
set_property -dict [list \
CONFIG.CONST_VAL {0} \
CONFIG.CONST_WIDTH {3} \
] $xlconstant_0
# Create instance: EMIO_I_1, and set properties
set EMIO_I_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 EMIO_I_1 ]
set_property -dict [list \
CONFIG.CONST_VAL {0} \
CONFIG.CONST_WIDTH {16} \
] $EMIO_I_1
# Create instance: uart_mux_0, and set properties
set block_name uart_mux
set block_cell_name uart_mux_0
@@ -626,8 +572,45 @@ proc create_root_design { parentCell } {
return 1
}
# Create instance: EMIO_O_0, and set properties
set EMIO_O_0 [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilslice:1.0 EMIO_O_0 ]
set_property -dict [list \
CONFIG.DIN_FROM {15} \
CONFIG.DIN_TO {0} \
CONFIG.DIN_WIDTH {64} \
] $EMIO_O_0
# Create instance: EMIO_O_1, and set properties
set EMIO_O_1 [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilslice:1.0 EMIO_O_1 ]
set_property -dict [list \
CONFIG.DIN_FROM {47} \
CONFIG.DIN_TO {32} \
CONFIG.DIN_WIDTH {64} \
] $EMIO_O_1
# Create instance: EMIO_I, and set properties
set EMIO_I [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilconcat:1.0 EMIO_I ]
set_property -dict [list \
CONFIG.IN0_WIDTH {16} \
CONFIG.IN1_WIDTH {16} \
CONFIG.IN2_WIDTH {16} \
CONFIG.IN3_WIDTH {16} \
CONFIG.NUM_PORTS {4} \
] $EMIO_I
# Create instance: EMIO_I_1, and set properties
set EMIO_I_1 [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilconstant:1.0 EMIO_I_1 ]
set_property -dict [list \
CONFIG.CONST_VAL {0} \
CONFIG.CONST_WIDTH {16} \
] $EMIO_I_1
# Create instance: UART_MUX, and set properties
set UART_MUX [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlslice:1.0 UART_MUX ]
set UART_MUX [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilslice:1.0 UART_MUX ]
set_property -dict [list \
CONFIG.DIN_FROM {10} \
CONFIG.DIN_TO {8} \
@@ -635,56 +618,187 @@ proc create_root_design { parentCell } {
] $UART_MUX
# Create instance: xlconstant_1, and set properties
set xlconstant_1 [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 xlconstant_1 ]
# Create instance: LEDS, and set properties
set LEDS [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilslice:1.0 LEDS ]
set_property -dict [list \
CONFIG.DIN_FROM {7} \
CONFIG.DIN_WIDTH {16} \
] $LEDS
# Create instance: EMIO_I_0, and set properties
set EMIO_I_0 [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilconcat:1.0 EMIO_I_0 ]
set_property -dict [list \
CONFIG.IN0_WIDTH {8} \
CONFIG.IN1_WIDTH {5} \
CONFIG.IN2_WIDTH {3} \
CONFIG.NUM_PORTS {3} \
] $EMIO_I_0
# Create instance: ilconstant_0, and set properties
set ilconstant_0 [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilconstant:1.0 ilconstant_0 ]
set_property -dict [list \
CONFIG.CONST_VAL {0} \
CONFIG.CONST_WIDTH {3} \
] $ilconstant_0
# Create instance: ilconstant_1, and set properties
set ilconstant_1 [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilconstant:1.0 ilconstant_1 ]
# Create instance: smartconnect_0, and set properties
set smartconnect_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:smartconnect:1.0 smartconnect_0 ]
set_property -dict [list \
CONFIG.NUM_MI {2} \
CONFIG.NUM_SI {1} \
] $smartconnect_0
# Create instance: IRQ_F2P, and set properties
set IRQ_F2P [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilconcat:1.0 IRQ_F2P ]
# Create instance: OLED_DC, and set properties
set OLED_DC [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilslice:1.0 OLED_DC ]
set_property -dict [list \
CONFIG.DIN_FROM {11} \
CONFIG.DIN_TO {11} \
CONFIG.DIN_WIDTH {16} \
] $OLED_DC
# Create instance: OLED_RESET, and set properties
set OLED_RESET [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilslice:1.0 OLED_RESET ]
set_property -dict [list \
CONFIG.DIN_FROM {12} \
CONFIG.DIN_TO {12} \
CONFIG.DIN_WIDTH {16} \
] $OLED_RESET
# Create instance: OLED_VDD, and set properties
set OLED_VDD [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilslice:1.0 OLED_VDD ]
set_property -dict [list \
CONFIG.DIN_FROM {13} \
CONFIG.DIN_TO {13} \
CONFIG.DIN_WIDTH {16} \
] $OLED_VDD
# Create instance: OLED_VBAT, and set properties
set OLED_VBAT [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilslice:1.0 OLED_VBAT ]
set_property -dict [list \
CONFIG.DIN_FROM {14} \
CONFIG.DIN_TO {14} \
CONFIG.DIN_WIDTH {16} \
] $OLED_VBAT
# Create instance: ilconstant_2, and set properties
set ilconstant_2 [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilconstant:1.0 ilconstant_2 ]
# Create instance: ilconstant_3, and set properties
set ilconstant_3 [ create_bd_cell -type inline_hdl -vlnv xilinx.com:inline_hdl:ilconstant:1.0 ilconstant_3 ]
set_property CONFIG.CONST_VAL {0} $ilconstant_3
# Create interface connections
connect_bd_intf_net -intf_net processing_system7_0_DDR [get_bd_intf_ports DDR] [get_bd_intf_pins processing_system7_0/DDR]
connect_bd_intf_net -intf_net processing_system7_0_FIXED_IO [get_bd_intf_ports FIXED_IO] [get_bd_intf_pins processing_system7_0/FIXED_IO]
connect_bd_intf_net -intf_net processing_system7_0_M_AXI_GP0 [get_bd_intf_pins processing_system7_0/M_AXI_GP0] [get_bd_intf_pins ps7_0_axi_periph/S00_AXI]
connect_bd_intf_net -intf_net ps7_0_axi_periph_M00_AXI [get_bd_intf_pins ps7_0_axi_periph/M00_AXI] [get_bd_intf_pins axi_uartlite_0/S_AXI]
connect_bd_intf_net -intf_net ps7_0_axi_periph_M01_AXI [get_bd_intf_pins ps7_0_axi_periph/M01_AXI] [get_bd_intf_pins axi_uart16550_0/S_AXI]
connect_bd_intf_net -intf_net processing_system7_0_M_AXI_GP0 [get_bd_intf_pins processing_system7_0/M_AXI_GP0] [get_bd_intf_pins smartconnect_0/S00_AXI]
connect_bd_intf_net -intf_net smartconnect_0_M00_AXI [get_bd_intf_pins smartconnect_0/M00_AXI] [get_bd_intf_pins axi_uartlite_0/S_AXI]
connect_bd_intf_net -intf_net smartconnect_0_M01_AXI [get_bd_intf_pins smartconnect_0/M01_AXI] [get_bd_intf_pins axi_uart16550_0/S_AXI]
# Create port connections
connect_bd_net -net EMIO_O_1_Dout [get_bd_pins EMIO_O_1/Dout] [get_bd_pins EMIO_I/In2]
connect_bd_net -net In0_0_1 [get_bd_ports SWITCHES] [get_bd_pins EMIO_I_0/In0]
connect_bd_net -net In1_0_1 [get_bd_ports BTTNS] [get_bd_pins EMIO_I_0/In1]
connect_bd_net -net axi_uart16550_0_ip2intc_irpt [get_bd_pins axi_uart16550_0/ip2intc_irpt] [get_bd_pins IRQ_F2P/In1]
connect_bd_net -net axi_uart16550_0_sout [get_bd_pins axi_uart16550_0/sout] [get_bd_pins uart_mux_0/uart_2_tx]
connect_bd_net -net axi_uartlite_0_interrupt [get_bd_pins axi_uartlite_0/interrupt] [get_bd_pins IRQ_F2P/In0]
connect_bd_net -net axi_uartlite_0_tx [get_bd_pins axi_uartlite_0/tx] [get_bd_pins uart_mux_0/uart_1_tx]
connect_bd_net -net processing_system7_0_FCLK_CLK0 [get_bd_pins processing_system7_0/FCLK_CLK0] [get_bd_pins processing_system7_0/M_AXI_GP0_ACLK] [get_bd_pins ps7_0_axi_periph/S00_ACLK] [get_bd_pins rst_ps7_0_100M/slowest_sync_clk] [get_bd_pins axi_uartlite_0/s_axi_aclk] [get_bd_pins ps7_0_axi_periph/M00_ACLK] [get_bd_pins ps7_0_axi_periph/ACLK] [get_bd_pins axi_uart16550_0/s_axi_aclk] [get_bd_pins ps7_0_axi_periph/M01_ACLK] [get_bd_pins uart_mux_0/sys_clk]
connect_bd_net -net processing_system7_0_FCLK_RESET0_N [get_bd_pins processing_system7_0/FCLK_RESET0_N] [get_bd_pins rst_ps7_0_100M/ext_reset_in]
connect_bd_net -net processing_system7_0_GPIO_O [get_bd_pins processing_system7_0/GPIO_O] [get_bd_pins EMIO_O_0/Din] [get_bd_pins EMIO_O_1/Din]
connect_bd_net -net processing_system7_0_TTC0_WAVE0_OUT [get_bd_pins processing_system7_0/TTC0_WAVE0_OUT] [get_bd_ports TTC0_WAVEOUT]
connect_bd_net -net processing_system7_0_UART0_TX [get_bd_pins processing_system7_0/UART0_TX] [get_bd_pins uart_mux_0/uart_0_tx]
connect_bd_net -net rst_ps7_0_100M_peripheral_aresetn [get_bd_pins rst_ps7_0_100M/peripheral_aresetn] [get_bd_pins ps7_0_axi_periph/S00_ARESETN] [get_bd_pins axi_uartlite_0/s_axi_aresetn] [get_bd_pins ps7_0_axi_periph/M00_ARESETN] [get_bd_pins ps7_0_axi_periph/ARESETN] [get_bd_pins axi_uart16550_0/s_axi_aresetn] [get_bd_pins ps7_0_axi_periph/M01_ARESETN]
connect_bd_net -net rx_in_0_1 [get_bd_ports UART_rxd] [get_bd_pins uart_mux_0/rx_in]
connect_bd_net -net uart_mux_0_tx_out [get_bd_pins uart_mux_0/tx_out] [get_bd_ports UART_txd]
connect_bd_net -net uart_mux_0_uart_0_rx [get_bd_pins uart_mux_0/uart_0_rx] [get_bd_pins processing_system7_0/UART0_RX]
connect_bd_net -net uart_mux_0_uart_1_rx [get_bd_pins uart_mux_0/uart_1_rx] [get_bd_pins axi_uartlite_0/rx]
connect_bd_net -net uart_mux_0_uart_2_rx [get_bd_pins uart_mux_0/uart_2_rx] [get_bd_pins axi_uart16550_0/sin]
connect_bd_net -net xlconcat_0_dout [get_bd_pins IRQ_F2P/dout] [get_bd_pins processing_system7_0/IRQ_F2P]
connect_bd_net -net xlconcat_1_dout [get_bd_pins EMIO_I/dout] [get_bd_pins processing_system7_0/GPIO_I]
connect_bd_net -net xlconcat_1_dout1 [get_bd_pins EMIO_I_0/dout] [get_bd_pins EMIO_I/In1]
connect_bd_net -net xlconstant_0_dout [get_bd_pins xlconstant_0/dout] [get_bd_pins EMIO_I_0/In2]
connect_bd_net -net xlconstant_1_dout [get_bd_pins EMIO_I_1/dout] [get_bd_pins EMIO_I/In3]
connect_bd_net -net xlconstant_1_dout1 [get_bd_pins xlconstant_1/dout] [get_bd_pins axi_uart16550_0/rin] [get_bd_pins axi_uart16550_0/dsrn] [get_bd_pins axi_uart16550_0/ctsn] [get_bd_pins axi_uart16550_0/dcdn]
connect_bd_net -net xlslice_0_Dout [get_bd_pins LEDS/Dout] [get_bd_ports LEDS]
connect_bd_net -net xlslice_0_Dout1 [get_bd_pins UART_MUX/Dout] [get_bd_pins uart_mux_0/sel]
connect_bd_net -net xlslice_1_Dout [get_bd_pins EMIO_O_0/Dout] [get_bd_pins LEDS/Din] [get_bd_pins EMIO_I/In0] [get_bd_pins UART_MUX/Din]
# Set DDR properties specified in the datasheet.
set_property -dict [list \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY0 {0.410} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY1 {0.411} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY2 {0.341} \
CONFIG.PCW_UIPARAM_DDR_BOARD_DELAY3 {0.358} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_0 {0.025} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_1 {0.028} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_2 {-0.009} \
CONFIG.PCW_UIPARAM_DDR_DQS_TO_CLK_DELAY_3 {-0.061} \
] [get_bd_cells processing_system7_0]
connect_bd_net -net BTTNS_1 [get_bd_ports BTTNS] \
[get_bd_pins EMIO_I_0/In1]
connect_bd_net -net EMIO_I_1_dout [get_bd_pins EMIO_I_1/dout] \
[get_bd_pins EMIO_I/In3]
connect_bd_net -net EMIO_O_1_Dout [get_bd_pins EMIO_O_1/Dout] \
[get_bd_pins EMIO_I/In2]
connect_bd_net -net OLED_RESET_Dout [get_bd_pins OLED_RESET/Dout] \
[get_bd_ports OLED_RESET]
connect_bd_net -net OLED_VBAT_Dout [get_bd_pins OLED_VBAT/Dout] \
[get_bd_ports OLED_VBAT]
connect_bd_net -net OLED_VDD_Dout [get_bd_pins OLED_VDD/Dout] \
[get_bd_ports OLED_VDD]
connect_bd_net -net SWITCHES_1 [get_bd_ports SWITCHES] \
[get_bd_pins EMIO_I_0/In0]
connect_bd_net -net axi_uart16550_0_ip2intc_irpt [get_bd_pins axi_uart16550_0/ip2intc_irpt] \
[get_bd_pins IRQ_F2P/In1]
connect_bd_net -net axi_uart16550_0_sout [get_bd_pins axi_uart16550_0/sout] \
[get_bd_pins uart_mux_0/uart_2_tx]
connect_bd_net -net axi_uartlite_0_interrupt [get_bd_pins axi_uartlite_0/interrupt] \
[get_bd_pins IRQ_F2P/In0]
connect_bd_net -net axi_uartlite_0_tx [get_bd_pins axi_uartlite_0/tx] \
[get_bd_pins uart_mux_0/uart_1_tx]
connect_bd_net -net ilconcat_0_dout [get_bd_pins EMIO_I/dout] \
[get_bd_pins processing_system7_0/GPIO_I]
connect_bd_net -net ilconcat_0_dout1 [get_bd_pins EMIO_I_0/dout] \
[get_bd_pins EMIO_I/In1]
connect_bd_net -net ilconcat_0_dout2 [get_bd_pins IRQ_F2P/dout] \
[get_bd_pins processing_system7_0/IRQ_F2P]
connect_bd_net -net ilconstant_0_dout [get_bd_pins ilconstant_0/dout] \
[get_bd_pins EMIO_I_0/In2]
connect_bd_net -net ilconstant_2_dout [get_bd_pins ilconstant_2/dout] \
[get_bd_pins processing_system7_0/SPI0_MISO_I] \
[get_bd_pins processing_system7_0/SPI0_SS_I]
connect_bd_net -net ilconstant_3_dout [get_bd_pins ilconstant_3/dout] \
[get_bd_pins processing_system7_0/SPI0_MOSI_I] \
[get_bd_pins processing_system7_0/SPI0_SCLK_I]
connect_bd_net -net ilslice_0_Dout [get_bd_pins UART_MUX/Dout] \
[get_bd_pins uart_mux_0/sel]
connect_bd_net -net ilslice_0_Dout1 [get_bd_pins LEDS/Dout] \
[get_bd_ports LEDS]
connect_bd_net -net ilslice_0_Dout2 [get_bd_pins OLED_DC/Dout] \
[get_bd_ports OLED_DC]
connect_bd_net -net processing_system7_0_FCLK_CLK0 [get_bd_pins processing_system7_0/FCLK_CLK0] \
[get_bd_pins processing_system7_0/M_AXI_GP0_ACLK] \
[get_bd_pins rst_ps7_0_100M/slowest_sync_clk] \
[get_bd_pins axi_uartlite_0/s_axi_aclk] \
[get_bd_pins axi_uart16550_0/s_axi_aclk] \
[get_bd_pins uart_mux_0/sys_clk] \
[get_bd_pins smartconnect_0/aclk]
connect_bd_net -net processing_system7_0_FCLK_RESET0_N [get_bd_pins processing_system7_0/FCLK_RESET0_N] \
[get_bd_pins rst_ps7_0_100M/ext_reset_in]
connect_bd_net -net processing_system7_0_GPIO_O [get_bd_pins processing_system7_0/GPIO_O] \
[get_bd_pins EMIO_O_0/Din] \
[get_bd_pins EMIO_O_1/Din]
connect_bd_net -net processing_system7_0_SPI0_MOSI_O [get_bd_pins processing_system7_0/SPI0_MOSI_O] \
[get_bd_ports OLED_SDIN]
connect_bd_net -net processing_system7_0_SPI0_SCLK_O [get_bd_pins processing_system7_0/SPI0_SCLK_O] \
[get_bd_ports OLED_SCLK]
connect_bd_net -net processing_system7_0_TTC0_WAVE0_OUT [get_bd_pins processing_system7_0/TTC0_WAVE0_OUT] \
[get_bd_ports TTC0_WAVEOUT]
connect_bd_net -net processing_system7_0_UART0_TX [get_bd_pins processing_system7_0/UART0_TX] \
[get_bd_pins uart_mux_0/uart_0_tx]
connect_bd_net -net rst_ps7_0_100M_peripheral_aresetn [get_bd_pins rst_ps7_0_100M/peripheral_aresetn] \
[get_bd_pins axi_uartlite_0/s_axi_aresetn] \
[get_bd_pins axi_uart16550_0/s_axi_aresetn] \
[get_bd_pins smartconnect_0/aresetn]
connect_bd_net -net rx_in_0_1 [get_bd_ports UART_rxd] \
[get_bd_pins uart_mux_0/rx_in]
connect_bd_net -net uart_mux_0_tx_out [get_bd_pins uart_mux_0/tx_out] \
[get_bd_ports UART_txd]
connect_bd_net -net uart_mux_0_uart_0_rx [get_bd_pins uart_mux_0/uart_0_rx] \
[get_bd_pins processing_system7_0/UART0_RX]
connect_bd_net -net uart_mux_0_uart_1_rx [get_bd_pins uart_mux_0/uart_1_rx] \
[get_bd_pins axi_uartlite_0/rx]
connect_bd_net -net uart_mux_0_uart_2_rx [get_bd_pins uart_mux_0/uart_2_rx] \
[get_bd_pins axi_uart16550_0/sin]
connect_bd_net -net xlconstant_1_dout1 [get_bd_pins ilconstant_1/dout] \
[get_bd_pins axi_uart16550_0/rin] \
[get_bd_pins axi_uart16550_0/dsrn] \
[get_bd_pins axi_uart16550_0/ctsn] \
[get_bd_pins axi_uart16550_0/dcdn]
connect_bd_net -net xlslice_1_Dout [get_bd_pins EMIO_O_0/Dout] \
[get_bd_pins EMIO_I/In0] \
[get_bd_pins UART_MUX/Din] \
[get_bd_pins LEDS/Din] \
[get_bd_pins OLED_DC/Din] \
[get_bd_pins OLED_RESET/Din] \
[get_bd_pins OLED_VDD/Din] \
[get_bd_pins OLED_VBAT/Din]
# Create address segments
assign_bd_address -offset 0x43C00000 -range 0x00010000 -target_address_space [get_bd_addr_spaces processing_system7_0/Data] [get_bd_addr_segs axi_uart16550_0/S_AXI/Reg] -force
+41 -1
View File
@@ -70,6 +70,46 @@ set_property IOSTANDARD LVCMOS33 [get_ports UART_rxd]
set_property PACKAGE_PIN AA11 [get_ports UART_txd]
set_property IOSTANDARD LVCMOS33 [get_ports UART_txd]
# OLED SPI
set_property PACKAGE_PIN AB12 [get_ports OLED_SCLK]
set_property PACKAGE_PIN AA12 [get_ports OLED_SDIN]
set_property PACKAGE_PIN U9 [get_ports OLED_RESET]
set_property PULLUP TRUE [get_ports OLED_RESET]
set_property PACKAGE_PIN U10 [get_ports OLED_DC]
set_property PACKAGE_PIN U12 [get_ports OLED_VDD]
set_property PACKAGE_PIN U11 [get_ports OLED_VBAT]
# TTC0 Wave Out
set_property PACKAGE_PIN W12 [get_ports {TTC0_WAVEOUT}]
set_property IOSTANDARD LVCMOS33 [get_ports {TTC0_WAVEOUT}]
set_property IOSTANDARD LVCMOS33 [get_ports {TTC0_WAVEOUT}]
# ----------------------------------------------------------------------------
# IOSTANDARD Constraints
#
# Note that these IOSTANDARD constraints are applied to all IOs currently
# assigned within an I/O bank. If these IOSTANDARD constraints are
# evaluated prior to other PACKAGE_PIN constraints being applied, then
# the IOSTANDARD specified will likely not be applied properly to those
# pins. Therefore, bank wide IOSTANDARD constraints should be placed
# within the XDC file in a location that is evaluated AFTER all
# PACKAGE_PIN constraints within the target bank have been evaluated.
#
# Un-comment one or more of the following IOSTANDARD constraints according to
# the bank pin assignments that are required within a design.
# ----------------------------------------------------------------------------
# Note that the bank voltage for IO Bank 33 is fixed to 3.3V on ZedBoard.
set_property IOSTANDARD LVCMOS33 [get_ports -of_objects [get_iobanks 33]];
# Set the bank voltage for IO Bank 34 to 1.8V by default.
# set_property IOSTANDARD LVCMOS33 [get_ports -of_objects [get_iobanks 34]];
# set_property IOSTANDARD LVCMOS25 [get_ports -of_objects [get_iobanks 34]];
set_property IOSTANDARD LVCMOS18 [get_ports -of_objects [get_iobanks 34]];
# Set the bank voltage for IO Bank 35 to 1.8V by default.
# set_property IOSTANDARD LVCMOS33 [get_ports -of_objects [get_iobanks 35]];
# set_property IOSTANDARD LVCMOS25 [get_ports -of_objects [get_iobanks 35]];
set_property IOSTANDARD LVCMOS18 [get_ports -of_objects [get_iobanks 35]];
# Note that the bank voltage for IO Bank 13 is fixed to 3.3V on ZedBoard.
set_property IOSTANDARD LVCMOS33 [get_ports -of_objects [get_iobanks 13]];