Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 18b6ee2c41 | |||
| dfe68131c2 | |||
| af23c47fc7 | |||
| 32e0e27ca7 | |||
| bdc4780bcc | |||
| 7df10e6ea1 | |||
| 306ef90094 | |||
| fcd971a7d3 | |||
| c09d75b602 | |||
| c72ba780d4 | |||
| 5472293907 | |||
| 80ad791061 | |||
| f78f159fde | |||
| 1d66fcd077 | |||
| 7bf6322fc2 | |||
| 90e4604187 | |||
| 34727bf48e | |||
| 3bd62fc1eb | |||
| 11b37e9c2b | |||
| 9ed6ac32ce | |||
| 7eeedec527 | |||
| d30273aa0c | |||
| 79c6aea160 | |||
| b85e2cf1a3 | |||
| 78e2dd23f9 | |||
| df887d5665 | |||
| 62eebc6770 | |||
| 5a44b3f658 | |||
| c85f492c03 | |||
| 20102a2b7a | |||
| 110adc8a63 | |||
| 6fe5ac5edb | |||
| 923f988a32 | |||
| a6c40c5fa9 | |||
| 21a930dc09 | |||
| 8215eec0cf | |||
| bf6f492c49 | |||
| f7074bcf7d | |||
| 349509c90c | |||
| c38c98dfb0 | |||
| 69537126ec | |||
| 004e862715 | |||
| f1312c1b17 |
@@ -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 |
@@ -11,6 +11,7 @@ members = [
|
||||
"examples/embassy",
|
||||
"examples/zedboard",
|
||||
"examples/defmt",
|
||||
"examples/multiprio",
|
||||
|
||||
"zedboard-bsp",
|
||||
"zedboard-qspi-flasher",
|
||||
@@ -40,3 +41,11 @@ codegen-units = 1
|
||||
lto = 'fat'
|
||||
# Leave the debug symbols in (default is no debug info)
|
||||
debug = 2
|
||||
|
||||
[patch.crates-io]
|
||||
embassy-executor = { path = "../../embassy/embassy-executor" }
|
||||
embassy-executor-macros = { path = "../../embassy/embassy-executor-macros" }
|
||||
embassy-time-driver = { path = "../../embassy/embassy-time-driver" }
|
||||
embassy-time = { path = "../../embassy/embassy-time" }
|
||||
embassy-executor-timer-queue = { path = "../../embassy/embassy-executor-timer-queue" }
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -11,22 +11,27 @@ 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 = [
|
||||
"platform-cortex-ar",
|
||||
"platform-z7",
|
||||
"executor-thread",
|
||||
]}
|
||||
|
||||
# embassy-executor = { git = "https://github.com/robamu/embassy.git", branch = "add-z7-arch-support", features = ["platform-z7", "executor-thread"] }
|
||||
# embassy-executor = { git = "https://github.com/robamu/embassy.git", features = ["platform-cortex-ar", "executor-thread"] }
|
||||
embassy-time = { version = "0.5", features = ["tick-hz-1_000_000"] }
|
||||
|
||||
@@ -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();
|
||||
@@ -71,13 +72,7 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
uart.write_all(b"-- Zynq 7000 DHT22 --\n\r").unwrap();
|
||||
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
|
||||
|
||||
let mut delay = Delay;
|
||||
|
||||
@@ -157,23 +152,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 _;
|
||||
|
||||
@@ -49,14 +51,7 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
.unwrap();
|
||||
uart.write_all(b"-- Zynq 7000 Embassy Hello World --\n\r")
|
||||
.unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
|
||||
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
@@ -71,23 +66,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)]
|
||||
|
||||
@@ -8,17 +8,18 @@ use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use log::info;
|
||||
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::{self, 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,58 @@ async fn main(spawner: Spawner) -> ! {
|
||||
uart.flush().unwrap();
|
||||
|
||||
let (tx, _rx) = uart.split();
|
||||
let mut logger = TxAsync::new(tx);
|
||||
let logger = TxAsync::new(tx, true);
|
||||
|
||||
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
|
||||
let mut log_runner =
|
||||
zynq7000_hal::log::asynch::init_with_uart_tx(log::LevelFilter::Trace, logger).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());
|
||||
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;
|
||||
}
|
||||
spawner.spawn(hello_task().unwrap());
|
||||
|
||||
log_runner.run().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)]
|
||||
@@ -144,6 +147,7 @@ fn prefetch_handler(_faulting_addr: usize) -> ! {
|
||||
/// Panic handler
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
error!("Panic: {info:?}");
|
||||
let mut uart = unsafe { uart::Uart::steal(uart::UartId::Uart1) };
|
||||
writeln!(uart, "panic: {}\r", info).ok();
|
||||
loop {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use aarch32_cpu::asm::nop;
|
||||
use core::panic::PanicInfo;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Delay, Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_hal_async::delay::DelayNs as _;
|
||||
use embedded_io::Write as _;
|
||||
use log::error;
|
||||
use zynq7000_hal::{
|
||||
InteruptConfig, clocks,
|
||||
executor::InterruptExecutor,
|
||||
generic_interrupt_handler,
|
||||
gic::{SgiInterrupt, TargetCpus},
|
||||
gpio::{self, Output},
|
||||
gtc,
|
||||
time::Hertz,
|
||||
uart,
|
||||
};
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||
|
||||
static INTERUPT_EXECUTOR: InterruptExecutor = InterruptExecutor::new();
|
||||
|
||||
/// Entry point which calls the embassy main method.
|
||||
#[zynq7000_rt::entry]
|
||||
fn entry_point() -> ! {
|
||||
main();
|
||||
}
|
||||
|
||||
#[embassy_executor::main(executor = "zynq7000_hal::executor::Executor")]
|
||||
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(InteruptConfig::AllInterruptsToCpu0),
|
||||
})
|
||||
.unwrap();
|
||||
let clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
|
||||
// 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);
|
||||
let mio_pins = gpio::mio::Pins::new(periphs.gpio);
|
||||
let led = gpio::Output::new_for_mio(mio_pins.mio7, gpio::PinState::Low);
|
||||
let sgi_interrupt = SgiInterrupt::new(0).unwrap();
|
||||
|
||||
// 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),
|
||||
(mio_pins.mio48, mio_pins.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
uart.write_all(b"-- Zynq 7000 Interrupt Executor --\n\r")
|
||||
.unwrap();
|
||||
uart.flush().unwrap();
|
||||
|
||||
//let (tx, _rx) = uart.split();
|
||||
// let mut logger = uart::TxAsync::new(tx, true);
|
||||
//let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace).unwrap();
|
||||
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
|
||||
log::info!(
|
||||
"interrupt counter: {}",
|
||||
CNTR.load(core::sync::atomic::Ordering::Relaxed)
|
||||
);
|
||||
|
||||
let spawner = INTERUPT_EXECUTOR.start(sgi_interrupt, TargetCpus::CPU_0);
|
||||
zynq7000_hal::register_interrupt(
|
||||
zynq7000_hal::Interrupt::Sgi(sgi_interrupt),
|
||||
on_interrupt_low_prio,
|
||||
);
|
||||
spawner.spawn(led_task(led).unwrap());
|
||||
|
||||
log::info!(
|
||||
"interrupt counter: {}",
|
||||
CNTR.load(core::sync::atomic::Ordering::Relaxed)
|
||||
);
|
||||
|
||||
//Delay.delay_ms(1).await;
|
||||
|
||||
/*
|
||||
log::info!(
|
||||
"interrupt counter: {}",
|
||||
CNTR.load(core::sync::atomic::Ordering::Relaxed)
|
||||
);
|
||||
*/
|
||||
|
||||
//let mut log_buf: [u8; 2048] = [0; 2048];
|
||||
loop {
|
||||
log::info!("THR alive");
|
||||
/*
|
||||
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;
|
||||
}
|
||||
*/
|
||||
Ticker::every(Duration::from_millis(1000)).next().await;
|
||||
}
|
||||
}
|
||||
|
||||
static CNTR: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
|
||||
|
||||
unsafe fn interrupt_handler(_ctx: *mut ()) {
|
||||
CNTR.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
|
||||
unsafe {
|
||||
INTERUPT_EXECUTOR.on_interrupt();
|
||||
}
|
||||
CNTR.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[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();
|
||||
log::info!(
|
||||
"Toggling LED ({})",
|
||||
ATOMIC_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
|
||||
);
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[zynq7000_rt::irq]
|
||||
pub fn irq_handler() {
|
||||
// Safety: Called here once.
|
||||
let result = unsafe { generic_interrupt_handler() };
|
||||
log::info!(
|
||||
"Interrupt handled, counter: {}",
|
||||
CNTR.load(core::sync::atomic::Ordering::Relaxed)
|
||||
);
|
||||
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 {}
|
||||
}
|
||||
@@ -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();
|
||||
@@ -78,13 +79,7 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
.unwrap();
|
||||
uart.write_all(b"-- Zynq 7000 PWM example--\n\r").unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
|
||||
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
@@ -107,23 +102,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)]
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
[package]
|
||||
name = "embassy-multiprio"
|
||||
version = "0.1.0"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
edition = "2024"
|
||||
description = "Embassy examples for the Zynq7000 SoC"
|
||||
homepage = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||
repository = "https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs"
|
||||
license = "MIT OR Apache-2.0"
|
||||
keywords = ["no-std", "arm", "cortex-a", "amd", "zynq7000"]
|
||||
categories = ["embedded", "no-std", "hardware-support"]
|
||||
|
||||
[dependencies]
|
||||
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"
|
||||
arbitrary-int = "2"
|
||||
fugit = "0.4"
|
||||
log = "0.4"
|
||||
embassy-time = { version = "0.5", features = ["tick-hz-1_000_000"] }
|
||||
embassy-executor = { version = "0.10", features = [
|
||||
"platform-z7",
|
||||
"executor-thread",
|
||||
"executor-interrupt",
|
||||
]}
|
||||
@@ -0,0 +1,31 @@
|
||||
//! This build script copies the `memory.x` file from the crate root into
|
||||
//! a directory where the linker can always find it at build time.
|
||||
//! For many projects this is optional, as the linker always searches the
|
||||
//! project root directory -- wherever `Cargo.toml` is. However, if you
|
||||
//! are using a workspace or have a more complicated build setup, this
|
||||
//! build script becomes required. Additionally, by requesting that
|
||||
//! Cargo re-run the build script whenever `memory.x` is changed,
|
||||
//! updating `memory.x` ensures a rebuild of the application with the
|
||||
//! new memory settings.
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
// Put `memory.x` in our output directory and ensure it's
|
||||
// on the linker search path.
|
||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
File::create(out.join("memory.x"))
|
||||
.unwrap()
|
||||
.write_all(include_bytes!("memory.x"))
|
||||
.unwrap();
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
// By default, Cargo will re-run a build script whenever
|
||||
// any file in the project changes. By specifying `memory.x`
|
||||
// here, we ensure the build script is only re-run when
|
||||
// `memory.x` is changed.
|
||||
println!("cargo:rerun-if-changed=memory.x");
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
MEMORY
|
||||
{
|
||||
/* Zedboard: 512 MB DDR3. Only use 62 MB for now, should be plenty for a bare-metal app.
|
||||
1 MB stack memory and 1 MB of memory which will be configured as uncached device memory by the
|
||||
MMU. This is recommended for something like DMA descriptors. */
|
||||
CODE(rx) : ORIGIN = 0x00100000, LENGTH = 62M
|
||||
STACKS : ORIGIN = 0x3F00000, LENGTH = 1M
|
||||
UNCACHED(rx): ORIGIN = 0x4000000, LENGTH = 1M
|
||||
OCM_UPPER(rx): ORIGIN = 0xFFFF0000, LENGTH = 64K
|
||||
}
|
||||
|
||||
REGION_ALIAS("VECTORS", CODE);
|
||||
REGION_ALIAS("DATA", CODE);
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Uncached memory */
|
||||
.uncached (NOLOAD) : ALIGN(4) {
|
||||
. = ALIGN(4);
|
||||
_sbss_uncached = .;
|
||||
*(.uncached .uncached.*);
|
||||
. = ALIGN(4);
|
||||
_ebss_uncached = .;
|
||||
} > UNCACHED
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use aarch32_cpu::asm::nop;
|
||||
use arbitrary_int::u5;
|
||||
use core::panic::PanicInfo;
|
||||
use embassy_executor::{InterruptExecutor, Spawner};
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write as _;
|
||||
use log::error;
|
||||
use zynq7000_hal::{
|
||||
InteruptConfig, clocks, generic_interrupt_handler,
|
||||
gic::SgiInterrupt,
|
||||
gpio::{self, Output},
|
||||
gtc,
|
||||
time::Hertz,
|
||||
uart,
|
||||
};
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||
|
||||
static INTERUPT_EXECUTOR_LOW_PRIO: InterruptExecutor = InterruptExecutor::new();
|
||||
static INTERUPT_EXECUTOR_MED_PRIO: InterruptExecutor = InterruptExecutor::new();
|
||||
|
||||
/// 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(InteruptConfig::AllInterruptsToCpu0),
|
||||
})
|
||||
.unwrap();
|
||||
let clocks = clocks::Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
let mut gic = unsafe { zynq7000_hal::gic::Configurator::steal() };
|
||||
gic.set_all_sgi_interrupt_targets_cpu0();
|
||||
|
||||
// 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);
|
||||
let mio_pins = gpio::mio::Pins::new(periphs.gpio);
|
||||
let led = gpio::Output::new_for_mio(mio_pins.mio7, gpio::PinState::Low);
|
||||
let sgi_interrupt_low_prio = SgiInterrupt::new(0).unwrap();
|
||||
let sgi_interrupt_med_prio = SgiInterrupt::new(1).unwrap();
|
||||
|
||||
// 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),
|
||||
(mio_pins.mio48, mio_pins.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
uart.write_all(b"-- Zynq 7000 Interrupt Executor --\n\r")
|
||||
.unwrap();
|
||||
uart.flush().unwrap();
|
||||
|
||||
let (tx, _rx) = uart.split();
|
||||
let mut logger = uart::TxAsync::new(tx, true);
|
||||
let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace).unwrap();
|
||||
|
||||
gic.set_sgi_interrupt_priority(sgi_interrupt_low_prio, u5::new(2));
|
||||
gic.set_sgi_interrupt_priority(sgi_interrupt_med_prio, u5::new(1));
|
||||
|
||||
zynq7000_hal::register_interrupt(
|
||||
zynq7000_hal::Interrupt::Sgi(sgi_interrupt_low_prio),
|
||||
on_interrupt_low_prio,
|
||||
);
|
||||
zynq7000_hal::register_interrupt(
|
||||
zynq7000_hal::Interrupt::Sgi(sgi_interrupt_med_prio),
|
||||
on_interrupt_med_prio,
|
||||
);
|
||||
let spawner = INTERUPT_EXECUTOR_LOW_PRIO.start(sgi_interrupt_low_prio.as_u4());
|
||||
spawner.spawn(led_task(led).unwrap());
|
||||
let spawner = INTERUPT_EXECUTOR_MED_PRIO.start(sgi_interrupt_med_prio.as_u4());
|
||||
spawner.spawn(hello_task().unwrap());
|
||||
|
||||
let mut log_buf: [u8; 2048] = [0; 2048];
|
||||
loop {
|
||||
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;
|
||||
}
|
||||
Ticker::every(Duration::from_millis(1000)).next().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();
|
||||
log::info!(
|
||||
"Toggling LED ({})",
|
||||
ATOMIC_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed)
|
||||
);
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn hello_task() {
|
||||
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||
loop {
|
||||
log::info!("Hello from the low priority task");
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn on_interrupt_low_prio() {
|
||||
unsafe {
|
||||
INTERUPT_EXECUTOR_LOW_PRIO.on_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn on_interrupt_med_prio() {
|
||||
unsafe {
|
||||
INTERUPT_EXECUTOR_MED_PRIO.on_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
@@ -59,14 +60,7 @@ fn main() -> ! {
|
||||
.unwrap();
|
||||
uart.write_all(b"-- Zynq 7000 GTC Ticks example --\n\r")
|
||||
.unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
|
||||
|
||||
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
|
||||
loop {
|
||||
@@ -83,8 +77,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) => {
|
||||
|
||||
@@ -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();
|
||||
@@ -60,13 +60,7 @@ fn main() -> ! {
|
||||
uart.write_all(b"-- Zynq 7000 Logging example --\n\r")
|
||||
.unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
|
||||
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {boot_mode:?}");
|
||||
@@ -85,13 +79,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
//! Example which uses the UART1 to send log messages.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use aarch32_cpu::asm::nop;
|
||||
use core::{panic::PanicInfo, sync::atomic::AtomicU64};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::Clocks,
|
||||
gic::{self, Configurator, Interrupt, SgiInterrupt},
|
||||
gpio::{Output, PinState, mio},
|
||||
l2_cache,
|
||||
time::Hertz,
|
||||
uart::{ClockConfig, Config, Uart},
|
||||
};
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||
|
||||
static SGI_COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
#[zynq7000_rt::entry]
|
||||
fn main() -> ! {
|
||||
let mut dp = zynq7000::Peripherals::take().unwrap();
|
||||
l2_cache::init_with_defaults(&mut dp.l2c);
|
||||
|
||||
// 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 = Configurator::new_with_init(dp.gicc, dp.gicd);
|
||||
gic.enable_all_interrupts();
|
||||
gic.set_all_spi_interrupt_targets_cpu0();
|
||||
gic.set_all_sgi_interrupt_targets_cpu0();
|
||||
gic.enable();
|
||||
// Enable interrupt exception.
|
||||
unsafe { gic.enable_interrupts() };
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClockConfig::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mio_pins = mio::Pins::new(dp.gpio);
|
||||
|
||||
let mut uart = Uart::new_with_mio_for_uart_1(
|
||||
dp.uart_1,
|
||||
Config::new_with_clk_config(uart_clk_config),
|
||||
(mio_pins.mio48, mio_pins.mio49),
|
||||
)
|
||||
.unwrap();
|
||||
uart.write_all(b"-- Zynq 7000 Software Interrupt example --\n\r")
|
||||
.unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {boot_mode:?}");
|
||||
|
||||
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
|
||||
loop {
|
||||
let gtc = SGI_COUNTER.load(core::sync::atomic::Ordering::Relaxed);
|
||||
info!("Hello, world!");
|
||||
info!("SGI counter: {gtc}");
|
||||
gic.trigger_software_interrupt(SgiInterrupt::new(0).unwrap());
|
||||
led.toggle().unwrap();
|
||||
for _ in 0..5_000_000 {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[zynq7000_rt::irq]
|
||||
fn irq_handler() {
|
||||
let mut gic_helper = gic::InterruptGuard::new();
|
||||
let irq_info = gic_helper.interrupt_info();
|
||||
match irq_info.interrupt() {
|
||||
Interrupt::Sgi(_sgi) => {
|
||||
// TODO: Send ID to main thread.
|
||||
SGI_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
Interrupt::Ppi(_ppi_interrupt) => (),
|
||||
Interrupt::Spi(_spi_interrupt) => (),
|
||||
Interrupt::Invalid(_) => (),
|
||||
Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[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 {}
|
||||
}
|
||||
@@ -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.
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 |
Binary file not shown.
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
@@ -241,7 +242,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
.unwrap();
|
||||
uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe { zynq7000_hal::log::uart_blocking::init_unsafe_single_core(uart, LOG_LEVEL, false) };
|
||||
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, LOG_LEVEL, false);
|
||||
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
@@ -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();
|
||||
@@ -80,14 +80,8 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
.unwrap();
|
||||
uart.write_all(b"-- Zynq 7000 Zedboard I2C L3GD20H example --\n\r")
|
||||
.unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
|
||||
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
|
||||
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
@@ -153,24 +147,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)]
|
||||
|
||||
@@ -20,14 +20,15 @@ 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},
|
||||
log::asynch::UartLoggerRunner,
|
||||
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,32 @@ 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 (tx, _) = uart.split();
|
||||
let tx_async = TxAsync::new(tx, true);
|
||||
let log_runner =
|
||||
zynq7000_hal::log::asynch::init_with_uart_tx(log::LevelFilter::Trace, tx_async).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,
|
||||
// l3gd20::MODE,
|
||||
embedded_hal::spi::MODE_3,
|
||||
spi::SlaveSelectConfig::AutoWithAutoStart,
|
||||
spi::SlaveSelectConfig::AutoCsAutoStart,
|
||||
),
|
||||
(
|
||||
gpio_pins.mio.mio12,
|
||||
@@ -123,10 +131,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,25 +166,17 @@ async fn main(spawner: Spawner) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
spawner.spawn(logger_task(uart).unwrap());
|
||||
spawner.spawn(logger_task(log_runner).unwrap());
|
||||
if BLOCKING {
|
||||
blocking_application(mio_led, emio_leds, spi).await;
|
||||
blocking_application(mio_led, emio_leds, spi).await
|
||||
} else {
|
||||
non_blocking_application(mio_led, emio_leds, spi).await;
|
||||
non_blocking_application(mio_led, emio_leds, spi).await
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn logger_task(uart: uart::Uart) {
|
||||
let (tx, _) = uart.split();
|
||||
let mut tx_async = TxAsync::new(tx);
|
||||
let frame_queue = zynq7000_hal::log::rb::get_frame_queue();
|
||||
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;
|
||||
}
|
||||
pub async fn logger_task(mut log_runner: UartLoggerRunner) -> ! {
|
||||
log_runner.run().await
|
||||
}
|
||||
|
||||
pub async fn blocking_application(
|
||||
@@ -237,30 +240,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,276 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use aarch32_cpu::asm::nop;
|
||||
use core::panic::PanicInfo;
|
||||
use dummy_pin::DummyPin;
|
||||
use embassy_executor::Spawner;
|
||||
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::info;
|
||||
use ssd1306::{Ssd1306Async, prelude::*};
|
||||
use zedboard::PS_CLOCK_FREQUENCY;
|
||||
use zynq7000_hal::{
|
||||
BootMode, clocks, generic_interrupt_handler, gpio, gtc,
|
||||
log::asynch::UartLoggerRunner,
|
||||
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 Async 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();
|
||||
|
||||
let (tx, _) = uart.split();
|
||||
let tx_async = TxAsync::new(tx, true);
|
||||
let log_runner =
|
||||
zynq7000_hal::log::asynch::init_with_uart_tx(log::LevelFilter::Trace, tx_async)
|
||||
.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(log_runner).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(mut log_runner: UartLoggerRunner) -> ! {
|
||||
log_runner.run().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) -> ! {
|
||||
let mut uart = unsafe { uart::Uart::steal(uart::UartId::Uart1) };
|
||||
writeln!(uart, "panic: {}\r", info).ok();
|
||||
loop {}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use aarch32_cpu::asm::nop;
|
||||
use core::panic::PanicInfo;
|
||||
use dummy_pin::DummyPin;
|
||||
use embassy_executor::Spawner;
|
||||
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::info;
|
||||
use ssd1306::{Ssd1306, prelude::*};
|
||||
use zedboard::PS_CLOCK_FREQUENCY;
|
||||
use zynq7000_hal::{
|
||||
BootMode, clocks, generic_interrupt_handler, gpio, gtc,
|
||||
log::asynch::UartLoggerRunner,
|
||||
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();
|
||||
|
||||
let (tx, _) = uart.split();
|
||||
let tx_async = TxAsync::new(tx, true);
|
||||
let log_runner =
|
||||
zynq7000_hal::log::asynch::init_with_uart_tx(log::LevelFilter::Trace, tx_async)
|
||||
.expect("Failed to initialize async logger");
|
||||
spawner.spawn(logger_task(log_runner).unwrap());
|
||||
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
info!(
|
||||
"SPI reference clock speed: {:?}",
|
||||
clocks.io_clocks().spi_clk()
|
||||
);
|
||||
|
||||
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(mut log_runner: UartLoggerRunner) -> ! {
|
||||
log_runner.run().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) -> ! {
|
||||
let mut uart = unsafe { uart::Uart::steal(uart::UartId::Uart1) };
|
||||
writeln!(uart, "panic: {}\r", info).ok();
|
||||
loop {}
|
||||
}
|
||||
@@ -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 _;
|
||||
|
||||
@@ -60,14 +62,7 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
)
|
||||
.unwrap();
|
||||
uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
|
||||
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
@@ -193,23 +188,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)]
|
||||
|
||||
@@ -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 _;
|
||||
|
||||
@@ -67,13 +67,7 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
.unwrap();
|
||||
uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
|
||||
|
||||
let sdio_clock_config =
|
||||
SdClockConfig::calculate_for_io_clock(clocks.io_clocks(), 100.MHz(), 10.MHz()).unwrap();
|
||||
@@ -227,23 +221,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)]
|
||||
|
||||
@@ -0,0 +1,587 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use aarch32_cpu::asm::nop;
|
||||
use core::{cell::RefCell, panic::PanicInfo, sync::atomic::AtomicU8};
|
||||
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 as _;
|
||||
use embedded_io::Write;
|
||||
use embedded_io_async::Read as _;
|
||||
use log::info;
|
||||
use zedboard::PS_CLOCK_FREQUENCY;
|
||||
use zynq7000::spi::FifoWrite;
|
||||
use zynq7000_hal::{
|
||||
BootMode, clocks, generic_interrupt_handler, gpio, gtc,
|
||||
log::asynch::UartLoggerRunner,
|
||||
spi::{self, SpiAsync},
|
||||
uart::{self, TxAsync},
|
||||
};
|
||||
|
||||
use zynq7000_rt as _;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum TestType {
|
||||
Blocking,
|
||||
Async,
|
||||
}
|
||||
|
||||
const TEST_TYPE: TestType = TestType::Async;
|
||||
|
||||
const INIT_STRING: &str = "-- Zynq 7000 SPI slave 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);
|
||||
|
||||
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();
|
||||
Delay.delay_ms(1).await;
|
||||
|
||||
let (tx, _rx) = uart.split();
|
||||
let tx_asynch = TxAsync::new(tx, true);
|
||||
let logger_runner =
|
||||
zynq7000_hal::log::asynch::init_with_uart_tx(log::LevelFilter::Trace, tx_asynch)
|
||||
.expect("Failed to initialize async logger");
|
||||
spawner.spawn(logger_task(logger_runner).unwrap());
|
||||
|
||||
// This pin is used to select between a routing from SPI0 to OLED, or SPI0 to SPI1. Low selects
|
||||
// the SPI0 to OLED interface.
|
||||
let _spi_mux_pin =
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(15).unwrap(), gpio::PinState::High);
|
||||
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
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());
|
||||
|
||||
// Enable loopback.
|
||||
spi::enable_spi0_to_spi1_loopback();
|
||||
|
||||
let mut spi_slave = spi::SpiSlave::new(
|
||||
spi::SpiId::Spi1,
|
||||
periphs.spi_1,
|
||||
spi::SlaveConfig {
|
||||
mode: spi::MODE_0,
|
||||
enable_modefail: true,
|
||||
},
|
||||
);
|
||||
zynq7000_hal::register_interrupt(spi_slave.interrupt_id(), spi_interrupt_handler);
|
||||
|
||||
let spi_master = spi::Spi::new_for_emio(
|
||||
periphs.spi_0,
|
||||
spi::Config::new(
|
||||
// 10 MHz maximum rating of the sensor.
|
||||
zynq7000::spi::BaudDivSel::By64,
|
||||
// l3gd20::MODE,
|
||||
embedded_hal::spi::MODE_0,
|
||||
spi::SlaveSelectConfig::AutoCsManualStart,
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
let cs_pin = spi::ChipSelectPin::new(spi_master.id(), spi::ChipSelect::Slave0);
|
||||
|
||||
// We pre-load the FIFO with a fixed, known sequence.
|
||||
for i in 0..spi::FIFO_DEPTH / 2 {
|
||||
spi_slave.write_tx_data(i as u8);
|
||||
}
|
||||
log::info!("SPI slave initialized, enabling interrupts and peripheral");
|
||||
spi_slave.enable_interrupts(true);
|
||||
spi_slave.enable();
|
||||
|
||||
static RX_DATA_PIPE: static_cell::ConstStaticCell<
|
||||
embassy_sync::pipe::Pipe<CriticalSectionRawMutex, 256>,
|
||||
> = static_cell::ConstStaticCell::new(embassy_sync::pipe::Pipe::new());
|
||||
|
||||
let pipe = RX_DATA_PIPE.take();
|
||||
let (mut slave_rx_reader, writer) = pipe.split();
|
||||
critical_section::with(|cs| {
|
||||
RX_DATA_WRITER.borrow(cs).replace(Some(writer));
|
||||
});
|
||||
|
||||
if TEST_TYPE == TestType::Blocking {
|
||||
let mut spi_master =
|
||||
embedded_hal_bus::spi::ExclusiveDevice::new(spi_master, cs_pin, embassy_time::Delay)
|
||||
.expect("creating exlusive SPI master failed");
|
||||
|
||||
let current_rx_val = blocking_tests_small(&mut spi_master, &mut slave_rx_reader).await;
|
||||
log::info!("blocking tests with small data blocks done");
|
||||
Delay.delay_ms(10).await;
|
||||
blocking_tests_large(&mut spi_master, current_rx_val, &mut slave_rx_reader).await;
|
||||
log::info!("blocking tests with large data blocks done");
|
||||
} else {
|
||||
let spi_master = SpiAsync::new(spi_master);
|
||||
let mut spi_master =
|
||||
embedded_hal_bus::spi::ExclusiveDevice::new(spi_master, cs_pin, embassy_time::Delay)
|
||||
.expect("creating exlusive SPI master failed");
|
||||
let current_rx_val =
|
||||
blocking_tests_small_async(&mut spi_master, &mut slave_rx_reader).await;
|
||||
log::info!("async tests with small data blocks done");
|
||||
Delay.delay_ms(10).await;
|
||||
blocking_tests_large_async(&mut spi_master, current_rx_val, &mut slave_rx_reader).await;
|
||||
log::info!("blocking tests with large data blocks done");
|
||||
}
|
||||
|
||||
log::info!("SPI slave tests done");
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||
loop {
|
||||
ticker.next().await; // Wait for the next cycle of the ticker
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn blocking_tests_small(
|
||||
spi_master: &mut impl embedded_hal::spi::SpiDevice,
|
||||
slave_rx_reader: &mut embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 256>,
|
||||
) -> u8 {
|
||||
let mut tx_buf = [0; 64];
|
||||
let mut buf = [0; 64];
|
||||
|
||||
// Write test.
|
||||
// We should read back 0,1,2,3 here but the sent values are ignored.
|
||||
spi_master.write(&[1, 2, 3, 4]).unwrap();
|
||||
slave_rx_reader.read_exact(&mut buf[0..4]).await.unwrap();
|
||||
assert_eq!(
|
||||
&buf[0..4],
|
||||
&[1, 2, 3, 4],
|
||||
"slave did not receive the expected data"
|
||||
);
|
||||
|
||||
// Read test.
|
||||
spi_master.read(&mut buf[0..4]).unwrap();
|
||||
// Now we expect 4,5,6,7 sent by the SPI slave
|
||||
assert_eq!(
|
||||
&buf[0..4],
|
||||
&[4, 5, 6, 7],
|
||||
"slave did not send the expected data"
|
||||
);
|
||||
// Slave should receive dummy data
|
||||
slave_rx_reader.read_exact(&mut buf[0..4]).await.unwrap();
|
||||
assert_eq!(
|
||||
&buf[0..4],
|
||||
&[0, 0, 0, 0],
|
||||
"slave did not receive the expected data"
|
||||
);
|
||||
|
||||
// Transfer test.
|
||||
for (i, item) in tx_buf.iter_mut().enumerate().take(16) {
|
||||
*item = (i * 2) as u8;
|
||||
}
|
||||
spi_master
|
||||
.transfer(&mut buf[0..16], &tx_buf[0..16])
|
||||
.unwrap();
|
||||
for i in 8..24 {
|
||||
assert_eq!(
|
||||
buf[i - 8],
|
||||
i as u8,
|
||||
"slave did not receive the expected data"
|
||||
);
|
||||
}
|
||||
slave_rx_reader.read_exact(&mut buf[0..16]).await.unwrap();
|
||||
for (i, item) in buf.iter().enumerate().take(16) {
|
||||
assert_eq!(
|
||||
*item,
|
||||
(i * 2) as u8,
|
||||
"slave did not send the expected data"
|
||||
);
|
||||
}
|
||||
|
||||
// Transfer in place test.
|
||||
for (i, item) in buf.iter_mut().enumerate().take(16) {
|
||||
*item = (i * 2) as u8;
|
||||
}
|
||||
spi_master.transfer_in_place(&mut buf[0..16]).unwrap();
|
||||
for i in 24..40 {
|
||||
assert_eq!(
|
||||
buf[i - 24],
|
||||
i as u8,
|
||||
"slave did not receive the expected data"
|
||||
);
|
||||
}
|
||||
slave_rx_reader.read_exact(&mut buf[0..16]).await.unwrap();
|
||||
for (i, item) in buf.iter().enumerate().take(16) {
|
||||
assert_eq!(
|
||||
*item,
|
||||
(i * 2) as u8,
|
||||
"slave did not send the expected data"
|
||||
);
|
||||
}
|
||||
40
|
||||
}
|
||||
|
||||
pub async fn blocking_tests_large(
|
||||
spi_master: &mut impl embedded_hal::spi::SpiDevice,
|
||||
mut current_rx_val: u8,
|
||||
slave_rx_reader: &mut embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 256>,
|
||||
) -> u8 {
|
||||
const BUF_LEN: usize = 164;
|
||||
let mut buf = [0; BUF_LEN];
|
||||
|
||||
// Write test.
|
||||
for (i, item) in buf.iter_mut().enumerate() {
|
||||
*item = i as u8;
|
||||
}
|
||||
spi_master.write(&buf).unwrap();
|
||||
slave_rx_reader
|
||||
.read_exact(&mut buf[0..BUF_LEN])
|
||||
.await
|
||||
.unwrap();
|
||||
for (i, item) in buf.iter().enumerate() {
|
||||
assert_eq!(*item, i as u8, "slave did not receive the expected data");
|
||||
}
|
||||
current_rx_val = current_rx_val.wrapping_add(buf.len() as u8);
|
||||
|
||||
// Read test.
|
||||
spi_master.read(&mut buf).unwrap();
|
||||
for item in &buf {
|
||||
assert_eq!(
|
||||
*item, current_rx_val,
|
||||
"slave did not send the expected data"
|
||||
);
|
||||
current_rx_val = current_rx_val.wrapping_add(1);
|
||||
}
|
||||
slave_rx_reader
|
||||
.read_exact(&mut buf[0..BUF_LEN])
|
||||
.await
|
||||
.unwrap();
|
||||
for item in &buf {
|
||||
assert_eq!(*item, 0, "slave did not receive the expected data");
|
||||
}
|
||||
|
||||
// Transfer test.
|
||||
let mut tx_buf = [0; BUF_LEN];
|
||||
for (i, item) in tx_buf.iter_mut().enumerate() {
|
||||
*item = i as u8;
|
||||
}
|
||||
spi_master.transfer(&mut buf, &tx_buf).unwrap();
|
||||
for item in &buf {
|
||||
assert_eq!(
|
||||
*item, current_rx_val,
|
||||
"slave did not send the expected data"
|
||||
);
|
||||
current_rx_val = current_rx_val.wrapping_add(1);
|
||||
}
|
||||
slave_rx_reader
|
||||
.read_exact(&mut buf[0..BUF_LEN])
|
||||
.await
|
||||
.unwrap();
|
||||
for (i, item) in buf.iter().enumerate() {
|
||||
assert_eq!(*item, i as u8, "slave did not receive the expected data");
|
||||
}
|
||||
|
||||
// Transfer in place test.
|
||||
for (i, item) in buf.iter_mut().enumerate() {
|
||||
*item = i as u8;
|
||||
}
|
||||
spi_master.transfer_in_place(&mut buf).unwrap();
|
||||
for item in &buf {
|
||||
assert_eq!(
|
||||
*item, current_rx_val,
|
||||
"slave did not send the expected data"
|
||||
);
|
||||
current_rx_val = current_rx_val.wrapping_add(1);
|
||||
}
|
||||
slave_rx_reader
|
||||
.read_exact(&mut buf[0..BUF_LEN])
|
||||
.await
|
||||
.unwrap();
|
||||
for (i, item) in buf.iter().enumerate() {
|
||||
assert_eq!(*item, i as u8, "slave did not receive the expected data");
|
||||
}
|
||||
current_rx_val
|
||||
}
|
||||
|
||||
pub async fn blocking_tests_small_async(
|
||||
spi_master: &mut impl embedded_hal_async::spi::SpiDevice,
|
||||
slave_rx_reader: &mut embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 256>,
|
||||
) -> u8 {
|
||||
let mut tx_buf = [0; 64];
|
||||
let mut buf = [0; 64];
|
||||
|
||||
// Write test.
|
||||
// We should read back 0,1,2,3 here but the sent values are ignored.
|
||||
spi_master.write(&[1, 2, 3, 4]).await.unwrap();
|
||||
slave_rx_reader.read_exact(&mut buf[0..4]).await.unwrap();
|
||||
assert_eq!(
|
||||
&buf[0..4],
|
||||
&[1, 2, 3, 4],
|
||||
"slave did not receive the expected data"
|
||||
);
|
||||
|
||||
// Read test.
|
||||
spi_master.read(&mut buf[0..4]).await.unwrap();
|
||||
// Now we expect 4,5,6,7 sent by the SPI slave
|
||||
assert_eq!(
|
||||
&buf[0..4],
|
||||
&[4, 5, 6, 7],
|
||||
"slave did not send the expected data"
|
||||
);
|
||||
// Slave should receive dummy data
|
||||
slave_rx_reader.read_exact(&mut buf[0..4]).await.unwrap();
|
||||
assert_eq!(
|
||||
&buf[0..4],
|
||||
&[0, 0, 0, 0],
|
||||
"slave did not receive the expected data"
|
||||
);
|
||||
|
||||
// Transfer test.
|
||||
for (i, item) in tx_buf.iter_mut().enumerate().take(16) {
|
||||
*item = (i * 2) as u8;
|
||||
}
|
||||
spi_master
|
||||
.transfer(&mut buf[0..16], &tx_buf[0..16])
|
||||
.await
|
||||
.unwrap();
|
||||
for i in 8..24 {
|
||||
assert_eq!(
|
||||
buf[i - 8],
|
||||
i as u8,
|
||||
"slave did not receive the expected data"
|
||||
);
|
||||
}
|
||||
slave_rx_reader.read_exact(&mut buf[0..16]).await.unwrap();
|
||||
for (i, item) in buf.iter().enumerate().take(16) {
|
||||
assert_eq!(
|
||||
*item,
|
||||
(i * 2) as u8,
|
||||
"slave did not send the expected data"
|
||||
);
|
||||
}
|
||||
|
||||
// Transfer in place test.
|
||||
for (i, item) in buf.iter_mut().enumerate().take(16) {
|
||||
*item = (i * 2) as u8;
|
||||
}
|
||||
spi_master.transfer_in_place(&mut buf[0..16]).await.unwrap();
|
||||
for i in 24..40 {
|
||||
assert_eq!(
|
||||
buf[i - 24],
|
||||
i as u8,
|
||||
"slave did not receive the expected data"
|
||||
);
|
||||
}
|
||||
slave_rx_reader.read_exact(&mut buf[0..16]).await.unwrap();
|
||||
for (i, item) in buf.iter().enumerate().take(16) {
|
||||
assert_eq!(
|
||||
*item,
|
||||
(i * 2) as u8,
|
||||
"slave did not send the expected data"
|
||||
);
|
||||
}
|
||||
40
|
||||
}
|
||||
|
||||
pub async fn blocking_tests_large_async(
|
||||
spi_master: &mut impl embedded_hal_async::spi::SpiDevice,
|
||||
mut current_rx_val: u8,
|
||||
slave_rx_reader: &mut embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 256>,
|
||||
) -> u8 {
|
||||
const BUF_LEN: usize = 164;
|
||||
let mut buf = [0; BUF_LEN];
|
||||
|
||||
// Write test.
|
||||
for (i, item) in buf.iter_mut().enumerate() {
|
||||
*item = i as u8;
|
||||
}
|
||||
spi_master.write(&buf).await.unwrap();
|
||||
slave_rx_reader
|
||||
.read_exact(&mut buf[0..BUF_LEN])
|
||||
.await
|
||||
.unwrap();
|
||||
for (i, &item) in buf.iter().enumerate() {
|
||||
assert_eq!(item, i as u8, "slave did not receive the expected data");
|
||||
}
|
||||
current_rx_val = current_rx_val.wrapping_add(buf.len() as u8);
|
||||
|
||||
// Read test.
|
||||
spi_master.read(&mut buf).await.unwrap();
|
||||
for &item in &buf {
|
||||
assert_eq!(item, current_rx_val, "slave did not send the expected data");
|
||||
current_rx_val = current_rx_val.wrapping_add(1);
|
||||
}
|
||||
slave_rx_reader
|
||||
.read_exact(&mut buf[0..BUF_LEN])
|
||||
.await
|
||||
.unwrap();
|
||||
for &item in &buf {
|
||||
assert_eq!(item, 0, "slave did not receive the expected data");
|
||||
}
|
||||
|
||||
// Transfer test.
|
||||
let mut tx_buf = [0; BUF_LEN];
|
||||
for (i, item) in tx_buf.iter_mut().enumerate() {
|
||||
*item = i as u8;
|
||||
}
|
||||
spi_master.transfer(&mut buf, &tx_buf).await.unwrap();
|
||||
for &item in &buf {
|
||||
assert_eq!(item, current_rx_val, "slave did not send the expected data");
|
||||
current_rx_val = current_rx_val.wrapping_add(1);
|
||||
}
|
||||
slave_rx_reader
|
||||
.read_exact(&mut buf[0..BUF_LEN])
|
||||
.await
|
||||
.unwrap();
|
||||
for (i, &item) in buf.iter().enumerate() {
|
||||
assert_eq!(item, i as u8, "slave did not receive the expected data");
|
||||
}
|
||||
|
||||
// Transfer in place test.
|
||||
for (i, item) in buf.iter_mut().enumerate() {
|
||||
*item = i as u8;
|
||||
}
|
||||
spi_master.transfer_in_place(&mut buf).await.unwrap();
|
||||
for &item in &buf {
|
||||
assert_eq!(item, current_rx_val, "slave did not send the expected data");
|
||||
current_rx_val = current_rx_val.wrapping_add(1);
|
||||
}
|
||||
slave_rx_reader
|
||||
.read_exact(&mut buf[0..BUF_LEN])
|
||||
.await
|
||||
.unwrap();
|
||||
for (i, &item) in buf.iter().enumerate() {
|
||||
assert_eq!(item, i as u8, "slave did not receive the expected data");
|
||||
}
|
||||
current_rx_val
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn logger_task(mut logger_task: UartLoggerRunner) {
|
||||
logger_task.run().await;
|
||||
}
|
||||
|
||||
static RX_DATA_WRITER: critical_section::Mutex<
|
||||
RefCell<Option<embassy_sync::pipe::Writer<'static, CriticalSectionRawMutex, 256>>>,
|
||||
> = critical_section::Mutex::new(RefCell::new(None));
|
||||
|
||||
unsafe fn spi_interrupt_handler() {
|
||||
static CURRENT_TX_VAL: AtomicU8 = AtomicU8::new((spi::FIFO_DEPTH / 2) as u8);
|
||||
|
||||
let mut buf = [0; 64];
|
||||
let mut current_val = CURRENT_TX_VAL.load(core::sync::atomic::Ordering::Relaxed);
|
||||
let mut spi = unsafe { spi::SpiLowLevel::steal(spi::SpiId::Spi1) };
|
||||
|
||||
let status = spi.read_interrupt_status();
|
||||
spi.write_interrupt_status(status);
|
||||
|
||||
let mut index = 0;
|
||||
while spi.read_interrupt_status().rx_not_empty() && index < buf.len() {
|
||||
buf[index] = spi.read_rx_data().value();
|
||||
index += 1;
|
||||
}
|
||||
while !spi.read_interrupt_status().tx_full() {
|
||||
spi.write_tx_data(FifoWrite::new(current_val));
|
||||
current_val = current_val.wrapping_add(1);
|
||||
}
|
||||
CURRENT_TX_VAL.store(current_val, core::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let opt_writer = RX_DATA_WRITER.borrow(cs).borrow();
|
||||
if let Some(writer) = opt_writer.as_ref() {
|
||||
// In a real application, you would read the received data from the SPI peripheral here.
|
||||
// For this example, we just write a dummy value to the pipe to demonstrate the concept.
|
||||
let _ = writer.try_write(&buf[0..index]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[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) -> ! {
|
||||
let mut uart = unsafe { uart::Uart::steal(uart::UartId::Uart1) };
|
||||
writeln!(uart, "panic: {}\r", info).ok();
|
||||
loop {}
|
||||
}
|
||||
@@ -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();
|
||||
@@ -131,13 +129,7 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
log_uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||
|
||||
// Safety: Co-operative multi-tasking is used.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
log_uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
zynq7000_hal::log::uart_blocking::init_with_busy_flag(log_uart, log::LevelFilter::Trace, false);
|
||||
|
||||
// UART0 routed through EMIO to PL pins.
|
||||
let mut uart_0 =
|
||||
@@ -147,7 +139,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 +208,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)]
|
||||
|
||||
@@ -37,19 +37,20 @@ use embedded_alloc::LlffHeap as Heap;
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write as _;
|
||||
use heapless::spsc::Queue;
|
||||
use log::{error, info, warn};
|
||||
use log::{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,
|
||||
time::Hertz,
|
||||
uart::{ClockConfig, Config, Uart},
|
||||
uart::{self, 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,14 +219,21 @@ async fn main(spawner: Spawner) -> ! {
|
||||
unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) }
|
||||
}
|
||||
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
log_uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
// 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,
|
||||
);
|
||||
|
||||
zynq7000_hal::log::uart_blocking::init_with_busy_flag(log_uart, log::LevelFilter::Trace, false);
|
||||
|
||||
// Set up UART multiplexing before creating and configuring the UARTs.
|
||||
let mut uart_mux = UartMultiplexer::new([
|
||||
@@ -225,24 +241,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 +306,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 +421,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 +431,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 +464,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 +538,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| {
|
||||
@@ -565,6 +577,7 @@ fn prefetch_handler(_faulting_addr: usize) -> ! {
|
||||
/// Panic handler
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
error!("Panic: {info:?}");
|
||||
let mut uart = unsafe { uart::Uart::steal(uart::UartId::Uart1) };
|
||||
writeln!(uart, "panic: {}\r", info).ok();
|
||||
loop {}
|
||||
}
|
||||
|
||||
@@ -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 _;
|
||||
|
||||
@@ -49,14 +49,8 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
)
|
||||
.unwrap();
|
||||
uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
|
||||
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
|
||||
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
@@ -88,23 +82,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
@@ -5,4 +5,4 @@ break main
|
||||
|
||||
load
|
||||
|
||||
continue
|
||||
# continue
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -17,7 +17,6 @@ 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::{
|
||||
BootMode,
|
||||
clocks::{
|
||||
@@ -31,6 +30,7 @@ use zynq7000_hal::{
|
||||
time::Hertz,
|
||||
uart::{ClockConfig, Config, Uart},
|
||||
};
|
||||
use zynq7000_hal::{generic_interrupt_handler, priv_tim};
|
||||
|
||||
// PS clock input frequency.
|
||||
const PS_CLK: Hertz = Hertz::from_raw(33_333_333);
|
||||
@@ -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;
|
||||
|
||||
@@ -109,17 +109,10 @@ fn main() -> ! {
|
||||
logger_uart
|
||||
.write_all(b"-- Zedboard Rust FSBL --\n\r")
|
||||
.unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
logger_uart,
|
||||
log::LevelFilter::Trace,
|
||||
false,
|
||||
)
|
||||
};
|
||||
zynq7000_hal::log::uart_blocking::init_with_busy_flag(logger_uart, log::LevelFilter::Trace, true);
|
||||
|
||||
// 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 +346,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)]
|
||||
|
||||
@@ -65,14 +65,7 @@ fn main() -> ! {
|
||||
)
|
||||
.unwrap();
|
||||
uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe {
|
||||
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
|
||||
uart,
|
||||
log::LevelFilter::Info,
|
||||
false,
|
||||
)
|
||||
};
|
||||
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Info, true);
|
||||
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -9,7 +12,7 @@ use aarch32_cpu::interrupt;
|
||||
use zynq7000::gic::{
|
||||
CpuInterfaceRegisters, DistributorControlRegister, DistributorRegisters, InterfaceControl,
|
||||
InterruptProcessorTargetRegister, InterruptSignalRegister, MmioCpuInterfaceRegisters,
|
||||
MmioDistributorRegisters, PriorityRegister,
|
||||
MmioDistributorRegisters, PriorityRegister, SoftwareGeneratedInterruptRegister,
|
||||
};
|
||||
|
||||
/// Spurious interrupt ID.
|
||||
@@ -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.
|
||||
@@ -230,12 +235,41 @@ pub enum SpiInterrupt {
|
||||
ScuParity = 92,
|
||||
}
|
||||
|
||||
/// Interrupt ID wrapper.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
/// SGI interrupt ID wrapper.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct SgiInterrupt(usize);
|
||||
|
||||
impl SgiInterrupt {
|
||||
/// Create a new SGI interrupt ID wrapper.
|
||||
pub const fn new(int_id: usize) -> Result<Self, InvalidSgiInterruptId> {
|
||||
if int_id >= 16 {
|
||||
return Err(InvalidSgiInterruptId(int_id));
|
||||
}
|
||||
Ok(SgiInterrupt(int_id))
|
||||
}
|
||||
|
||||
/// Returns the raw interrupt ID of the SGI.
|
||||
#[inline]
|
||||
pub const fn raw_id(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Returns the raw interrupt ID of the SGI as [u4].
|
||||
#[inline]
|
||||
pub const fn as_u4(&self) -> u4 {
|
||||
u4::new(self.0 as u8)
|
||||
}
|
||||
}
|
||||
|
||||
/// Interrupt ID wrapper.
|
||||
#[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),
|
||||
Sgi(SgiInterrupt),
|
||||
/// Private peripheral interrupt (PPI).
|
||||
Ppi(PpiInterrupt),
|
||||
/// Shared peripheral interrupt (SPI).
|
||||
@@ -247,8 +281,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 +369,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 +386,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 +400,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() },
|
||||
}
|
||||
@@ -491,7 +526,29 @@ impl GicConfigurator {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Utility function to set all SGI interrupt targets to CPU0.
|
||||
/// Set the interrupt priority for a SGI interrupt.
|
||||
///
|
||||
/// There are 32 priority levels, and a lower value means a higher priority.
|
||||
#[inline]
|
||||
pub fn set_sgi_interrupt_priority(&mut self, sgi: SgiInterrupt, priority: u5) {
|
||||
// The IPR arrays are byte accessible.
|
||||
let base_ptr = self.gicd.pointer_to_ipr_start() as *mut u8;
|
||||
let raw_index = sgi.raw_id();
|
||||
unsafe {
|
||||
core::ptr::write_volatile(base_ptr.add(raw_index), priority.as_u8() << 3);
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the interrupt priority for a SGI interrupt.
|
||||
#[inline]
|
||||
pub fn read_sgi_interrupt_priority(&mut self, sgi: SgiInterrupt) -> u5 {
|
||||
// The IPR arrays are byte accessible.
|
||||
let base_ptr = self.gicd.pointer_to_ipr_start() as *const u8;
|
||||
let raw_index = sgi.raw_id();
|
||||
unsafe { u5::new(core::ptr::read_volatile(base_ptr.add(raw_index)) >> 3) }
|
||||
}
|
||||
|
||||
/// Utility function to set all SPI interrupt targets to CPU0.
|
||||
///
|
||||
/// This does not clear interrupt target bits for CPU1, it only activates the interrupts for
|
||||
/// CPU 0 as well.
|
||||
@@ -510,6 +567,25 @@ impl GicConfigurator {
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility function to set all SGI interrupt targets to CPU0.
|
||||
///
|
||||
/// This does not clear interrupt target bits for CPU1, it only activates the interrupts for
|
||||
/// CPU 0 as well.
|
||||
/// This is useful if only CPU0 is active in a system, or if CPU0 handles most interrupts in
|
||||
/// the system.
|
||||
#[inline]
|
||||
pub fn set_all_sgi_interrupt_targets_cpu0(&mut self) {
|
||||
for i in 0..4 {
|
||||
self.gicd
|
||||
.modify_iptr_sgi(i, |v| {
|
||||
InterruptProcessorTargetRegister::new_with_raw_value(
|
||||
v.raw_value() | TARGETS_ALL_CPU_0_IPTR_VAL.raw_value(),
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable a specific SGI interrupt.
|
||||
#[inline]
|
||||
pub fn enable_sgi_interrupt(&mut self, int_id: usize) -> Result<(), InvalidSpiInterruptId> {
|
||||
@@ -554,6 +630,19 @@ impl GicConfigurator {
|
||||
})
|
||||
};
|
||||
}
|
||||
/// Enable all PPI interrupts.
|
||||
#[inline]
|
||||
pub fn trigger_software_interrupt(&mut self, sgi_id: SgiInterrupt) {
|
||||
self.gicd.write_sgir(
|
||||
SoftwareGeneratedInterruptRegister::builder()
|
||||
.with_target_list_filter(zynq7000::gic::TargetListFilter::SendToSelf)
|
||||
.with_security_condition(zynq7000::gic::SecurityCondition::IfConfiguredAsSecure)
|
||||
.with_sbz(u11::ZERO)
|
||||
.with_cpu_target_list(0)
|
||||
.with_interrupt_id(u4::new(sgi_id.raw_id() as u8))
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Enable specific SPI interrupt.
|
||||
#[inline]
|
||||
@@ -648,26 +737,42 @@ 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),
|
||||
27..=31 => Interrupt::Ppi(PpiInterrupt::try_from(int_id as u8).unwrap()),
|
||||
32..=92 => Interrupt::Spi(SpiInterrupt::try_from(int_id as u8).unwrap()),
|
||||
0..=15 => Interrupt::Sgi(
|
||||
SgiInterrupt::new(int_id as usize).expect("invalid SGI interrupt number"),
|
||||
),
|
||||
27..=31 => Interrupt::Ppi(
|
||||
PpiInterrupt::try_from(int_id as u8).expect("invalid PPI interrupt number"),
|
||||
),
|
||||
32..=92 => Interrupt::Spi(
|
||||
SpiInterrupt::try_from(int_id as u8).expect("invalid SPI interrupt number"),
|
||||
),
|
||||
SPURIOUS_INTERRUPT_ID => Interrupt::Spurious,
|
||||
_ => Interrupt::Invalid(int_id as usize),
|
||||
};
|
||||
@@ -678,16 +783,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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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),
|
||||
|
||||
@@ -12,10 +12,9 @@ static LOG_SEL: AtomicU8 = AtomicU8::new(0);
|
||||
/// Blocking UART loggers.
|
||||
pub mod uart_blocking {
|
||||
use super::*;
|
||||
use core::cell::{Cell, RefCell, UnsafeCell};
|
||||
use core::cell::{RefCell, UnsafeCell};
|
||||
use embedded_io::Write as _;
|
||||
|
||||
use aarch32_cpu::register::Cpsr;
|
||||
use critical_section::Mutex;
|
||||
use log::{LevelFilter, Log, set_logger, set_max_level};
|
||||
|
||||
@@ -78,28 +77,64 @@ pub mod uart_blocking {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UartLoggerUnsafeSingleThread {
|
||||
skip_in_isr: Cell<bool>,
|
||||
pub struct UartLoggerWithBusyFlag {
|
||||
busy: AtomicBool,
|
||||
skip_in_isr: AtomicBool,
|
||||
uart: UnsafeCell<Option<Uart>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for UartLoggerUnsafeSingleThread {}
|
||||
unsafe impl Sync for UartLoggerUnsafeSingleThread {}
|
||||
unsafe impl Send for UartLoggerWithBusyFlag {}
|
||||
unsafe impl Sync for UartLoggerWithBusyFlag {}
|
||||
|
||||
static UART_LOGGER_UNSAFE_SINGLE_THREAD: UartLoggerUnsafeSingleThread =
|
||||
UartLoggerUnsafeSingleThread {
|
||||
skip_in_isr: Cell::new(false),
|
||||
uart: UnsafeCell::new(None),
|
||||
};
|
||||
static UART_LOGGER_UNSAFE_SINGLE_THREAD: UartLoggerWithBusyFlag = UartLoggerWithBusyFlag {
|
||||
busy: AtomicBool::new(false),
|
||||
skip_in_isr: AtomicBool::new(false),
|
||||
uart: UnsafeCell::new(None),
|
||||
};
|
||||
|
||||
/// Initialize the logger with a blocking UART instance which does not use locks.
|
||||
struct UartGuard<'lock>(&'lock AtomicBool);
|
||||
|
||||
impl<'lock> UartGuard<'lock> {
|
||||
pub fn new(flag: &'lock AtomicBool) -> Option<Self> {
|
||||
let proc_mode = aarch32_cpu::register::Cpsr::read().mode().ok()?;
|
||||
|
||||
let is_irq = proc_mode == aarch32_cpu::register::cpsr::ProcessorMode::Fiq
|
||||
|| proc_mode == aarch32_cpu::register::cpsr::ProcessorMode::Irq;
|
||||
|
||||
// For IRQs, only try once.
|
||||
if is_irq {
|
||||
if UART_LOGGER_UNSAFE_SINGLE_THREAD
|
||||
.skip_in_isr
|
||||
.load(core::sync::atomic::Ordering::Relaxed)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
if flag.swap(true, core::sync::atomic::Ordering::AcqRel) {
|
||||
return None;
|
||||
}
|
||||
return Some(Self(flag));
|
||||
}
|
||||
|
||||
// For threaded code, spinning is allowed.
|
||||
while flag.swap(true, core::sync::atomic::Ordering::AcqRel) {}
|
||||
Some(Self(flag))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for UartGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
self.0.store(false, core::sync::atomic::Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the logger with a blocking UART instance which spins on a busy flag in threaded
|
||||
/// mode, and does not log in interrupt contexts if the main task was busy with logging.
|
||||
///
|
||||
/// # Safety
|
||||
/// It should be noted that this is still a blocking logger, and using it in an ISR might
|
||||
/// invalidate application logic and introduce problematic delays in the system.
|
||||
///
|
||||
/// This is a blocking logger which performs a write WITHOUT a critical section. This logger is
|
||||
/// NOT thread-safe, which might lead to garbled output. Log output in ISRs can optionally be
|
||||
/// surpressed.
|
||||
pub unsafe fn init_unsafe_single_core(uart: Uart, level: LevelFilter, skip_in_isr: bool) {
|
||||
/// Therefore, the initialization also allows skipping logging in ISRs completely.
|
||||
pub fn init_with_busy_flag(uart: Uart, level: LevelFilter, skip_in_isr: bool) {
|
||||
if LOGGER_INIT_DONE.swap(true, core::sync::atomic::Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
@@ -109,34 +144,31 @@ pub mod uart_blocking {
|
||||
);
|
||||
let opt_uart = unsafe { &mut *UART_LOGGER_UNSAFE_SINGLE_THREAD.uart.get() };
|
||||
opt_uart.replace(uart);
|
||||
|
||||
UART_LOGGER_UNSAFE_SINGLE_THREAD
|
||||
.skip_in_isr
|
||||
.set(skip_in_isr);
|
||||
.store(skip_in_isr, core::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
set_logger(&UART_LOGGER_UNSAFE_SINGLE_THREAD).unwrap();
|
||||
set_max_level(level); // Adjust as needed
|
||||
}
|
||||
|
||||
impl log::Log for UartLoggerUnsafeSingleThread {
|
||||
impl log::Log for UartLoggerWithBusyFlag {
|
||||
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &log::Record) {
|
||||
if self.skip_in_isr.get() {
|
||||
match Cpsr::read().mode().unwrap() {
|
||||
aarch32_cpu::register::cpsr::ProcessorMode::Fiq
|
||||
| aarch32_cpu::register::cpsr::ProcessorMode::Irq => {
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let guard = UartGuard::new(&self.busy);
|
||||
if guard.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let uart_mut = unsafe { &mut *self.uart.get() }.as_mut();
|
||||
if uart_mut.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
writeln!(
|
||||
uart_mut.unwrap(),
|
||||
"{} - {}\r",
|
||||
@@ -147,6 +179,11 @@ pub mod uart_blocking {
|
||||
}
|
||||
|
||||
fn flush(&self) {
|
||||
let guard = UartGuard::new(&self.busy);
|
||||
if guard.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let uart_mut = unsafe { &mut *self.uart.get() }.as_mut();
|
||||
if uart_mut.is_none() {
|
||||
return;
|
||||
@@ -165,96 +202,103 @@ 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},
|
||||
};
|
||||
|
||||
use crate::uart::TxAsync;
|
||||
|
||||
/// 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_generic(
|
||||
level: LevelFilter,
|
||||
) -> 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
|
||||
Some(reader)
|
||||
}
|
||||
|
||||
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 init_with_uart_tx(level: LevelFilter, tx: TxAsync) -> Option<UartLoggerRunner> {
|
||||
init_generic(level).map(|reader| UartLoggerRunner { reader, tx })
|
||||
}
|
||||
|
||||
pub fn get_frame_queue()
|
||||
-> &'static embassy_sync::channel::Channel<CriticalSectionRawMutex, usize, 32> {
|
||||
LOGGER_RB.frame_queue()
|
||||
pub struct UartLoggerRunner {
|
||||
reader: embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>,
|
||||
tx: TxAsync,
|
||||
}
|
||||
|
||||
impl UartLoggerRunner {
|
||||
pub async fn run(&mut self) -> ! {
|
||||
let mut log_buf = [0u8; 1024];
|
||||
|
||||
loop {
|
||||
let read_bytes = self.reader.read(&mut log_buf).await;
|
||||
if read_bytes > 0 {
|
||||
self.tx.write(&log_buf[..read_bytes]).unwrap().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1210,7 +1212,7 @@ pub fn initialize_card(
|
||||
}
|
||||
|
||||
let responded_to_cmd8 = !status_cmd8.0.command_timeout_error();
|
||||
let acmd41_arg = if responded_to_cmd8 {
|
||||
let hsc = if responded_to_cmd8 {
|
||||
let r7 = response::R7::new_with_raw_value(ll.read_u32_response());
|
||||
if !r7.voltage_accepted().is_ok_and(|val| {
|
||||
val == embedded_sdmmc::sdcard::argument::VoltageSuppliedSelect::_2_7To3_6V
|
||||
@@ -1221,31 +1223,26 @@ pub fn initialize_card(
|
||||
error: InitializationError::ResponseError(status.response_errors()),
|
||||
});
|
||||
}
|
||||
argument::Acmd41::builder()
|
||||
.with_host_capacity_support(
|
||||
embedded_sdmmc::sdcard::argument::HostCapacitySupport::SdhcOrSdxc,
|
||||
)
|
||||
.with_fast_boot(false)
|
||||
.with_xpc(embedded_sdmmc::sdcard::argument::PowerControl::MaximumPerformance)
|
||||
.with_s18r(false)
|
||||
.with_ocr(VOLTAGE_LEVEL_CAPABILITIES)
|
||||
.build()
|
||||
embedded_sdmmc::sdcard::argument::HostCapacitySupport::SdhcOrSdxc
|
||||
} else {
|
||||
embedded_sdmmc::sdcard::argument::HostCapacitySupport::SdscOnly
|
||||
};
|
||||
send_acmd(
|
||||
ll,
|
||||
commands::ACMD41_SEND_IF_COND,
|
||||
argument::Acmd41::builder()
|
||||
.with_host_capacity_support(
|
||||
embedded_sdmmc::sdcard::argument::HostCapacitySupport::SdscOnly,
|
||||
)
|
||||
.with_host_capacity_support(hsc)
|
||||
.with_fast_boot(false)
|
||||
.with_xpc(embedded_sdmmc::sdcard::argument::PowerControl::MaximumPerformance)
|
||||
.with_s18r(false)
|
||||
.with_ocr(VOLTAGE_LEVEL_CAPABILITIES)
|
||||
.build()
|
||||
};
|
||||
send_acmd(ll, commands::ACMD41_SEND_IF_COND, acmd41_arg.raw_value(), 0).map_err(|e| {
|
||||
InitializationErrorWithStep {
|
||||
step: InitStep::SendingIfCondAcmd41,
|
||||
error: e,
|
||||
}
|
||||
.raw_value(),
|
||||
0,
|
||||
)
|
||||
.map_err(|e| InitializationErrorWithStep {
|
||||
step: InitStep::SendingIfCondAcmd41,
|
||||
error: e,
|
||||
})?;
|
||||
|
||||
let mut r3 = embedded_sdmmc::sdcard::response::R3::new_with_raw_value(ll.read_u32_response());
|
||||
|
||||
@@ -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();
|
||||
let index = peripheral as usize;
|
||||
let enabled_irqs = spi.read_enabled_interrupts();
|
||||
// Prevent spurious interrupts from messing with out logic here.
|
||||
spi.disable_interrupts_master_mode();
|
||||
let interrupt_status = spi.read_interrupt_status();
|
||||
spi.clear_interrupts_master_mode();
|
||||
// IRQ is not related.
|
||||
if !imr.tx_trig() && !imr.tx_full() && !imr.tx_underflow() && !imr.rx_ovr() && !imr.rx_full() {
|
||||
if !enabled_irqs.tx_below_threshold()
|
||||
&& !enabled_irqs.tx_full()
|
||||
&& !enabled_irqs.tx_underflow()
|
||||
&& !enabled_irqs.rx_ovr()
|
||||
&& !enabled_irqs.rx_full()
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Prevent spurious interrupts from messing with out logic here.
|
||||
spi.disable_interrupts();
|
||||
let isr = spi.read_isr();
|
||||
spi.clear_interrupts();
|
||||
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,15 @@ fn on_interrupt_transfer(
|
||||
spi.write_fifo_unchecked(0);
|
||||
}
|
||||
context.tx_progress += 1;
|
||||
isr = spi.read_isr();
|
||||
}
|
||||
|
||||
while context.rx_progress < transfer_len && 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 +171,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 +204,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 +220,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 +228,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_master_mode(tx_pending);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -242,6 +269,7 @@ pub struct TransferContext {
|
||||
rx_progress: usize,
|
||||
tx_slice: RawBufSlice,
|
||||
rx_slice: RawBufSliceMut,
|
||||
rx_overrun: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
@@ -253,173 +281,208 @@ 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.clear_interrupts_master_mode();
|
||||
spi.inner
|
||||
.enable_interrupts_master_mode(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.clear_interrupts_master_mode();
|
||||
spi.inner
|
||||
.enable_interrupts_master_mode(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);
|
||||
|
||||
// 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::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.clear_interrupts_master_mode();
|
||||
spi.inner
|
||||
.enable_interrupts_master_mode(full_write_len > FIFO_DEPTH);
|
||||
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.clear_interrupts_master_mode();
|
||||
spi.inner
|
||||
.enable_interrupts_master_mode(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();
|
||||
spi.inner.disable_interrupts_master_mode();
|
||||
}
|
||||
|
||||
// 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 +490,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 +524,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 +647,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 +661,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;
|
||||
|
||||
@@ -17,11 +17,12 @@ use crate::{enable_amba_peripheral_clock, spi_mode_const_to_cpol_cpha};
|
||||
use crate::{clocks::IoClocks, slcr::Slcr, time::Hertz};
|
||||
use arbitrary_int::{prelude::*, u3, u4, u6};
|
||||
use embedded_hal::delay::DelayNs;
|
||||
pub use embedded_hal::spi::Mode;
|
||||
pub use embedded_hal::spi::{MODE_0, MODE_1, MODE_2, MODE_3, 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;
|
||||
@@ -30,12 +31,40 @@ pub const MODULE_ID: u32 = 0x90106;
|
||||
pub mod asynch;
|
||||
pub use asynch::*;
|
||||
|
||||
pub mod slave;
|
||||
pub use slave::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SpiId {
|
||||
Spi0 = 0,
|
||||
Spi1 = 1,
|
||||
}
|
||||
|
||||
impl SpiId {
|
||||
#[inline]
|
||||
pub const fn interrupt_id(&self) -> crate::Interrupt {
|
||||
match self {
|
||||
SpiId::Spi0 => crate::Interrupt::Spi(crate::SpiInterrupt::Spi0),
|
||||
SpiId::Spi1 => crate::Interrupt::Spi(crate::SpiInterrupt::Spi1),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unsafely steal the register block.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This API can be used to potentially create a driver to the same peripheral structure
|
||||
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
|
||||
/// interfere with each other.
|
||||
#[inline]
|
||||
pub unsafe fn steal_regs(&self) -> MmioRegisters<'static> {
|
||||
match self {
|
||||
SpiId::Spi0 => unsafe { zynq7000::spi::Registers::new_mmio_fixed_0() },
|
||||
SpiId::Spi1 => unsafe { zynq7000::spi::Registers::new_mmio_fixed_1() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PsSpi {
|
||||
fn reg_block(&self) -> MmioRegisters<'static>;
|
||||
fn id(&self) -> Option<SpiId>;
|
||||
@@ -348,17 +377,56 @@ impl ChipSelect {
|
||||
}
|
||||
}
|
||||
|
||||
/// This abstraction which can be used to map a hardware chip select pin
|
||||
/// to [embedded_hal::digital::OutputPin]. This is useful for creating physical chip select
|
||||
/// pins required by the [embedded_hal_bus](https://docs.rs/embedded-hal-bus/latest/embedded_hal_bus/)
|
||||
/// API.
|
||||
pub struct ChipSelectPin {
|
||||
spi_id: SpiId,
|
||||
cs: ChipSelect,
|
||||
}
|
||||
|
||||
impl ChipSelectPin {
|
||||
/// Chip select pin constructor.
|
||||
pub const fn new(spi_id: SpiId, cs: ChipSelect) -> Self {
|
||||
Self { spi_id, cs }
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::ErrorType for ChipSelectPin {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::OutputPin for ChipSelectPin {
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
// Safety: We only touch the CS register bits of the specified peripheral.
|
||||
let mut spi_regs = unsafe { SpiLowLevel::steal(self.spi_id).regs };
|
||||
spi_regs.modify_config(|val| val.with_cs_raw(self.cs.raw_reg()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
// Safety: We only touch the CS register bits of the specified peripheral.
|
||||
let mut spi_regs = unsafe { SpiLowLevel::steal(self.spi_id).regs };
|
||||
spi_regs.modify_config(|val| val.with_cs_raw(u4::MAX));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
/// 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 +447,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 +515,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 +552,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 +562,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 +579,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)
|
||||
@@ -492,7 +597,7 @@ impl SpiLowLevel {
|
||||
.with_baud_rate_div(config.baud_div)
|
||||
.with_cpha(cpha)
|
||||
.with_cpol(cpol)
|
||||
.with_master_ern(true)
|
||||
.with_mode(zynq7000::spi::Mode::Master)
|
||||
.build(),
|
||||
);
|
||||
// Configures for polling mode by default: TX trigger by one will lead to the
|
||||
@@ -520,30 +625,27 @@ impl SpiLowLevel {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn write_fifo_unchecked(&mut self, data: u8) {
|
||||
self.regs.write_txd(FifoWrite::new(data));
|
||||
self.regs.write_tx_data(FifoWrite::new(data));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_fifo_unchecked(&mut self) -> u8 {
|
||||
self.regs.read_rxd().value()
|
||||
self.regs.read_rx_data().value()
|
||||
}
|
||||
|
||||
#[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 +671,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(
|
||||
pub fn disable_interrupts_master_mode(&mut self) {
|
||||
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,44 +696,73 @@ 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_master_mode(&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(),
|
||||
);
|
||||
}
|
||||
|
||||
/// This enables all interrupts relevant for non-blocking interrupt driven SPI operation
|
||||
/// in SPI slave mode.
|
||||
#[inline]
|
||||
pub fn enable_interrupts_slave_mode(&mut self, mode_fault: 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_below_threshold(true)
|
||||
.with_mode_fault(mode_fault)
|
||||
.with_rx_ovr(true)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
|
||||
/// This clears all interrupts relevant for non-blocking interrupt driven SPI operation
|
||||
/// in SPI master mode.
|
||||
#[inline]
|
||||
pub fn clear_interrupts(&mut self) {
|
||||
self.regs.write_isr(
|
||||
pub fn clear_interrupts_master_mode(&mut self) {
|
||||
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 +791,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 +808,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 +832,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 +866,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 +878,6 @@ impl Spi {
|
||||
Ss2: SsPin,
|
||||
>(
|
||||
spi: impl PsSpi,
|
||||
clocks: &IoClocks,
|
||||
config: Config,
|
||||
spi_pins: (Sck, Mosi, Miso),
|
||||
ss_pins: (Ss0, Ss1, Ss2),
|
||||
@@ -793,36 +910,50 @@ 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
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn id(&self) -> SpiId {
|
||||
self.inner.id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn interrupt_id(&self) -> crate::Interrupt {
|
||||
self.inner.id.interrupt_id()
|
||||
}
|
||||
|
||||
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 +969,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 +987,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 +1003,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 +1011,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 +1033,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 +1046,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 +1088,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 +1113,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 +1123,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 +1140,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 +1261,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 +1275,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 +1299,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| {
|
||||
@@ -1191,3 +1342,14 @@ pub fn configure_spi_ref_clk(clks: &mut Clocks, divisor: u6) {
|
||||
};
|
||||
clks.io_clocks_mut().update_spi_clk(new_clk);
|
||||
}
|
||||
|
||||
/// Connects SPI0 output signals to SPI1 input signals and vice-versa.
|
||||
#[inline]
|
||||
pub fn enable_spi0_to_spi1_loopback() {
|
||||
// Safety: We only modify the SPI bit.
|
||||
unsafe {
|
||||
Slcr::with(|slcr| {
|
||||
slcr.modify_mio_loopback(|val| val.with_spi0_loop_spi1(true));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
use zynq7000::spi::{Config, FifoWrite, MmioRegisters};
|
||||
|
||||
use crate::{
|
||||
enable_amba_peripheral_clock,
|
||||
spi::{SpiId, SpiLowLevel},
|
||||
spi_mode_const_to_cpol_cpha,
|
||||
};
|
||||
|
||||
pub struct SpiSlave(pub SpiLowLevel);
|
||||
|
||||
pub struct SlaveConfig {
|
||||
pub mode: embedded_hal::spi::Mode,
|
||||
pub enable_modefail: bool,
|
||||
}
|
||||
|
||||
impl SpiSlave {
|
||||
/// Creates a very simple SPI slave driver.
|
||||
///
|
||||
/// The SPI slave will not start in the enabled state to allow pre-loading the FIFO.
|
||||
pub fn new(id: SpiId, regs: MmioRegisters<'static>, config: SlaveConfig) -> 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 (cpol, cpha) = spi_mode_const_to_cpol_cpha(config.mode);
|
||||
ll.write_config(
|
||||
Config::ZERO
|
||||
.with_modefail_gen_en(config.enable_modefail)
|
||||
.with_cpha(cpha)
|
||||
.with_cpol(cpol),
|
||||
);
|
||||
Self(ll)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn id(&self) -> SpiId {
|
||||
self.0.id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn interrupt_id(&self) -> crate::Interrupt {
|
||||
self.id().interrupt_id()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable_interrupts(&mut self, mode_error: bool) {
|
||||
self.0.enable_interrupts_slave_mode(mode_error);
|
||||
}
|
||||
|
||||
/// Enable the SPI slave. This will allow it to respond to transfers initiated by the master.
|
||||
///
|
||||
/// Please refer to the word detection in the TRM p.568 for more details.
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
self.0.enable();
|
||||
}
|
||||
|
||||
/// This register is used for IDLE state detection on the SCLK line.
|
||||
///
|
||||
/// Please refer to the word detection in the TRM p.568 for more details how this is useful.
|
||||
#[inline]
|
||||
pub fn write_slave_idle_counter(&mut self, count: u8) {
|
||||
self.0.write_sicr(count as u32);
|
||||
}
|
||||
|
||||
/// Write data to the TX FIFO, which allows pre-loading data which will be sent via the
|
||||
/// MISO line when the master initiates a transfer.
|
||||
#[inline]
|
||||
pub fn write_tx_data(&mut self, data: u8) {
|
||||
self.0.write_tx_data(FifoWrite::new(data));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -66,6 +66,32 @@ pub enum UartId {
|
||||
Uart1 = 1,
|
||||
}
|
||||
|
||||
impl UartId {
|
||||
/// Interrupt ID.
|
||||
#[inline]
|
||||
pub const fn interrupt_id(&self) -> crate::Interrupt {
|
||||
match self {
|
||||
UartId::Uart0 => crate::Interrupt::Spi(crate::SpiInterrupt::Uart0),
|
||||
UartId::Uart1 => crate::Interrupt::Spi(crate::SpiInterrupt::Uart1),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unsafely steal the register block.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This API can be used to potentially create a driver to the same peripheral structure
|
||||
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
|
||||
/// interfere with each other.
|
||||
#[inline]
|
||||
pub unsafe fn steal_regs(&self) -> MmioRegisters<'static> {
|
||||
match self {
|
||||
UartId::Uart0 => unsafe { zynq7000::uart::Registers::new_mmio_fixed_0() },
|
||||
UartId::Uart1 => unsafe { zynq7000::uart::Registers::new_mmio_fixed_1() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Common trait for PS UART peripherals.
|
||||
pub trait PsUart {
|
||||
/// UART register block.
|
||||
@@ -254,8 +280,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 +316,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 +395,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,7 +492,6 @@ impl Config {
|
||||
pub struct Uart {
|
||||
rx: Rx,
|
||||
tx: Tx,
|
||||
cfg: Config,
|
||||
}
|
||||
|
||||
/// Invalid PS UART error.
|
||||
@@ -565,35 +590,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 +635,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 +647,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,16 +661,34 @@ impl Uart {
|
||||
},
|
||||
tx: Tx {
|
||||
regs: reg_block,
|
||||
idx: uart_id,
|
||||
id: uart_id,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Steal a UART without doing ANY configuration.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Circumvents ownership and safety guarantees by the HAL. Also, the driver will not work
|
||||
/// unless the UART was configured beforehand.
|
||||
pub unsafe fn steal(id: UartId) -> Uart {
|
||||
let reg_block = unsafe { id.steal_regs() };
|
||||
Self {
|
||||
rx: Rx {
|
||||
regs: unsafe { reg_block.clone() },
|
||||
},
|
||||
tx: Tx {
|
||||
regs: reg_block,
|
||||
id,
|
||||
},
|
||||
cfg,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
});
|
||||
@@ -652,12 +700,6 @@ impl Uart {
|
||||
&mut self.rx.regs
|
||||
}
|
||||
|
||||
/// Configuration.
|
||||
#[inline]
|
||||
pub const fn cfg(&self) -> &Config {
|
||||
&self.cfg
|
||||
}
|
||||
|
||||
/// Split into TX and RX halves.
|
||||
#[inline]
|
||||
pub const fn split(self) -> (Tx, Rx) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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"
|
||||
|
||||
@@ -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
|
||||
@@ -46,18 +46,20 @@ impl TypeRegister {
|
||||
|
||||
pub type Typer = TypeRegister;
|
||||
|
||||
#[bitbybit::bitfield(u32, debug)]
|
||||
#[bitbybit::bitfield(
|
||||
u32,
|
||||
debug,
|
||||
default = 0x0,
|
||||
defmt_bitfields(feature = "defmt"),
|
||||
forbid_overlaps
|
||||
)]
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct InterruptProcessorTargetRegister {
|
||||
/// Target array. Every register holds the information for 4 interrupts.
|
||||
#[bits(0..=1, rw, stride = 8)]
|
||||
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 +94,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 +104,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 +118,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 +185,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,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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -98,6 +98,24 @@ pub struct LevelShifterRegister {
|
||||
user_lvl_shftr_en: Option<LevelShifterConfig>,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(
|
||||
u32,
|
||||
default = 0,
|
||||
debug,
|
||||
defmt_fields(feature = "defmt"),
|
||||
forbid_overlaps
|
||||
)]
|
||||
pub struct MioLoopback {
|
||||
#[bit(3, rw)]
|
||||
i2c0_loop_i2c1: bool,
|
||||
#[bit(2, rw)]
|
||||
can0_loop_can1: bool,
|
||||
#[bit(1, rw)]
|
||||
ua0_loop_ua1: bool,
|
||||
#[bit(0, rw)]
|
||||
spi0_loop_spi1: bool,
|
||||
}
|
||||
|
||||
/// System Level Control Registers access.
|
||||
#[derive(derive_mmio::Mmio)]
|
||||
#[repr(C)]
|
||||
@@ -158,7 +176,7 @@ pub struct Registers {
|
||||
|
||||
_gap10: [u32; 0x0B],
|
||||
|
||||
mio_loopback: u32,
|
||||
mio_loopback: MioLoopback,
|
||||
_gap11: u32,
|
||||
mio_mst_tri_0: u32,
|
||||
mio_mst_tri_1: u32,
|
||||
|
||||
@@ -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))]
|
||||
@@ -34,13 +34,15 @@ impl BaudDivSel {
|
||||
}
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(
|
||||
u32,
|
||||
default = 0x0,
|
||||
debug,
|
||||
defmt_bitfields(feature = "defmt"),
|
||||
forbid_overlaps
|
||||
)]
|
||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Mode {
|
||||
Slave = 0,
|
||||
Master = 1,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0, debug, defmt_bitfields(feature = "defmt"))]
|
||||
pub struct Config {
|
||||
#[bit(17, rw)]
|
||||
modefail_gen_en: bool,
|
||||
@@ -70,6 +72,9 @@ pub struct Config {
|
||||
cpol: SpiClockPolarity,
|
||||
/// Master mode enable. 1 is master mode.
|
||||
#[bit(0, rw)]
|
||||
mode: Mode,
|
||||
// Deprecated access to bit 0.
|
||||
#[bit(0, rw)]
|
||||
master_ern: bool,
|
||||
}
|
||||
|
||||
@@ -87,15 +92,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 +122,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.
|
||||
@@ -123,18 +132,33 @@ pub struct InterruptControl {
|
||||
rx_ovr: bool,
|
||||
}
|
||||
|
||||
impl InterruptControl {
|
||||
pub const ALL: Self = Self::builder()
|
||||
.with_tx_underflow(true)
|
||||
.with_rx_full(true)
|
||||
.with_rx_not_empty(true)
|
||||
.with_tx_full(true)
|
||||
.with_tx_below_threshold(true)
|
||||
.with_mode_fault(true)
|
||||
.with_rx_ovr(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
#[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,24 +230,24 @@ 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)]
|
||||
txd: FifoWrite,
|
||||
tx_data: FifoWrite,
|
||||
#[mmio(Read)]
|
||||
rxd: FifoRead,
|
||||
rx_data: FifoRead,
|
||||
sicr: u32,
|
||||
tx_trig: u32,
|
||||
rx_trig: u32,
|
||||
|
||||
@@ -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],
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Generated
+1
-64
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
@@ -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 {} {
|
||||
|
||||
@@ -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 ""} {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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,186 @@ 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_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
|
||||
|
||||
@@ -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]];
|
||||
|
||||
Reference in New Issue
Block a user