Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 18b6ee2c41 | |||
| dfe68131c2 | |||
| af23c47fc7 | |||
| 32e0e27ca7 | |||
| bdc4780bcc | |||
| 7df10e6ea1 | |||
| 306ef90094 | |||
| fcd971a7d3 | |||
| c09d75b602 | |||
| c72ba780d4 |
@@ -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" }
|
||||
|
||||
|
||||
@@ -28,7 +28,10 @@ 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"] }
|
||||
|
||||
@@ -72,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;
|
||||
|
||||
|
||||
@@ -51,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);
|
||||
|
||||
@@ -8,7 +8,7 @@ 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,
|
||||
@@ -19,7 +19,7 @@ use zynq7000_hal::{
|
||||
gtc::GlobalTimerCounter,
|
||||
l2_cache,
|
||||
time::Hertz,
|
||||
uart::{ClockConfig, Config, TxAsync, Uart},
|
||||
uart::{self, ClockConfig, Config, TxAsync, Uart},
|
||||
};
|
||||
|
||||
use zynq7000_rt as _;
|
||||
@@ -69,9 +69,10 @@ async fn main(spawner: Spawner) -> ! {
|
||||
uart.flush().unwrap();
|
||||
|
||||
let (tx, _rx) = uart.split();
|
||||
let mut logger = TxAsync::new(tx, true);
|
||||
let logger = TxAsync::new(tx, true);
|
||||
|
||||
let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace).unwrap();
|
||||
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);
|
||||
@@ -80,14 +81,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
spawner.spawn(led_task(led).unwrap());
|
||||
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;
|
||||
}
|
||||
}
|
||||
log_runner.run().await
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
@@ -153,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 {}
|
||||
}
|
||||
@@ -79,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);
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
@@ -60,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 {
|
||||
|
||||
@@ -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:?}");
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
@@ -242,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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
use aarch32_cpu::asm::nop;
|
||||
use core::panic::PanicInfo;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_time::{Delay, Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_hal_async::delay::DelayNs;
|
||||
@@ -26,6 +25,7 @@ use zynq7000_hal::{
|
||||
gpio::{GpioPins, Output, PinState},
|
||||
gtc::GlobalTimerCounter,
|
||||
l2_cache,
|
||||
log::asynch::UartLoggerRunner,
|
||||
spi::{self, SpiAsync, SpiWithHwCs, SpiWithHwCsAsync},
|
||||
time::Hertz,
|
||||
uart::{self, TxAsync},
|
||||
@@ -97,7 +97,10 @@ async fn main(spawner: Spawner) -> ! {
|
||||
uart.write_all(b"-- Zynq 7000 Zedboard SPI L3GD20H example --\n\r")
|
||||
.unwrap();
|
||||
|
||||
let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace).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).unwrap();
|
||||
|
||||
let boot_mode = BootMode::new_from_regs();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
@@ -116,7 +119,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
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::AutoCsAutoStart,
|
||||
),
|
||||
@@ -163,28 +166,17 @@ async fn main(spawner: Spawner) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
spawner.spawn(logger_task(uart, log_reader).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,
|
||||
reader: embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>,
|
||||
) -> ! {
|
||||
let (tx, _) = uart.split();
|
||||
let mut tx_async = TxAsync::new(tx, true);
|
||||
let mut log_buf: [u8; 2048] = [0; 2048];
|
||||
loop {
|
||||
let read_bytes = reader.read(&mut log_buf).await;
|
||||
if read_bytes > 0 {
|
||||
tx_async.write(&log_buf[0..read_bytes]).unwrap().await;
|
||||
}
|
||||
}
|
||||
pub async fn logger_task(mut log_runner: UartLoggerRunner) -> ! {
|
||||
log_runner.run().await
|
||||
}
|
||||
|
||||
pub async fn blocking_application(
|
||||
|
||||
@@ -5,18 +5,18 @@ use aarch32_cpu::asm::nop;
|
||||
use core::panic::PanicInfo;
|
||||
use dummy_pin::DummyPin;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_time::{Delay, Duration, Ticker};
|
||||
use embedded_graphics::{Drawable as _, geometry::Point};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_hal_async::delay::DelayNs as _;
|
||||
use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use 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},
|
||||
@@ -27,7 +27,7 @@ use tinybmp::Bmp;
|
||||
|
||||
use zynq7000_rt as _;
|
||||
|
||||
const INIT_STRING: &str = "-- Zynq 7000 Zedboard OLED example --\n\r";
|
||||
const INIT_STRING: &str = "-- Zynq 7000 Zedboard Async OLED example --\n\r";
|
||||
|
||||
/// Entry point which calls the embassy main method.
|
||||
#[zynq7000_rt::entry]
|
||||
@@ -77,9 +77,11 @@ async fn main(spawner: Spawner) -> ! {
|
||||
uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||
uart.flush().unwrap();
|
||||
|
||||
// Safety: We are not multi-threaded yet.
|
||||
let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace)
|
||||
.expect("Failed to initialize async logger");
|
||||
let (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);
|
||||
@@ -88,7 +90,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
clocks.io_clocks().spi_clk()
|
||||
);
|
||||
|
||||
spawner.spawn(logger_task(uart, log_reader).unwrap());
|
||||
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] = [
|
||||
@@ -216,19 +218,8 @@ impl FerrisMovement {
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn logger_task(
|
||||
uart: uart::Uart,
|
||||
reader: embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>,
|
||||
) -> ! {
|
||||
let (tx, _) = uart.split();
|
||||
let mut tx_async = TxAsync::new(tx, true);
|
||||
let mut log_buf: [u8; 2048] = [0; 2048];
|
||||
loop {
|
||||
let read_bytes = reader.read(&mut log_buf).await;
|
||||
if read_bytes > 0 {
|
||||
tx_async.write(&log_buf[0..read_bytes]).unwrap().await;
|
||||
}
|
||||
}
|
||||
pub async fn logger_task(mut log_runner: UartLoggerRunner) -> ! {
|
||||
log_runner.run().await
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
@@ -279,6 +270,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 {}
|
||||
}
|
||||
|
||||
@@ -5,18 +5,19 @@ use aarch32_cpu::asm::nop;
|
||||
use core::panic::PanicInfo;
|
||||
use dummy_pin::DummyPin;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_time::{Delay, Duration, Ticker};
|
||||
use embedded_graphics::{Drawable as _, geometry::Point};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_hal_async::delay::DelayNs as _;
|
||||
use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use log::info;
|
||||
use ssd1306::{Ssd1306, prelude::*};
|
||||
use zedboard::PS_CLOCK_FREQUENCY;
|
||||
use zynq7000_hal::{
|
||||
BootMode, clocks, generic_interrupt_handler, gpio, gtc, spi,
|
||||
BootMode, clocks, generic_interrupt_handler, gpio, gtc,
|
||||
log::asynch::UartLoggerRunner,
|
||||
spi,
|
||||
time::Hertz,
|
||||
uart::{self, TxAsync},
|
||||
};
|
||||
@@ -76,9 +77,12 @@ async fn main(spawner: Spawner) -> ! {
|
||||
uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||
uart.flush().unwrap();
|
||||
|
||||
// Safety: We are not multi-threaded yet.
|
||||
let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace)
|
||||
.expect("Failed to initialize async logger");
|
||||
let (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);
|
||||
@@ -87,8 +91,6 @@ async fn main(spawner: Spawner) -> ! {
|
||||
clocks.io_clocks().spi_clk()
|
||||
);
|
||||
|
||||
spawner.spawn(logger_task(uart, log_reader).unwrap());
|
||||
|
||||
let mio_led = gpio::Output::new_for_mio(gpio_pins.mio.mio7, gpio::PinState::Low);
|
||||
let emio_leds: [gpio::Output; 8] = [
|
||||
gpio::Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), gpio::PinState::Low),
|
||||
@@ -211,19 +213,8 @@ impl FerrisMovement {
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn logger_task(
|
||||
uart: uart::Uart,
|
||||
reader: embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>,
|
||||
) -> ! {
|
||||
let (tx, _) = uart.split();
|
||||
let mut tx_async = TxAsync::new(tx, true);
|
||||
let mut log_buf: [u8; 2048] = [0; 2048];
|
||||
loop {
|
||||
let read_bytes = reader.read(&mut log_buf).await;
|
||||
if read_bytes > 0 {
|
||||
tx_async.write(&log_buf[0..read_bytes]).unwrap().await;
|
||||
}
|
||||
}
|
||||
pub async fn logger_task(mut log_runner: UartLoggerRunner) -> ! {
|
||||
log_runner.run().await
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
@@ -274,6 +265,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 {}
|
||||
}
|
||||
|
||||
@@ -62,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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
@@ -129,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 =
|
||||
|
||||
@@ -37,7 +37,7 @@ 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,
|
||||
@@ -47,9 +47,10 @@ use zynq7000_hal::{
|
||||
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
|
||||
@@ -224,14 +233,7 @@ async fn main(spawner: Spawner) -> ! {
|
||||
on_interrupt_uart_0,
|
||||
);
|
||||
|
||||
// Safety: We are not multi-threaded yet.
|
||||
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);
|
||||
|
||||
// Set up UART multiplexing before creating and configuring the UARTs.
|
||||
let mut uart_mux = UartMultiplexer::new([
|
||||
@@ -239,15 +241,25 @@ 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
|
||||
@@ -256,10 +268,15 @@ async fn main(spawner: Spawner) -> ! {
|
||||
|
||||
let (clk_config, error) = axi_uart16550::ClockConfig::new_autocalc_with_error(
|
||||
fugit_03::HertzU32::from_raw(clocks.pl_clocks()[0].to_raw()),
|
||||
115200,
|
||||
uart_speed,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(error < 0.02);
|
||||
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,
|
||||
@@ -289,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| {
|
||||
@@ -560,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 {}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -10,7 +10,7 @@ keywords = ["no-std", "zedboard", "bare-metal", "amd", "zynq7000"]
|
||||
categories = ["embedded", "no-std", "hardware-support"]
|
||||
|
||||
[dependencies]
|
||||
zynq7000 = { path = "../zynq7000", version = "0.3" }
|
||||
zynq7000 = { path = "../zynq7000", version = "0.4" }
|
||||
zynq7000-hal = { path = "../zynq7000-hal", version = "0.1" }
|
||||
bitbybit = "2"
|
||||
log = "0.4"
|
||||
|
||||
@@ -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::{generic_interrupt_handler, 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);
|
||||
@@ -109,14 +109,7 @@ 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::Configurator::new_with_init(periphs.gicc, periphs.gicd);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -22,6 +22,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 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
|
||||
|
||||
@@ -12,8 +12,8 @@ categories = ["embedded", "no-std", "hardware-support"]
|
||||
|
||||
[dependencies]
|
||||
aarch32-cpu = { version = "0.3" }
|
||||
zynq7000 = { path = "../zynq7000", version = "0.3" }
|
||||
zynq7000-mmu = { path = "../zynq7000-mmu", version = "0.1" }
|
||||
zynq7000 = { path = "../zynq7000", version = "0.4" }
|
||||
zynq7000-mmu = { path = "../zynq7000-mmu", version = "0.2" }
|
||||
static_assertions = "1.1"
|
||||
bitbybit = "2"
|
||||
arbitrary-int = "2"
|
||||
|
||||
@@ -12,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.
|
||||
@@ -235,13 +235,41 @@ pub enum SpiInterrupt {
|
||||
ScuParity = 92,
|
||||
}
|
||||
|
||||
/// 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).
|
||||
@@ -498,7 +526,29 @@ impl Configurator {
|
||||
.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.
|
||||
@@ -517,6 +567,25 @@ impl Configurator {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
@@ -561,6 +630,19 @@ impl Configurator {
|
||||
})
|
||||
};
|
||||
}
|
||||
/// 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]
|
||||
@@ -682,9 +764,15 @@ impl InterruptGuard {
|
||||
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),
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
@@ -172,6 +209,8 @@ pub mod asynch {
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use log::{LevelFilter, set_logger, set_max_level};
|
||||
|
||||
use crate::uart::TxAsync;
|
||||
|
||||
/// Logger implementation which logs frames via a ring buffer and sends the frame sizes
|
||||
/// as messages.
|
||||
///
|
||||
@@ -228,9 +267,8 @@ pub mod asynch {
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
pub fn init(
|
||||
pub fn init_generic(
|
||||
level: LevelFilter,
|
||||
//writer: embassy_sync::pipe::Writer<'static, CriticalSectionRawMutex, 4096>,
|
||||
) -> Option<embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>> {
|
||||
if super::LOGGER_INIT_DONE.swap(true, core::sync::atomic::Ordering::Relaxed) {
|
||||
return None;
|
||||
@@ -241,4 +279,26 @@ pub mod asynch {
|
||||
set_max_level(level); // Adjust as needed
|
||||
Some(reader)
|
||||
}
|
||||
|
||||
pub fn init_with_uart_tx(level: LevelFilter, tx: TxAsync) -> Option<UartLoggerRunner> {
|
||||
init_generic(level).map(|reader| UartLoggerRunner { reader, tx })
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1212,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
|
||||
@@ -1223,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());
|
||||
|
||||
@@ -38,9 +38,9 @@ pub unsafe fn on_interrupt(peripheral: SpiId) {
|
||||
let index = peripheral as usize;
|
||||
let enabled_irqs = spi.read_enabled_interrupts();
|
||||
// Prevent spurious interrupts from messing with out logic here.
|
||||
spi.disable_interrupts();
|
||||
spi.disable_interrupts_master_mode();
|
||||
let interrupt_status = spi.read_interrupt_status();
|
||||
spi.clear_interrupts();
|
||||
spi.clear_interrupts_master_mode();
|
||||
// IRQ is not related.
|
||||
if !enabled_irqs.tx_below_threshold()
|
||||
&& !enabled_irqs.tx_full()
|
||||
@@ -155,8 +155,7 @@ fn on_interrupt_transfer(idx: usize, context: &mut TransferContext, spi: &mut Sp
|
||||
context.tx_progress += 1;
|
||||
}
|
||||
|
||||
// Read data from RX FIFO first.
|
||||
while spi.read_interrupt_status().rx_not_empty() {
|
||||
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 {
|
||||
@@ -252,7 +251,7 @@ fn unfinished_transfer(spi: &mut SpiLowLevel, transfer_len: usize, context: &Tra
|
||||
}
|
||||
// Re-enable interrupts with the new RX FIFO trigger level. Only enable TX threshold interrupt
|
||||
// if we are not done yet with pushing all bytes to the FIFO.
|
||||
spi.enable_interrupts(tx_pending);
|
||||
spi.enable_interrupts_master_mode(tx_pending);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -308,6 +307,7 @@ impl<'spi> SpiFuture<'spi> {
|
||||
});
|
||||
|
||||
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();
|
||||
@@ -322,8 +322,9 @@ impl<'spi> SpiFuture<'spi> {
|
||||
context.tx_slice.set_null();
|
||||
context.tx_progress = write_index;
|
||||
context.rx_progress = 0;
|
||||
spi.inner.clear_interrupts();
|
||||
spi.inner.enable_interrupts(write_index < words.len());
|
||||
spi.inner.clear_interrupts_master_mode();
|
||||
spi.inner
|
||||
.enable_interrupts_master_mode(write_index < words.len());
|
||||
spi.inner.enable();
|
||||
});
|
||||
Self {
|
||||
@@ -349,8 +350,9 @@ impl<'spi> SpiFuture<'spi> {
|
||||
context.rx_slice.set_null();
|
||||
context.tx_progress = write_index;
|
||||
context.rx_progress = 0;
|
||||
spi.inner.clear_interrupts();
|
||||
spi.inner.enable_interrupts(write_index < words.len());
|
||||
spi.inner.clear_interrupts_master_mode();
|
||||
spi.inner
|
||||
.enable_interrupts_master_mode(write_index < words.len());
|
||||
spi.inner.enable();
|
||||
});
|
||||
Self {
|
||||
@@ -376,6 +378,11 @@ impl<'spi> SpiFuture<'spi> {
|
||||
}
|
||||
|
||||
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[spi_id as usize].borrow(cs);
|
||||
let mut context = context_ref.borrow_mut();
|
||||
@@ -386,8 +393,9 @@ impl<'spi> SpiFuture<'spi> {
|
||||
}
|
||||
context.tx_progress = fifo_prefill;
|
||||
context.rx_progress = 0;
|
||||
spi.inner.clear_interrupts();
|
||||
spi.inner.enable_interrupts(fifo_prefill < write.len());
|
||||
spi.inner.clear_interrupts_master_mode();
|
||||
spi.inner
|
||||
.enable_interrupts_master_mode(full_write_len > FIFO_DEPTH);
|
||||
spi.inner.enable();
|
||||
});
|
||||
Self {
|
||||
@@ -413,8 +421,9 @@ impl<'spi> SpiFuture<'spi> {
|
||||
context.tx_slice.set_null();
|
||||
context.tx_progress = write_index;
|
||||
context.rx_progress = 0;
|
||||
spi.inner.clear_interrupts();
|
||||
spi.inner.enable_interrupts(write_index < words.len());
|
||||
spi.inner.clear_interrupts_master_mode();
|
||||
spi.inner
|
||||
.enable_interrupts_master_mode(write_index < words.len());
|
||||
spi.inner.enable();
|
||||
});
|
||||
Self {
|
||||
@@ -429,7 +438,7 @@ impl<'spi> SpiFuture<'spi> {
|
||||
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.
|
||||
|
||||
@@ -17,7 +17,7 @@ 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::{
|
||||
@@ -31,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>;
|
||||
@@ -349,6 +377,42 @@ 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 {
|
||||
@@ -533,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
|
||||
@@ -561,12 +625,12 @@ 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]
|
||||
@@ -615,7 +679,7 @@ impl SpiLowLevel {
|
||||
/// This disables all interrupts relevant for non-blocking interrupt driven SPI operation
|
||||
/// in SPI master mode.
|
||||
#[inline]
|
||||
pub fn disable_interrupts(&mut self) {
|
||||
pub fn disable_interrupts_master_mode(&mut self) {
|
||||
self.regs.write_interupt_disable(
|
||||
InterruptControl::builder()
|
||||
.with_tx_underflow(true)
|
||||
@@ -632,7 +696,7 @@ 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, tx_below_threshold: bool) {
|
||||
pub fn enable_interrupts_master_mode(&mut self, tx_below_threshold: bool) {
|
||||
self.regs.write_interrupt_enable(
|
||||
InterruptControl::builder()
|
||||
.with_tx_underflow(true)
|
||||
@@ -646,10 +710,27 @@ impl SpiLowLevel {
|
||||
);
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
pub fn clear_interrupts_master_mode(&mut self) {
|
||||
self.regs.write_interrupt_status(
|
||||
InterruptStatus::builder()
|
||||
.with_tx_underflow(true)
|
||||
@@ -859,6 +940,16 @@ impl Spi {
|
||||
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);
|
||||
}
|
||||
@@ -1251,3 +1342,14 @@ pub fn configure_spi_ref_clock_with_divisor(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));
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -466,7 +492,6 @@ impl Config {
|
||||
pub struct Uart {
|
||||
rx: Rx,
|
||||
tx: Tx,
|
||||
cfg: Config,
|
||||
}
|
||||
|
||||
/// Invalid PS UART error.
|
||||
@@ -576,31 +601,27 @@ impl Uart {
|
||||
}
|
||||
enable_amba_peripheral_clock(periph_sel);
|
||||
reset(uart_id);
|
||||
reg_block.modify_control(|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_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_mode(mode);
|
||||
reg_block.write_baudgen(
|
||||
@@ -615,8 +636,8 @@ impl Uart {
|
||||
);
|
||||
// Soft reset for both TX and RX.
|
||||
reg_block.modify_control(|mut v| {
|
||||
v.set_tx_rst(true);
|
||||
v.set_rx_rst(true);
|
||||
v.set_tx_reset(true);
|
||||
v.set_rx_reset(true);
|
||||
v
|
||||
});
|
||||
|
||||
@@ -627,10 +648,10 @@ impl Uart {
|
||||
|
||||
// Enable TX and RX.
|
||||
reg_block.modify_control(|mut v| {
|
||||
v.set_tx_dis(false);
|
||||
v.set_rx_dis(false);
|
||||
v.set_tx_en(true);
|
||||
v.set_rx_en(true);
|
||||
v.set_tx_disable(false);
|
||||
v.set_rx_disable(false);
|
||||
v.set_tx_enable(true);
|
||||
v.set_rx_enable(true);
|
||||
v
|
||||
});
|
||||
|
||||
@@ -642,7 +663,25 @@ impl Uart {
|
||||
regs: reg_block,
|
||||
id: uart_id,
|
||||
},
|
||||
cfg,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,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) {
|
||||
|
||||
@@ -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_control(|mut cr| {
|
||||
cr.set_rx_rst(true);
|
||||
cr.set_rx_reset(true);
|
||||
cr
|
||||
});
|
||||
while self.regs.read_control().rx_rst() {}
|
||||
while self.regs.read_control().rx_reset() {}
|
||||
}
|
||||
|
||||
/// Helper function to start the interrupt driven reception of data.
|
||||
@@ -114,8 +114,14 @@ 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();
|
||||
}
|
||||
@@ -135,7 +141,7 @@ impl Rx {
|
||||
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)
|
||||
@@ -145,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(),
|
||||
);
|
||||
}
|
||||
@@ -161,7 +167,7 @@ impl Rx {
|
||||
let mut result = RxInterruptResult::default();
|
||||
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()
|
||||
@@ -170,15 +176,9 @@ impl Rx {
|
||||
return result;
|
||||
}
|
||||
let isr = self.regs.read_interrupt_status();
|
||||
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() {
|
||||
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();
|
||||
});
|
||||
@@ -204,7 +204,7 @@ impl Rx {
|
||||
// Handle timeout event.
|
||||
if isr.rx_timeout() && reset_rx_timeout {
|
||||
self.regs.modify_control(|mut cr| {
|
||||
cr.set_rstto(true);
|
||||
cr.set_restart_timeout(true);
|
||||
cr
|
||||
});
|
||||
}
|
||||
@@ -229,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(),
|
||||
);
|
||||
}
|
||||
@@ -262,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -62,8 +62,8 @@ impl Tx {
|
||||
self.soft_reset();
|
||||
}
|
||||
self.regs.modify_control(|mut val| {
|
||||
val.set_tx_en(true);
|
||||
val.set_tx_dis(false);
|
||||
val.set_tx_enable(true);
|
||||
val.set_tx_disable(false);
|
||||
val
|
||||
});
|
||||
}
|
||||
@@ -72,8 +72,8 @@ impl Tx {
|
||||
#[inline]
|
||||
pub fn disable(&mut self) {
|
||||
self.regs.modify_control(|mut val| {
|
||||
val.set_tx_en(false);
|
||||
val.set_tx_dis(true);
|
||||
val.set_tx_enable(false);
|
||||
val.set_tx_disable(true);
|
||||
val
|
||||
});
|
||||
}
|
||||
@@ -82,11 +82,11 @@ impl Tx {
|
||||
#[inline]
|
||||
pub fn soft_reset(&mut self) {
|
||||
self.regs.modify_control(|mut val| {
|
||||
val.set_tx_rst(true);
|
||||
val.set_tx_reset(true);
|
||||
val
|
||||
});
|
||||
loop {
|
||||
if !self.regs.read_control().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.
|
||||
@@ -110,7 +110,7 @@ impl Tx {
|
||||
InterruptControl::builder()
|
||||
.with_tx_over(true)
|
||||
.with_tx_near_full(false)
|
||||
.with_tx_trig(tx_trig)
|
||||
.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(),
|
||||
);
|
||||
}
|
||||
@@ -132,7 +132,7 @@ impl Tx {
|
||||
InterruptControl::builder()
|
||||
.with_tx_over(true)
|
||||
.with_tx_near_full(false)
|
||||
.with_tx_trig(true)
|
||||
.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(),
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ pub unsafe fn on_interrupt_tx(peripheral: UartId) {
|
||||
|
||||
// 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
|
||||
@@ -84,7 +84,7 @@ pub unsafe fn on_interrupt_tx(peripheral: UartId) {
|
||||
if remaining > FIFO_DEPTH {
|
||||
tx_with_irq.regs.write_tx_fifo_trigger(
|
||||
FifoTrigger::builder()
|
||||
.with_trig(u6::new((FIFO_DEPTH / 2) as u8))
|
||||
.with_trigger(u6::new((FIFO_DEPTH / 2) as u8))
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
@@ -148,7 +148,7 @@ impl<'uart> TxFuture<'uart> {
|
||||
if data.len() > FIFO_DEPTH {
|
||||
tx_with_irq.regs.write_tx_fifo_trigger(
|
||||
FifoTrigger::builder()
|
||||
.with_trig(u6::new((FIFO_DEPTH / 2) as u8))
|
||||
.with_trigger(u6::new((FIFO_DEPTH / 2) as u8))
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
@@ -14,7 +14,7 @@ categories = ["embedded", "no-std", "hardware-support"]
|
||||
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" }
|
||||
|
||||
@@ -8,6 +8,11 @@ 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.
|
||||
@@ -30,7 +35,8 @@ Documentation fix
|
||||
|
||||
Initial release
|
||||
|
||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/compare/zynq7000-v0.3.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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "zynq7000"
|
||||
version = "0.3.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)]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -127,6 +132,18 @@ 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 InterruptEnabled {
|
||||
#[bit(6, r)]
|
||||
@@ -228,9 +245,9 @@ pub struct Registers {
|
||||
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,
|
||||
|
||||
@@ -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,7 +218,7 @@ pub struct InterruptControl {
|
||||
#[bit(1, w)]
|
||||
rx_empty: bool,
|
||||
#[bit(0, w)]
|
||||
rx_trg: bool,
|
||||
rx_trigger: bool,
|
||||
}
|
||||
|
||||
#[bitbybit::bitfield(u32, default = 0x0)]
|
||||
@@ -226,7 +226,7 @@ pub struct InterruptControl {
|
||||
#[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()
|
||||
}
|
||||
}
|
||||
@@ -343,16 +343,16 @@ pub struct Registers {
|
||||
/// 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
-1
@@ -647,7 +647,7 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "zynq7000"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"arbitrary-int 2.0.0",
|
||||
"bitbybit 2.0.0",
|
||||
|
||||
@@ -741,7 +741,6 @@ proc create_root_design { parentCell } {
|
||||
connect_bd_net -net ilconstant_0_dout [get_bd_pins ilconstant_0/dout] \
|
||||
[get_bd_pins EMIO_I_0/In2]
|
||||
connect_bd_net -net ilconstant_2_dout [get_bd_pins ilconstant_2/dout] \
|
||||
[get_bd_pins processing_system7_0/SPI0_MISO_I] \
|
||||
[get_bd_pins processing_system7_0/SPI0_SS_I]
|
||||
connect_bd_net -net ilconstant_3_dout [get_bd_pins ilconstant_3/dout] \
|
||||
[get_bd_pins processing_system7_0/SPI0_MOSI_I] \
|
||||
|
||||
Reference in New Issue
Block a user