Compare commits

..

2 Commits

Author SHA1 Message Date
meierj 5acb8043bf fixed merge conflicts
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
2026-05-17 15:55:05 +02:00
meierj a9c5c8de57 function to invalidate l2cache
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (push) Has been cancelled
2026-05-17 12:49:41 +02:00
39 changed files with 284 additions and 1737 deletions
-9
View File
@@ -11,7 +11,6 @@ members = [
"examples/embassy",
"examples/zedboard",
"examples/defmt",
"examples/multiprio",
"zedboard-bsp",
"zedboard-qspi-flasher",
@@ -41,11 +40,3 @@ 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" }
+1 -4
View File
@@ -28,10 +28,7 @@ fugit = "0.4"
log = "0.4"
embassy-executor = { version = "0.10", features = [
"platform-z7",
"platform-cortex-ar",
"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,7 +72,13 @@ async fn main(_spawner: Spawner) -> ! {
uart.write_all(b"-- Zynq 7000 DHT22 --\n\r").unwrap();
// Safety: We are not multi-threaded yet.
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
let mut delay = Delay;
@@ -51,7 +51,14 @@ async fn main(_spawner: Spawner) -> ! {
.unwrap();
uart.write_all(b"-- Zynq 7000 Embassy Hello World --\n\r")
.unwrap();
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
// 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);
@@ -8,7 +8,7 @@ use embassy_executor::Spawner;
use embassy_time::{Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use log::info;
use log::{error, info};
use zynq7000::Peripherals;
use zynq7000_hal::{
BootMode,
@@ -19,7 +19,7 @@ use zynq7000_hal::{
gtc::GlobalTimerCounter,
l2_cache,
time::Hertz,
uart::{self, ClockConfig, Config, TxAsync, Uart},
uart::{ClockConfig, Config, TxAsync, Uart},
};
use zynq7000_rt as _;
@@ -69,10 +69,9 @@ async fn main(spawner: Spawner) -> ! {
uart.flush().unwrap();
let (tx, _rx) = uart.split();
let logger = TxAsync::new(tx, true);
let mut logger = TxAsync::new(tx, true);
let mut log_runner =
zynq7000_hal::log::asynch::init_with_uart_tx(log::LevelFilter::Trace, logger).unwrap();
let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace).unwrap();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
@@ -81,7 +80,14 @@ async fn main(spawner: Spawner) -> ! {
spawner.spawn(led_task(led).unwrap());
spawner.spawn(hello_task().unwrap());
log_runner.run().await
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;
}
}
}
#[embassy_executor::task]
@@ -147,7 +153,6 @@ fn prefetch_handler(_faulting_addr: usize) -> ! {
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
let mut uart = unsafe { uart::Uart::steal(uart::UartId::Uart1) };
writeln!(uart, "panic: {}\r", info).ok();
error!("Panic: {info:?}");
loop {}
}
@@ -1,183 +0,0 @@
#![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 {}
}
+7 -1
View File
@@ -79,7 +79,13 @@ async fn main(_spawner: Spawner) -> ! {
.unwrap();
uart.write_all(b"-- Zynq 7000 PWM example--\n\r").unwrap();
// Safety: We are not multi-threaded yet.
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
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);
-35
View File
@@ -1,35 +0,0 @@
[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",
]}
-31
View File
@@ -1,31 +0,0 @@
//! 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");
}
-25
View File
@@ -1,25 +0,0 @@
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
}
-170
View File
@@ -1,170 +0,0 @@
#![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,7 +60,14 @@ fn main() -> ! {
.unwrap();
uart.write_all(b"-- Zynq 7000 GTC Ticks example --\n\r")
.unwrap();
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
loop {
+7 -1
View File
@@ -60,7 +60,13 @@ fn main() -> ! {
uart.write_all(b"-- Zynq 7000 Logging example --\n\r")
.unwrap();
// Safety: We are not multi-threaded yet.
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
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:?}");
-123
View File
@@ -1,123 +0,0 @@
//! 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.
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, LOG_LEVEL, false);
unsafe { zynq7000_hal::log::uart_blocking::init_unsafe_single_core(uart, LOG_LEVEL, false) };
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
@@ -80,8 +80,14 @@ async fn main(_spawner: Spawner) -> ! {
.unwrap();
uart.write_all(b"-- Zynq 7000 Zedboard I2C L3GD20H example --\n\r")
.unwrap();
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
// 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);
@@ -12,6 +12,7 @@
use aarch32_cpu::asm::nop;
use core::panic::PanicInfo;
use embassy_executor::Spawner;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_time::{Delay, Duration, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_hal_async::delay::DelayNs;
@@ -25,7 +26,6 @@ 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,10 +97,7 @@ async fn main(spawner: Spawner) -> ! {
uart.write_all(b"-- Zynq 7000 Zedboard SPI L3GD20H example --\n\r")
.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 log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace).unwrap();
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
@@ -119,7 +116,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,
),
@@ -166,17 +163,28 @@ async fn main(spawner: Spawner) -> ! {
}
}
spawner.spawn(logger_task(log_runner).unwrap());
spawner.spawn(logger_task(uart, log_reader).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(mut log_runner: UartLoggerRunner) -> ! {
log_runner.run().await
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 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::info;
use log::{error, 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 Async OLED example --\n\r";
const INIT_STRING: &str = "-- Zynq 7000 Zedboard OLED example --\n\r";
/// Entry point which calls the embassy main method.
#[zynq7000_rt::entry]
@@ -77,11 +77,9 @@ async fn main(spawner: Spawner) -> ! {
uart.write_all(INIT_STRING.as_bytes()).unwrap();
uart.flush().unwrap();
let (tx, _) = uart.split();
let tx_async = TxAsync::new(tx, true);
let log_runner =
zynq7000_hal::log::asynch::init_with_uart_tx(log::LevelFilter::Trace, tx_async)
.expect("Failed to initialize async logger");
// Safety: We are not multi-threaded yet.
let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace)
.expect("Failed to initialize async logger");
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
@@ -90,7 +88,7 @@ async fn main(spawner: Spawner) -> ! {
clocks.io_clocks().spi_clk()
);
spawner.spawn(logger_task(log_runner).unwrap());
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] = [
@@ -218,8 +216,19 @@ impl FerrisMovement {
}
#[embassy_executor::task]
pub async fn logger_task(mut log_runner: UartLoggerRunner) -> ! {
log_runner.run().await
pub async fn logger_task(
uart: uart::Uart,
reader: embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>,
) -> ! {
let (tx, _) = uart.split();
let mut tx_async = TxAsync::new(tx, true);
let mut log_buf: [u8; 2048] = [0; 2048];
loop {
let read_bytes = reader.read(&mut log_buf).await;
if read_bytes > 0 {
tx_async.write(&log_buf[0..read_bytes]).unwrap().await;
}
}
}
#[embassy_executor::task]
@@ -270,7 +279,6 @@ fn prefetch_handler(_faulting_addr: usize) -> ! {
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
let mut uart = unsafe { uart::Uart::steal(uart::UartId::Uart1) };
writeln!(uart, "panic: {}\r", info).ok();
error!("Panic: {info:?}");
loop {}
}
+22 -14
View File
@@ -5,19 +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::info;
use log::{error, info};
use ssd1306::{Ssd1306, prelude::*};
use zedboard::PS_CLOCK_FREQUENCY;
use zynq7000_hal::{
BootMode, clocks, generic_interrupt_handler, gpio, gtc,
log::asynch::UartLoggerRunner,
spi,
BootMode, clocks, generic_interrupt_handler, gpio, gtc, spi,
time::Hertz,
uart::{self, TxAsync},
};
@@ -77,12 +76,9 @@ async fn main(spawner: Spawner) -> ! {
uart.write_all(INIT_STRING.as_bytes()).unwrap();
uart.flush().unwrap();
let (tx, _) = uart.split();
let tx_async = TxAsync::new(tx, true);
let log_runner =
zynq7000_hal::log::asynch::init_with_uart_tx(log::LevelFilter::Trace, tx_async)
.expect("Failed to initialize async logger");
spawner.spawn(logger_task(log_runner).unwrap());
// Safety: We are not multi-threaded yet.
let log_reader = zynq7000_hal::log::asynch::init(log::LevelFilter::Trace)
.expect("Failed to initialize async logger");
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
@@ -91,6 +87,8 @@ 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),
@@ -213,8 +211,19 @@ impl FerrisMovement {
}
#[embassy_executor::task]
pub async fn logger_task(mut log_runner: UartLoggerRunner) -> ! {
log_runner.run().await
pub async fn logger_task(
uart: uart::Uart,
reader: embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>,
) -> ! {
let (tx, _) = uart.split();
let mut tx_async = TxAsync::new(tx, true);
let mut log_buf: [u8; 2048] = [0; 2048];
loop {
let read_bytes = reader.read(&mut log_buf).await;
if read_bytes > 0 {
tx_async.write(&log_buf[0..read_bytes]).unwrap().await;
}
}
}
#[embassy_executor::task]
@@ -265,7 +274,6 @@ fn prefetch_handler(_faulting_addr: usize) -> ! {
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
let mut uart = unsafe { uart::Uart::steal(uart::UartId::Uart1) };
writeln!(uart, "panic: {}\r", info).ok();
error!("Panic: {info:?}");
loop {}
}
+8 -1
View File
@@ -62,7 +62,14 @@ async fn main(_spawner: Spawner) -> ! {
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
// 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);
+7 -1
View File
@@ -67,7 +67,13 @@ async fn main(_spawner: Spawner) -> ! {
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
// Safety: We are not multi-threaded yet.
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
let sdio_clock_config =
SdClockConfig::calculate_for_io_clock(clocks.io_clocks(), 100.MHz(), 10.MHz()).unwrap();
@@ -1,587 +0,0 @@
#![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,7 +129,13 @@ async fn main(_spawner: Spawner) -> ! {
log_uart.write_all(INIT_STRING.as_bytes()).unwrap();
// Safety: Co-operative multi-tasking is used.
zynq7000_hal::log::uart_blocking::init_with_busy_flag(log_uart, log::LevelFilter::Trace, false);
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
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::{info, warn};
use log::{error, info, warn};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
@@ -47,7 +47,7 @@ use zynq7000_hal::{
gtc::GlobalTimerCounter,
l2_cache,
time::Hertz,
uart::{self, ClockConfig, Config, Uart},
uart::{ClockConfig, Config, Uart},
};
#[derive(Debug, Copy, Clone, PartialEq)]
@@ -233,7 +233,14 @@ async fn main(spawner: Spawner) -> ! {
on_interrupt_uart_0,
);
zynq7000_hal::log::uart_blocking::init_with_busy_flag(log_uart, log::LevelFilter::Trace, false);
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
log_uart,
log::LevelFilter::Trace,
false,
)
};
// Set up UART multiplexing before creating and configuring the UARTs.
let mut uart_mux = UartMultiplexer::new([
@@ -577,7 +584,6 @@ fn prefetch_handler(_faulting_addr: usize) -> ! {
/// Panic handler
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
let mut uart = unsafe { uart::Uart::steal(uart::UartId::Uart1) };
writeln!(uart, "panic: {}\r", info).ok();
error!("Panic: {info:?}");
loop {}
}
+8 -2
View File
@@ -49,8 +49,14 @@ async fn main(_spawner: Spawner) -> ! {
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Trace, false);
// 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);
+9 -2
View File
@@ -17,6 +17,7 @@ use log::{error, info};
use zedboard_bsp::qspi_spansion::{self, QspiSpansionS25Fl256SLinearMode};
use zynq7000_boot_image::DestinationDevice;
use zynq7000_hal::clocks::ArmClocks;
use zynq7000_hal::{generic_interrupt_handler, priv_tim};
use zynq7000_hal::{
BootMode,
clocks::{
@@ -30,7 +31,6 @@ 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,7 +109,14 @@ fn main() -> ! {
logger_uart
.write_all(b"-- Zedboard Rust FSBL --\n\r")
.unwrap();
zynq7000_hal::log::uart_blocking::init_with_busy_flag(logger_uart, log::LevelFilter::Trace, true);
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
logger_uart,
log::LevelFilter::Trace,
false,
)
};
// Set up the global interrupt controller.
let mut gic = gic::Configurator::new_with_init(periphs.gicc, periphs.gicd);
+8 -1
View File
@@ -65,7 +65,14 @@ fn main() -> ! {
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
zynq7000_hal::log::uart_blocking::init_with_busy_flag(uart, log::LevelFilter::Info, true);
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Info,
false,
)
};
let boot_mode = BootMode::new_from_regs();
info!("Boot mode: {:?}", boot_mode);
+6 -94
View File
@@ -12,7 +12,7 @@ use aarch32_cpu::interrupt;
use zynq7000::gic::{
CpuInterfaceRegisters, DistributorControlRegister, DistributorRegisters, InterfaceControl,
InterruptProcessorTargetRegister, InterruptSignalRegister, MmioCpuInterfaceRegisters,
MmioDistributorRegisters, PriorityRegister, SoftwareGeneratedInterruptRegister,
MmioDistributorRegisters, PriorityRegister,
};
/// Spurious interrupt ID.
@@ -235,41 +235,13 @@ 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(SgiInterrupt),
Sgi(usize),
/// Private peripheral interrupt (PPI).
Ppi(PpiInterrupt),
/// Shared peripheral interrupt (SPI).
@@ -526,29 +498,7 @@ impl Configurator {
.unwrap();
}
/// 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.
/// 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.
@@ -567,25 +517,6 @@ 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> {
@@ -630,19 +561,6 @@ 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]
@@ -764,15 +682,9 @@ 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(
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"),
),
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()),
SPURIOUS_INTERRUPT_ID => Interrupt::Spurious,
_ => Interrupt::Invalid(int_id as usize),
};
+7
View File
@@ -88,3 +88,10 @@ pub fn disable() {
let mut l2c_mmio = unsafe { zynq7000::l2_cache::Registers::new_mmio_fixed() };
l2c_mmio.write_control(Control::new_disabled());
}
/// Function to invalidate l2 cache
pub fn invalidate_all(l2c_mmio: &mut MmioRegisters<'static>) {
l2c_mmio.write_clean_invalidate_by_way(0xffff);
while l2c_mmio.read_cache_sync().busy() {}
compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
+29 -89
View File
@@ -12,9 +12,10 @@ static LOG_SEL: AtomicU8 = AtomicU8::new(0);
/// Blocking UART loggers.
pub mod uart_blocking {
use super::*;
use core::cell::{RefCell, UnsafeCell};
use core::cell::{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};
@@ -77,64 +78,28 @@ pub mod uart_blocking {
}
}
pub struct UartLoggerWithBusyFlag {
busy: AtomicBool,
skip_in_isr: AtomicBool,
pub struct UartLoggerUnsafeSingleThread {
skip_in_isr: Cell<bool>,
uart: UnsafeCell<Option<Uart>>,
}
unsafe impl Send for UartLoggerWithBusyFlag {}
unsafe impl Sync for UartLoggerWithBusyFlag {}
unsafe impl Send for UartLoggerUnsafeSingleThread {}
unsafe impl Sync for UartLoggerUnsafeSingleThread {}
static UART_LOGGER_UNSAFE_SINGLE_THREAD: UartLoggerWithBusyFlag = UartLoggerWithBusyFlag {
busy: AtomicBool::new(false),
skip_in_isr: AtomicBool::new(false),
uart: UnsafeCell::new(None),
};
static UART_LOGGER_UNSAFE_SINGLE_THREAD: UartLoggerUnsafeSingleThread =
UartLoggerUnsafeSingleThread {
skip_in_isr: Cell::new(false),
uart: UnsafeCell::new(None),
};
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.
/// Initialize the logger with a blocking UART instance which does not use locks.
///
/// 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.
/// # Safety
///
/// Therefore, the initialization also allows skipping logging in ISRs completely.
pub fn init_with_busy_flag(uart: Uart, level: LevelFilter, skip_in_isr: bool) {
/// 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) {
if LOGGER_INIT_DONE.swap(true, core::sync::atomic::Ordering::Relaxed) {
return;
}
@@ -144,31 +109,34 @@ 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
.store(skip_in_isr, core::sync::atomic::Ordering::Relaxed);
.set(skip_in_isr);
set_logger(&UART_LOGGER_UNSAFE_SINGLE_THREAD).unwrap();
set_max_level(level); // Adjust as needed
}
impl log::Log for UartLoggerWithBusyFlag {
impl log::Log for UartLoggerUnsafeSingleThread {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}
fn log(&self, record: &log::Record) {
let guard = UartGuard::new(&self.busy);
if guard.is_none() {
return;
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 uart_mut = unsafe { &mut *self.uart.get() }.as_mut();
if uart_mut.is_none() {
return;
}
writeln!(
uart_mut.unwrap(),
"{} - {}\r",
@@ -179,11 +147,6 @@ 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;
@@ -209,8 +172,6 @@ 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.
///
@@ -267,8 +228,9 @@ pub mod asynch {
fn flush(&self) {}
}
pub fn init_generic(
pub fn init(
level: LevelFilter,
//writer: embassy_sync::pipe::Writer<'static, CriticalSectionRawMutex, 4096>,
) -> Option<embassy_sync::pipe::Reader<'static, CriticalSectionRawMutex, 4096>> {
if super::LOGGER_INIT_DONE.swap(true, core::sync::atomic::Ordering::Relaxed) {
return None;
@@ -279,26 +241,4 @@ 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;
}
}
}
}
}
+20 -15
View File
@@ -1212,7 +1212,7 @@ pub fn initialize_card(
}
let responded_to_cmd8 = !status_cmd8.0.command_timeout_error();
let hsc = if responded_to_cmd8 {
let acmd41_arg = 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,26 +1223,31 @@ pub fn initialize_card(
error: InitializationError::ResponseError(status.response_errors()),
});
}
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(hsc)
.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()
.raw_value(),
0,
)
.map_err(|e| InitializationErrorWithStep {
step: InitStep::SendingIfCondAcmd41,
error: e,
} else {
argument::Acmd41::builder()
.with_host_capacity_support(
embedded_sdmmc::sdcard::argument::HostCapacitySupport::SdscOnly,
)
.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,
}
})?;
let mut r3 = embedded_sdmmc::sdcard::response::R3::new_with_raw_value(ll.read_u32_response());
+14 -23
View File
@@ -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_master_mode();
spi.disable_interrupts();
let interrupt_status = spi.read_interrupt_status();
spi.clear_interrupts_master_mode();
spi.clear_interrupts();
// IRQ is not related.
if !enabled_irqs.tx_below_threshold()
&& !enabled_irqs.tx_full()
@@ -155,7 +155,8 @@ fn on_interrupt_transfer(idx: usize, context: &mut TransferContext, spi: &mut Sp
context.tx_progress += 1;
}
while context.rx_progress < transfer_len && spi.read_interrupt_status().rx_not_empty() {
// Read data from RX FIFO first.
while spi.read_interrupt_status().rx_not_empty() {
if context.rx_progress < read_len {
read_slice[context.rx_progress] = spi.read_fifo_unchecked();
} else {
@@ -251,7 +252,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_master_mode(tx_pending);
spi.enable_interrupts(tx_pending);
}
#[derive(Debug, Clone, Copy)]
@@ -307,7 +308,6 @@ 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,9 +322,8 @@ impl<'spi> SpiFuture<'spi> {
context.tx_slice.set_null();
context.tx_progress = write_index;
context.rx_progress = 0;
spi.inner.clear_interrupts_master_mode();
spi.inner
.enable_interrupts_master_mode(write_index < words.len());
spi.inner.clear_interrupts();
spi.inner.enable_interrupts(write_index < words.len());
spi.inner.enable();
});
Self {
@@ -350,9 +349,8 @@ impl<'spi> SpiFuture<'spi> {
context.rx_slice.set_null();
context.tx_progress = write_index;
context.rx_progress = 0;
spi.inner.clear_interrupts_master_mode();
spi.inner
.enable_interrupts_master_mode(write_index < words.len());
spi.inner.clear_interrupts();
spi.inner.enable_interrupts(write_index < words.len());
spi.inner.enable();
});
Self {
@@ -378,11 +376,6 @@ 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();
@@ -393,9 +386,8 @@ impl<'spi> SpiFuture<'spi> {
}
context.tx_progress = fifo_prefill;
context.rx_progress = 0;
spi.inner.clear_interrupts_master_mode();
spi.inner
.enable_interrupts_master_mode(full_write_len > FIFO_DEPTH);
spi.inner.clear_interrupts();
spi.inner.enable_interrupts(fifo_prefill < write.len());
spi.inner.enable();
});
Self {
@@ -421,9 +413,8 @@ impl<'spi> SpiFuture<'spi> {
context.tx_slice.set_null();
context.tx_progress = write_index;
context.rx_progress = 0;
spi.inner.clear_interrupts_master_mode();
spi.inner
.enable_interrupts_master_mode(write_index < words.len());
spi.inner.clear_interrupts();
spi.inner.enable_interrupts(write_index < words.len());
spi.inner.enable();
});
Self {
@@ -438,7 +429,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_master_mode();
spi.inner.disable_interrupts();
}
// Returns amount of bytes written to FIFO.
+7 -109
View File
@@ -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_0, MODE_1, MODE_2, MODE_3, Mode};
pub use embedded_hal::spi::Mode;
use zynq7000::slcr::reset::DualRefAndClockResetSpiUart;
pub use zynq7000::spi::DelayControl;
use zynq7000::spi::{
@@ -31,40 +31,12 @@ 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>;
@@ -377,42 +349,6 @@ 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 {
@@ -597,7 +533,7 @@ impl SpiLowLevel {
.with_baud_rate_div(config.baud_div)
.with_cpha(cpha)
.with_cpol(cpol)
.with_mode(zynq7000::spi::Mode::Master)
.with_master_ern(true)
.build(),
);
// Configures for polling mode by default: TX trigger by one will lead to the
@@ -625,12 +561,12 @@ impl SpiLowLevel {
#[inline(always)]
pub fn write_fifo_unchecked(&mut self, data: u8) {
self.regs.write_tx_data(FifoWrite::new(data));
self.regs.write_txd(FifoWrite::new(data));
}
#[inline(always)]
pub fn read_fifo_unchecked(&mut self) -> u8 {
self.regs.read_rx_data().value()
self.regs.read_rxd().value()
}
#[inline]
@@ -679,7 +615,7 @@ impl SpiLowLevel {
/// This disables all interrupts relevant for non-blocking interrupt driven SPI operation
/// in SPI master mode.
#[inline]
pub fn disable_interrupts_master_mode(&mut self) {
pub fn disable_interrupts(&mut self) {
self.regs.write_interupt_disable(
InterruptControl::builder()
.with_tx_underflow(true)
@@ -696,7 +632,7 @@ impl SpiLowLevel {
/// This enables all interrupts relevant for non-blocking interrupt driven SPI operation
/// in SPI master mode.
#[inline]
pub fn enable_interrupts_master_mode(&mut self, tx_below_threshold: bool) {
pub fn enable_interrupts(&mut self, tx_below_threshold: bool) {
self.regs.write_interrupt_enable(
InterruptControl::builder()
.with_tx_underflow(true)
@@ -710,27 +646,10 @@ 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_master_mode(&mut self) {
pub fn clear_interrupts(&mut self) {
self.regs.write_interrupt_status(
InterruptStatus::builder()
.with_tx_underflow(true)
@@ -940,16 +859,6 @@ 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);
}
@@ -1342,14 +1251,3 @@ 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));
});
}
}
-76
View File
@@ -1,76 +0,0 @@
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));
}
}
+8 -45
View File
@@ -66,32 +66,6 @@ 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.
@@ -492,6 +466,7 @@ impl Config {
pub struct Uart {
rx: Rx,
tx: Tx,
cfg: Config,
}
/// Invalid PS UART error.
@@ -663,25 +638,7 @@ impl Uart {
regs: reg_block,
id: uart_id,
},
}
}
/// Steal a UART without doing ANY configuration.
///
/// # Safety
///
/// Circumvents ownership and safety guarantees by the HAL. Also, the driver will not work
/// unless the UART was configured beforehand.
pub unsafe fn steal(id: UartId) -> Uart {
let reg_block = unsafe { id.steal_regs() };
Self {
rx: Rx {
regs: unsafe { reg_block.clone() },
},
tx: Tx {
regs: reg_block,
id,
},
cfg,
}
}
@@ -700,6 +657,12 @@ impl Uart {
&mut self.rx.regs
}
/// Configuration.
#[inline]
pub const fn cfg(&self) -> &Config {
&self.cfg
}
/// Split into TX and RX halves.
#[inline]
pub const fn split(self) -> (Tx, Rx) {
+2 -7
View File
@@ -46,14 +46,9 @@ impl TypeRegister {
pub type Typer = TypeRegister;
#[bitbybit::bitfield(
u32,
debug,
default = 0x0,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
#[bitbybit::bitfield(u32, debug)]
#[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)]
+1 -19
View File
@@ -98,24 +98,6 @@ 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)]
@@ -176,7 +158,7 @@ pub struct Registers {
_gap10: [u32; 0x0B],
mio_loopback: MioLoopback,
mio_loopback: u32,
_gap11: u32,
mio_mst_tri_0: u32,
mio_mst_tri_1: u32,
+9 -26
View File
@@ -34,15 +34,13 @@ impl BaudDivSel {
}
}
#[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"))]
#[bitbybit::bitfield(
u32,
default = 0x0,
debug,
defmt_bitfields(feature = "defmt"),
forbid_overlaps
)]
pub struct Config {
#[bit(17, rw)]
modefail_gen_en: bool,
@@ -72,9 +70,6 @@ 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,
}
@@ -132,18 +127,6 @@ 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)]
@@ -245,9 +228,9 @@ pub struct Registers {
enable: u32,
delay_control: DelayControl,
#[mmio(Write)]
tx_data: FifoWrite,
txd: FifoWrite,
#[mmio(Read)]
rx_data: FifoRead,
rxd: FifoRead,
sicr: u32,
tx_trig: u32,
rx_trig: u32,
+1
View File
@@ -741,6 +741,7 @@ 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] \