init commit
This commit is contained in:
28
examples/embassy/Cargo.toml
Normal file
28
examples/embassy/Cargo.toml
Normal file
@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "embassy-examples"
|
||||
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]
|
||||
cortex-ar = { path = "/home/rmueller/Rust/cortex-ar/cortex-ar", features = ["critical-section-single-core"] }
|
||||
zynq7000-rt = { path = "../../zynq7000-rt" }
|
||||
zynq7000 = { path = "../../zynq7000" }
|
||||
zynq7000-hal = { path = "../../zynq7000-hal" }
|
||||
zynq7000-embassy = { path = "../../zynq7000-embassy" }
|
||||
embedded-io = "0.6"
|
||||
embedded-hal = "1"
|
||||
fugit = "0.3"
|
||||
log = "0.4"
|
||||
|
||||
embassy-executor = { path = "/home/rmueller/Rust/embassy/embassy-executor", features = [
|
||||
"arch-cortex-ar",
|
||||
"executor-thread"
|
||||
]}
|
||||
embassy-time = { path = "/home/rmueller/Rust/embassy/embassy-time", version = "0.4" }
|
132
examples/embassy/src/main.rs
Normal file
132
examples/embassy/src/main.rs
Normal file
@ -0,0 +1,132 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::Clocks,
|
||||
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||
gpio::{MioPins, PinState},
|
||||
gtc::Gtc,
|
||||
time::Hertz,
|
||||
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||
};
|
||||
|
||||
use zynq7000::PsPeripherals;
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||
|
||||
/// Entry point (not called like a normal main function)
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
if cpu_id != 0 {
|
||||
panic!("unexpected CPU ID {}", cpu_id);
|
||||
}
|
||||
main();
|
||||
}
|
||||
|
||||
// TODO: The export name is ignored..
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let dp = PsPeripherals::take().unwrap();
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
// Set up the global interrupt controller.
|
||||
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||
gic.enable_all_interrupts();
|
||||
gic.set_all_spi_interrupt_targets_cpu0();
|
||||
gic.enable();
|
||||
unsafe {
|
||||
gic.enable_interrupts();
|
||||
}
|
||||
let mio_pins = MioPins::new(dp.gpio);
|
||||
|
||||
// Set up global timer counter and embassy time driver.
|
||||
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let uart_tx = mio_pins.mio48.into_uart();
|
||||
let uart_rx = mio_pins.mio49.into_uart();
|
||||
let mut uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
(uart_tx, uart_rx),
|
||||
)
|
||||
.unwrap();
|
||||
uart.write_all(b"-- Zynq 7000 Embassy Hello World --\n\r")
|
||||
.unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe { zynq7000_hal::log::init_unsafe_single_core(uart, log::LevelFilter::Trace, false) };
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||
let mut led = mio_pins.mio7.into_output(PinState::Low);
|
||||
loop {
|
||||
info!("Hello, world!");
|
||||
led.toggle().unwrap();
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
Interrupt::Sgi(_) => (),
|
||||
Interrupt::Ppi(ppi_interrupt) => {
|
||||
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||
unsafe {
|
||||
zynq7000_embassy::on_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
Interrupt::Spi(_spi_interrupt) => (),
|
||||
Interrupt::Invalid(_) => (),
|
||||
Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic handler
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
error!("Panic: {:?}", info);
|
||||
loop {}
|
||||
}
|
19
examples/simple/Cargo.toml
Normal file
19
examples/simple/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "blinky"
|
||||
version = "0.1.0"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
edition = "2024"
|
||||
description = "Simple 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"
|
||||
|
||||
[dependencies]
|
||||
cortex-ar = { git = "https://github.com/us-irs/cortex-ar.git", branch = "cortex-a-addition", features = ["critical-section-single-core"] }
|
||||
zynq7000-rt = { path = "../../zynq7000-rt" }
|
||||
zynq7000 = { path = "../../zynq7000" }
|
||||
zynq7000-hal = { path = "../../zynq7000-hal" }
|
||||
embedded-io = "0.6"
|
||||
embedded-hal = "1"
|
||||
fugit = "0.3"
|
||||
log = "0.4"
|
132
examples/simple/src/bin/gtc-ticks.rs
Normal file
132
examples/simple/src/bin/gtc-ticks.rs
Normal file
@ -0,0 +1,132 @@
|
||||
//! Example which uses the global timer for a simple tick counter.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::{panic::PanicInfo, sync::atomic::AtomicU64};
|
||||
use cortex_ar::asm::nop;
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use zynq7000_hal::{
|
||||
clocks::Clocks,
|
||||
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||
gpio::{MioPins, PinState},
|
||||
gtc::Gtc,
|
||||
prelude::*,
|
||||
time::Hertz,
|
||||
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||
};
|
||||
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||
|
||||
static MS_TICKS: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
/// Entry point (not called like a normal main function)
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
if cpu_id != 0 {
|
||||
panic!("unexpected CPU ID {}", cpu_id);
|
||||
}
|
||||
main();
|
||||
}
|
||||
|
||||
#[unsafe(export_name = "main")]
|
||||
pub fn main() -> ! {
|
||||
let dp = zynq7000::PsPeripherals::take().unwrap();
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
// Set up the global interrupt controller.
|
||||
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||
gic.enable_all_interrupts();
|
||||
gic.set_all_spi_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 = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||
let ticks = gtc.frequency_to_ticks(1000.Hz());
|
||||
gtc.set_auto_increment_value(ticks);
|
||||
gtc.set_comparator(gtc.read_timer() + ticks as u64);
|
||||
gtc.enable_auto_increment();
|
||||
gtc.enable_interrupt();
|
||||
gtc.enable();
|
||||
|
||||
// This structure holds all MIO pins.
|
||||
let mio_pins = MioPins::new(dp.gpio);
|
||||
let uart_tx = mio_pins.mio48.into_uart();
|
||||
let uart_rx = mio_pins.mio49.into_uart();
|
||||
let mut uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
(uart_tx, uart_rx),
|
||||
)
|
||||
.unwrap();
|
||||
uart.write_all(b"-- Zynq 7000 GTC Ticks example --\n\r")
|
||||
.unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe { zynq7000_hal::log::init_unsafe_single_core(uart, log::LevelFilter::Trace, false) };
|
||||
|
||||
let mut led = mio_pins.mio7.into_output(PinState::Low);
|
||||
loop {
|
||||
info!(
|
||||
"MS_TICKS: {}",
|
||||
MS_TICKS.load(core::sync::atomic::Ordering::Relaxed)
|
||||
);
|
||||
led.toggle().unwrap();
|
||||
for _ in 0..5_000_000 {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
Interrupt::Sgi(_) => (),
|
||||
Interrupt::Ppi(ppi_interrupt) => {
|
||||
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||
MS_TICKS.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
Interrupt::Spi(_spi_interrupt) => (),
|
||||
Interrupt::Invalid(_) => (),
|
||||
Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic handler
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
error!("Panic: {:?}", info);
|
||||
loop {}
|
||||
}
|
135
examples/simple/src/bin/logger.rs
Normal file
135
examples/simple/src/bin/logger.rs
Normal file
@ -0,0 +1,135 @@
|
||||
//! Example which uses the UART1 to send log messages.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::{panic::PanicInfo, sync::atomic::AtomicU64};
|
||||
use cortex_ar::asm::nop;
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::Clocks,
|
||||
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||
gpio::{MioPins, PinState},
|
||||
gtc::Gtc,
|
||||
prelude::*,
|
||||
time::Hertz,
|
||||
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||
};
|
||||
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||
|
||||
static MS_TICKS: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
/// Entry point (not called like a normal main function)
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
if cpu_id != 0 {
|
||||
panic!("unexpected CPU ID {}", cpu_id);
|
||||
}
|
||||
main();
|
||||
}
|
||||
|
||||
#[unsafe(export_name = "main")]
|
||||
pub fn main() -> ! {
|
||||
let dp = zynq7000::PsPeripherals::take().unwrap();
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
// Set up the global interrupt controller.
|
||||
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||
gic.enable_all_interrupts();
|
||||
gic.set_all_spi_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 = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let mut gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||
let ticks = gtc.frequency_to_ticks(1000.Hz());
|
||||
gtc.set_auto_increment_value(ticks);
|
||||
gtc.set_comparator(gtc.read_timer() + ticks as u64);
|
||||
gtc.enable_auto_increment();
|
||||
gtc.enable_interrupt();
|
||||
gtc.enable();
|
||||
let mio_pins = MioPins::new(dp.gpio);
|
||||
let uart_tx = mio_pins.mio48.into_uart();
|
||||
let uart_rx = mio_pins.mio49.into_uart();
|
||||
|
||||
let mut uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
(uart_tx, uart_rx),
|
||||
)
|
||||
.unwrap();
|
||||
uart.write_all(b"-- Zynq 7000 Logging example --\n\r")
|
||||
.unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe { zynq7000_hal::log::init_unsafe_single_core(uart, log::LevelFilter::Trace, false) };
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let mut led = mio_pins.mio7.into_output(PinState::Low);
|
||||
loop {
|
||||
let gtc = gtc.read_timer();
|
||||
info!("Hello, world!");
|
||||
info!("GTC ticks: {}", gtc);
|
||||
led.toggle().unwrap();
|
||||
for _ in 0..5_000_000 {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
Interrupt::Sgi(_) => (),
|
||||
Interrupt::Ppi(ppi_interrupt) => {
|
||||
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||
// TODO: Call embassy on interrupt handler here soon.
|
||||
MS_TICKS.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
Interrupt::Spi(_spi_interrupt) => (),
|
||||
Interrupt::Invalid(_) => (),
|
||||
Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic handler
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
error!("Panic: {:?}", info);
|
||||
loop {}
|
||||
}
|
90
examples/simple/src/main.rs
Normal file
90
examples/simple/src/main.rs
Normal file
@ -0,0 +1,90 @@
|
||||
//! Simple blinky app, showing a PAC variant and a HAL variant.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use zynq7000::PsPeripherals;
|
||||
use zynq7000_hal::gpio::{MioPins, PinState};
|
||||
use zynq7000_rt as _;
|
||||
|
||||
/// One user LED is MIO7
|
||||
const ZEDBOARD_LED_MASK: u32 = 1 << 7;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Lib {
|
||||
Pac,
|
||||
Hal,
|
||||
}
|
||||
|
||||
const LIB: Lib = Lib::Hal;
|
||||
|
||||
/// Entry point (not called like a normal main function)
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
if cpu_id != 0 {
|
||||
panic!("unexpected CPU ID {}", cpu_id);
|
||||
}
|
||||
main();
|
||||
}
|
||||
|
||||
#[unsafe(export_name = "main")]
|
||||
pub fn main() -> ! {
|
||||
match LIB {
|
||||
Lib::Pac => {
|
||||
let mut gpio = unsafe { zynq7000::gpio::Gpio::new_mmio_fixed() };
|
||||
gpio.bank_0().modify_dirm(|v| v | ZEDBOARD_LED_MASK);
|
||||
gpio.bank_0().modify_out_en(|v| v | ZEDBOARD_LED_MASK);
|
||||
loop {
|
||||
gpio.modify_out_0(|v| v ^ ZEDBOARD_LED_MASK);
|
||||
for _ in 0..5_000_000 {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
}
|
||||
Lib::Hal => {
|
||||
let dp = PsPeripherals::take().unwrap();
|
||||
let mio_pins = MioPins::new(dp.gpio);
|
||||
let mut led = mio_pins.mio7.into_output(PinState::High);
|
||||
loop {
|
||||
led.toggle().unwrap();
|
||||
for _ in 0..5_000_000 {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic handler
|
||||
#[panic_handler]
|
||||
fn panic(_info: &PanicInfo) -> ! {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
38
examples/zedboard/Cargo.toml
Normal file
38
examples/zedboard/Cargo.toml
Normal file
@ -0,0 +1,38 @@
|
||||
[package]
|
||||
name = "zedboard"
|
||||
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]
|
||||
cortex-ar = { git = "https://github.com/us-irs/cortex-ar.git", branch = "cortex-a-addition", features = ["critical-section-single-core"] }
|
||||
zynq7000-rt = { path = "../../zynq7000-rt" }
|
||||
zynq7000 = { path = "../../zynq7000" }
|
||||
zynq7000-hal = { path = "../../zynq7000-hal" }
|
||||
zynq7000-embassy = { path = "../../zynq7000-embassy" }
|
||||
l3gd20 = { git = "https://github.com/us-irs/l3gd20.git", branch = "add-i2c-if" }
|
||||
embedded-io = "0.6"
|
||||
arbitrary-int = "1.3"
|
||||
embedded-io-async = "0.6"
|
||||
critical-section = "1"
|
||||
static_cell = "2"
|
||||
embedded-alloc = "0.6"
|
||||
embedded-hal = "1"
|
||||
embedded-hal-async = "1"
|
||||
fugit = "0.3"
|
||||
log = "0.4"
|
||||
|
||||
embassy-executor = { path = "/home/rmueller/Rust/embassy/embassy-executor", features = [
|
||||
"arch-cortex-ar",
|
||||
"executor-thread"
|
||||
]}
|
||||
embassy-time = { path = "/home/rmueller/Rust/embassy/embassy-time", version = "0.4" }
|
||||
heapless = "0.8"
|
||||
axi-uartlite = { path = "../../axi-uartlite-rs" }
|
||||
axi-uart16550 = { path = "../../axi-uart16550-rs" }
|
197
examples/zedboard/src/bin/l3gd20h-i2c-mio.rs
Normal file
197
examples/zedboard/src/bin/l3gd20h-i2c-mio.rs
Normal file
@ -0,0 +1,197 @@
|
||||
//! PS I2C example using a L3GD20H sensor.
|
||||
//!
|
||||
//! External HW connections:
|
||||
//!
|
||||
//! - SCK pin to JE4 (MIO 12)
|
||||
//! - SDA pin to JE1 (MIO 13)
|
||||
//! - SDO / SA0 pin to JE3 (MIO 11) to select I2C address.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Delay, Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_hal_async::delay::DelayNs;
|
||||
use embedded_io::Write;
|
||||
use l3gd20::i2c::I2cAddr;
|
||||
use log::{error, info};
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::Clocks,
|
||||
configure_level_shifter,
|
||||
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||
gpio::{EmioPin, GpioPins, PinState},
|
||||
gtc::Gtc,
|
||||
i2c,
|
||||
time::Hertz,
|
||||
uart,
|
||||
};
|
||||
|
||||
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||
const I2C_ADDR_SEL: I2cAddr = I2cAddr::Sa0Low;
|
||||
|
||||
/// Entry point (not called like a normal main function)
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
if cpu_id != 0 {
|
||||
panic!("unexpected CPU ID {}", cpu_id);
|
||||
}
|
||||
main();
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
// Enable PS-PL level shifters.
|
||||
configure_level_shifter(LevelShifterConfig::EnableAll);
|
||||
let dp = PsPeripherals::take().unwrap();
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
|
||||
// Set up the global interrupt controller.
|
||||
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||
gic.enable_all_interrupts();
|
||||
gic.set_all_spi_interrupt_targets_cpu0();
|
||||
gic.enable();
|
||||
unsafe {
|
||||
gic.enable_interrupts();
|
||||
}
|
||||
|
||||
let mut gpio_pins = GpioPins::new(dp.gpio);
|
||||
|
||||
// Set up global timer counter and embassy time driver.
|
||||
let gtc = Gtc::new(dp.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::ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let uart_tx = gpio_pins.mio.mio48.into_uart();
|
||||
let uart_rx = gpio_pins.mio.mio49.into_uart();
|
||||
let mut uart = uart::Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
uart::UartConfig::new_with_clk_config(uart_clk_config),
|
||||
(uart_tx, uart_rx),
|
||||
)
|
||||
.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::init_unsafe_single_core(uart, log::LevelFilter::Trace, false) };
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let sck_pin = gpio_pins.mio.mio12.into_i2c();
|
||||
let sda_pin = gpio_pins.mio.mio13.into_i2c();
|
||||
let pin_sel = match I2C_ADDR_SEL {
|
||||
I2cAddr::Sa0Low => PinState::Low,
|
||||
I2cAddr::Sa0High => PinState::High,
|
||||
};
|
||||
let _sa0_pin = gpio_pins.mio.mio11.into_output(pin_sel);
|
||||
// The CS pin must be pulled high.
|
||||
let _cs_pin = gpio_pins.mio.mio10.into_output(PinState::High);
|
||||
|
||||
let clk_config = i2c::calculate_divisors(
|
||||
clocks.arm_clocks().cpu_1x_clk(),
|
||||
i2c::I2cSpeed::Normal100kHz,
|
||||
)
|
||||
.unwrap();
|
||||
let i2c = i2c::I2c::new_with_mio(dp.i2c_1, clk_config, (sck_pin, sda_pin)).unwrap();
|
||||
|
||||
let mut delay = Delay;
|
||||
let mut l3gd20 = l3gd20::i2c::L3gd20::new(i2c, l3gd20::i2c::I2cAddr::Sa0Low).unwrap();
|
||||
let who_am_i = l3gd20.who_am_i().unwrap();
|
||||
info!("L3GD20 WHO_AM_I: 0x{:02X}", who_am_i);
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_millis(400));
|
||||
let mut mio_led = gpio_pins.mio.mio7.into_output(PinState::Low);
|
||||
|
||||
let mut emio_leds: [EmioPin; 8] = [
|
||||
gpio_pins.emio.take(0).unwrap(),
|
||||
gpio_pins.emio.take(1).unwrap(),
|
||||
gpio_pins.emio.take(2).unwrap(),
|
||||
gpio_pins.emio.take(3).unwrap(),
|
||||
gpio_pins.emio.take(4).unwrap(),
|
||||
gpio_pins.emio.take(5).unwrap(),
|
||||
gpio_pins.emio.take(6).unwrap(),
|
||||
gpio_pins.emio.take(7).unwrap(),
|
||||
];
|
||||
for (idx, led) in emio_leds.iter_mut().enumerate() {
|
||||
if idx % 2 == 0 {
|
||||
led.into_output(PinState::High);
|
||||
} else {
|
||||
led.into_output(PinState::Low);
|
||||
}
|
||||
}
|
||||
|
||||
// Power up time for the sensor to get good readings.
|
||||
delay.delay_ms(50).await;
|
||||
loop {
|
||||
mio_led.toggle().unwrap();
|
||||
|
||||
let measurements = l3gd20.all().unwrap();
|
||||
info!("L3GD20: {:?}", measurements);
|
||||
info!("L3GD20 Temp: {:?}", measurements.temp_celcius());
|
||||
for led in emio_leds.iter_mut() {
|
||||
led.toggle().unwrap();
|
||||
}
|
||||
|
||||
ticker.next().await; // Wait for the next cycle of the ticker
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
Interrupt::Sgi(_) => (),
|
||||
Interrupt::Ppi(ppi_interrupt) => {
|
||||
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||
unsafe {
|
||||
zynq7000_embassy::on_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
Interrupt::Spi(_spi_interrupt) => (),
|
||||
Interrupt::Invalid(_) => (),
|
||||
Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic handler
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
error!("Panic: {:?}", info);
|
||||
loop {}
|
||||
}
|
230
examples/zedboard/src/bin/l3gd20h-spi-mio.rs
Normal file
230
examples/zedboard/src/bin/l3gd20h-spi-mio.rs
Normal file
@ -0,0 +1,230 @@
|
||||
//! PS SPI example using a L3GD20H sensor.
|
||||
//!
|
||||
//! External HW connections:
|
||||
//!
|
||||
//! - SCK pin to JE4 (MIO 12)
|
||||
//! - MOSI pin to JE2 (MIO 10)
|
||||
//! - MISO pin to JE3 (MIO 11)
|
||||
//! - SS pin to JE1 (MIO 13)
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Delay, Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_hal_async::delay::DelayNs;
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::Clocks,
|
||||
configure_level_shifter,
|
||||
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||
gpio::{EmioPin, GpioPins, PinState},
|
||||
gtc::Gtc,
|
||||
spi::{self, SpiWithHwCs},
|
||||
time::Hertz,
|
||||
uart,
|
||||
};
|
||||
|
||||
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig, spi::DelayControl};
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||
|
||||
const DEBUG_SPI_CLK_CONFIG: bool = false;
|
||||
|
||||
/// Entry point (not called like a normal main function)
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
if cpu_id != 0 {
|
||||
panic!("unexpected CPU ID {}", cpu_id);
|
||||
}
|
||||
main();
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
// Enable PS-PL level shifters.
|
||||
configure_level_shifter(LevelShifterConfig::EnableAll);
|
||||
let dp = PsPeripherals::take().unwrap();
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let mut clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
|
||||
// SPI reference clock must be larger than the CPU 1x clock.
|
||||
let spi_ref_clk_div = spi::calculate_largest_allowed_spi_ref_clk_divisor(&clocks)
|
||||
.unwrap()
|
||||
.value()
|
||||
- 1;
|
||||
spi::configure_spi_ref_clk(&mut clocks, arbitrary_int::u6::new(spi_ref_clk_div as u8));
|
||||
|
||||
// Set up the global interrupt controller.
|
||||
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||
gic.enable_all_interrupts();
|
||||
gic.set_all_spi_interrupt_targets_cpu0();
|
||||
gic.enable();
|
||||
unsafe {
|
||||
gic.enable_interrupts();
|
||||
}
|
||||
|
||||
let mut gpio_pins = GpioPins::new(dp.gpio);
|
||||
|
||||
// Set up global timer counter and embassy time driver.
|
||||
let gtc = Gtc::new(dp.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::ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let uart_tx = gpio_pins.mio.mio48.into_uart();
|
||||
let uart_rx = gpio_pins.mio.mio49.into_uart();
|
||||
let mut uart = uart::Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
uart::UartConfig::new_with_clk_config(uart_clk_config),
|
||||
(uart_tx, uart_rx),
|
||||
)
|
||||
.unwrap();
|
||||
uart.write_all(b"-- Zynq 7000 Zedboard SPI L3GD20H example --\n\r")
|
||||
.unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe { zynq7000_hal::log::init_unsafe_single_core(uart, log::LevelFilter::Trace, false) };
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
if DEBUG_SPI_CLK_CONFIG {
|
||||
info!(
|
||||
"SPI Clock Information: CPU 1x: {:?}, IO Ref Clk: {:?}, SPI Ref Clk: {:?}, DIV: {:?}",
|
||||
clocks.arm_clocks().cpu_1x_clk(),
|
||||
clocks.io_clocks().ref_clk(),
|
||||
clocks.io_clocks().spi_clk(),
|
||||
spi_ref_clk_div
|
||||
);
|
||||
}
|
||||
let sck_pin = gpio_pins.mio.mio12.into_spi();
|
||||
let mosi_pin = gpio_pins.mio.mio10.into_spi();
|
||||
let miso_pin = gpio_pins.mio.mio11.into_spi();
|
||||
let ss_pin = gpio_pins.mio.mio13.into_spi();
|
||||
|
||||
let mut spi = spi::Spi::new_one_hw_cs(
|
||||
dp.spi_1,
|
||||
clocks.io_clocks(),
|
||||
spi::Config::new(
|
||||
// 10 MHz maximum rating of the sensor.
|
||||
zynq7000::spi::BaudDivSelect::By64,
|
||||
//l3gd20::MODE,
|
||||
embedded_hal::spi::MODE_3,
|
||||
spi::SlaveSelectConfig::AutoWithAutoStart,
|
||||
),
|
||||
(sck_pin, mosi_pin, miso_pin),
|
||||
ss_pin,
|
||||
)
|
||||
.unwrap();
|
||||
let mod_id = spi.regs().read_mod_id();
|
||||
assert_eq!(mod_id, spi::MODULE_ID);
|
||||
assert!(spi.sclk() <= Hertz::from_raw(10_000_000));
|
||||
let min_delay = (spi.sclk().raw() * 5) / 1_000_000_000;
|
||||
spi.configure_delays(
|
||||
DelayControl::builder()
|
||||
.with_inter_word_cs_deassert(0)
|
||||
.with_between_cs_assertion(0)
|
||||
.with_inter_word(0)
|
||||
.with_cs_to_first_bit(min_delay as u8)
|
||||
.build(),
|
||||
);
|
||||
|
||||
let mut delay = Delay;
|
||||
let spi_dev = SpiWithHwCs::new(spi, spi::ChipSelect::Slave0, delay.clone());
|
||||
let mut l3gd20 = l3gd20::spi::L3gd20::new(spi_dev).unwrap();
|
||||
let who_am_i = l3gd20.who_am_i().unwrap();
|
||||
info!("L3GD20 WHO_AM_I: 0x{:02X}", who_am_i);
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_millis(400));
|
||||
let mut mio_led = gpio_pins.mio.mio7.into_output(PinState::Low);
|
||||
|
||||
let mut emio_leds: [EmioPin; 8] = [
|
||||
gpio_pins.emio.take(0).unwrap(),
|
||||
gpio_pins.emio.take(1).unwrap(),
|
||||
gpio_pins.emio.take(2).unwrap(),
|
||||
gpio_pins.emio.take(3).unwrap(),
|
||||
gpio_pins.emio.take(4).unwrap(),
|
||||
gpio_pins.emio.take(5).unwrap(),
|
||||
gpio_pins.emio.take(6).unwrap(),
|
||||
gpio_pins.emio.take(7).unwrap(),
|
||||
];
|
||||
for (idx, led) in emio_leds.iter_mut().enumerate() {
|
||||
if idx % 2 == 0 {
|
||||
led.into_output(PinState::High);
|
||||
} else {
|
||||
led.into_output(PinState::Low);
|
||||
}
|
||||
}
|
||||
|
||||
// Power up time for the sensor to get good readings.
|
||||
delay.delay_ms(50).await;
|
||||
loop {
|
||||
mio_led.toggle().unwrap();
|
||||
|
||||
let measurements = l3gd20.all().unwrap();
|
||||
info!("L3GD20: {:?}", measurements);
|
||||
info!("L3GD20 Temp: {:?}", measurements.temp_celcius());
|
||||
for led in emio_leds.iter_mut() {
|
||||
led.toggle().unwrap();
|
||||
}
|
||||
|
||||
ticker.next().await; // Wait for the next cycle of the ticker
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
Interrupt::Sgi(_) => (),
|
||||
Interrupt::Ppi(ppi_interrupt) => {
|
||||
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||
unsafe {
|
||||
zynq7000_embassy::on_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
Interrupt::Spi(_spi_interrupt) => (),
|
||||
Interrupt::Invalid(_) => (),
|
||||
Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic handler
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
error!("Panic: {:?}", info);
|
||||
loop {}
|
||||
}
|
263
examples/zedboard/src/bin/uart-blocking.rs
Normal file
263
examples/zedboard/src/bin/uart-blocking.rs
Normal file
@ -0,0 +1,263 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use axi_uart16550::AxiUart16550;
|
||||
use axi_uartlite::AxiUartlite;
|
||||
use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use fugit::RateExtU32;
|
||||
use log::{error, info};
|
||||
use zedboard::PS_CLOCK_FREQUENCY;
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::Clocks,
|
||||
configure_level_shifter,
|
||||
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||
gpio::{EmioPin, GpioPins, PinState},
|
||||
gtc::Gtc,
|
||||
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||
};
|
||||
|
||||
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
|
||||
use zynq7000_rt as _;
|
||||
|
||||
const INIT_STRING: &str = "-- Zynq 7000 Zedboard blocking UART example --\n\r";
|
||||
|
||||
const AXI_UARTLITE_BASE_ADDR: u32 = 0x42C0_0000;
|
||||
const AXI_UAR16550_BASE_ADDR: u32 = 0x43C0_0000;
|
||||
|
||||
/// Entry point (not called like a normal main function)
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
if cpu_id != 0 {
|
||||
panic!("unexpected CPU ID {}", cpu_id);
|
||||
}
|
||||
main();
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum UartSel {
|
||||
Uart0 = 0b000,
|
||||
Uartlite = 0b001,
|
||||
Uart16550 = 0b010,
|
||||
Uart0ToUartlite = 0b011,
|
||||
Uart0ToUart16550 = 0b100,
|
||||
UartliteToUart16550 = 0b101,
|
||||
}
|
||||
|
||||
pub struct UartMultiplexer {
|
||||
sel_pins: [EmioPin; 3],
|
||||
}
|
||||
|
||||
impl UartMultiplexer {
|
||||
pub fn new(mut sel_pins: [EmioPin; 3]) -> Self {
|
||||
for pin in sel_pins.iter_mut() {
|
||||
pin.into_output(PinState::Low);
|
||||
}
|
||||
Self { sel_pins }
|
||||
}
|
||||
|
||||
pub fn select(&mut self, sel: UartSel) {
|
||||
// TODO: A pin group switcher would be nice to do this in one go.
|
||||
match sel {
|
||||
UartSel::Uart0 => {
|
||||
self.sel_pins[2].set_low().unwrap();
|
||||
self.sel_pins[1].set_low().unwrap();
|
||||
self.sel_pins[0].set_low().unwrap();
|
||||
}
|
||||
UartSel::Uartlite => {
|
||||
self.sel_pins[2].set_low().unwrap();
|
||||
self.sel_pins[1].set_low().unwrap();
|
||||
self.sel_pins[0].set_high().unwrap();
|
||||
}
|
||||
UartSel::Uart16550 => {
|
||||
self.sel_pins[2].set_low().unwrap();
|
||||
self.sel_pins[1].set_high().unwrap();
|
||||
self.sel_pins[0].set_low().unwrap();
|
||||
}
|
||||
UartSel::Uart0ToUartlite => {
|
||||
self.sel_pins[2].set_low().unwrap();
|
||||
self.sel_pins[1].set_high().unwrap();
|
||||
self.sel_pins[0].set_high().unwrap();
|
||||
}
|
||||
UartSel::Uart0ToUart16550 => {
|
||||
self.sel_pins[2].set_high().unwrap();
|
||||
self.sel_pins[1].set_low().unwrap();
|
||||
self.sel_pins[0].set_low().unwrap();
|
||||
}
|
||||
UartSel::UartliteToUart16550 => {
|
||||
self.sel_pins[2].set_high().unwrap();
|
||||
self.sel_pins[1].set_low().unwrap();
|
||||
self.sel_pins[0].set_high().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
// Enable PS-PL level shifters.
|
||||
configure_level_shifter(LevelShifterConfig::EnableAll);
|
||||
let dp = PsPeripherals::take().unwrap();
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
// Set up the global interrupt controller.
|
||||
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||
gic.enable_all_interrupts();
|
||||
gic.set_all_spi_interrupt_targets_cpu0();
|
||||
gic.enable();
|
||||
unsafe {
|
||||
gic.enable_interrupts();
|
||||
}
|
||||
let mut gpio_pins = GpioPins::new(dp.gpio);
|
||||
|
||||
// Set up global timer counter and embassy time driver.
|
||||
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let uart_tx = gpio_pins.mio.mio48.into_uart();
|
||||
let uart_rx = gpio_pins.mio.mio49.into_uart();
|
||||
let mut log_uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
(uart_tx, uart_rx),
|
||||
)
|
||||
.unwrap();
|
||||
log_uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||
|
||||
// Safety: Co-operative multi-tasking is used.
|
||||
unsafe { zynq7000_hal::log::init_unsafe_single_core(log_uart, log::LevelFilter::Trace, false) };
|
||||
|
||||
// UART0 routed through EMIO to PL pins.
|
||||
let mut uart_0 =
|
||||
Uart::new_with_emio(dp.uart_0, UartConfig::new_with_clk_config(uart_clk_config)).unwrap();
|
||||
// Safety: Valid address of AXI UARTLITE.
|
||||
let mut uartlite = unsafe { AxiUartlite::new(AXI_UARTLITE_BASE_ADDR) };
|
||||
|
||||
// TODO: Can we determine/read the clock frequency to the FPGAs as well?
|
||||
let (clk_config, error) =
|
||||
axi_uart16550::ClkConfig::new_autocalc_with_error(100.MHz(), 115200).unwrap();
|
||||
assert!(error < 0.02);
|
||||
let mut uart_16550 = unsafe {
|
||||
AxiUart16550::new(
|
||||
AXI_UAR16550_BASE_ADDR,
|
||||
axi_uart16550::UartConfig::new_with_clk_config(clk_config),
|
||||
)
|
||||
};
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||
let mut mio_led = gpio_pins.mio.mio7.into_output(PinState::Low);
|
||||
|
||||
let mut emio_leds: [EmioPin; 8] = [
|
||||
gpio_pins.emio.take(0).unwrap(),
|
||||
gpio_pins.emio.take(1).unwrap(),
|
||||
gpio_pins.emio.take(2).unwrap(),
|
||||
gpio_pins.emio.take(3).unwrap(),
|
||||
gpio_pins.emio.take(4).unwrap(),
|
||||
gpio_pins.emio.take(5).unwrap(),
|
||||
gpio_pins.emio.take(6).unwrap(),
|
||||
gpio_pins.emio.take(7).unwrap(),
|
||||
];
|
||||
for led in emio_leds.iter_mut() {
|
||||
led.into_output(PinState::Low);
|
||||
}
|
||||
|
||||
let mut uart_mux = UartMultiplexer::new([
|
||||
gpio_pins.emio.take(8).unwrap(),
|
||||
gpio_pins.emio.take(9).unwrap(),
|
||||
gpio_pins.emio.take(10).unwrap(),
|
||||
]);
|
||||
let mut current_sel = UartSel::Uart0;
|
||||
uart_mux.select(current_sel);
|
||||
let mut led_idx = 0;
|
||||
loop {
|
||||
mio_led.toggle().unwrap();
|
||||
|
||||
emio_leds[led_idx].toggle().unwrap();
|
||||
led_idx += 1;
|
||||
if led_idx >= emio_leds.len() {
|
||||
led_idx = 0;
|
||||
}
|
||||
uart_0
|
||||
.write_all("Hello, World from UART0!\n\r".as_bytes())
|
||||
.unwrap();
|
||||
uartlite
|
||||
.write_all("Hello, World from AXI UARTLITE!\n\r".as_bytes())
|
||||
.unwrap();
|
||||
uart_16550
|
||||
.write_all("Hello, World from AXI UART16550!\n\r".as_bytes())
|
||||
.unwrap();
|
||||
|
||||
uart_0.flush().unwrap();
|
||||
uartlite.flush().unwrap();
|
||||
uart_16550.flush().unwrap();
|
||||
match current_sel {
|
||||
UartSel::Uart0 => current_sel = UartSel::Uartlite,
|
||||
UartSel::Uartlite => current_sel = UartSel::Uart16550,
|
||||
UartSel::Uart16550 => current_sel = UartSel::Uart0,
|
||||
UartSel::Uart0ToUartlite | UartSel::Uart0ToUart16550 | UartSel::UartliteToUart16550 => {
|
||||
}
|
||||
}
|
||||
uart_mux.select(current_sel);
|
||||
ticker.next().await; // Wait for the next cycle of the ticker
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
Interrupt::Sgi(_) => (),
|
||||
Interrupt::Ppi(ppi_interrupt) => {
|
||||
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||
unsafe {
|
||||
zynq7000_embassy::on_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
Interrupt::Spi(_spi_interrupt) => (),
|
||||
Interrupt::Invalid(_) => (),
|
||||
Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic handler
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
error!("Panic: {:?}", info);
|
||||
loop {}
|
||||
}
|
569
examples/zedboard/src/bin/uart-non-blocking.rs
Normal file
569
examples/zedboard/src/bin/uart-non-blocking.rs
Normal file
@ -0,0 +1,569 @@
|
||||
//! This example application shows usage of the non-blocking APIs offered for
|
||||
//! various UART peripherals which are part of the PS or the sample bitstream.
|
||||
//!
|
||||
//! This example ONLY works with the provided `zedboard-fpga-design`.
|
||||
//! The example FPGA design contains the following components relevant for this design
|
||||
//!
|
||||
//! - An AXI UARTLite peripheral, with the interrupt signal connected to the PS
|
||||
//! - An AXI UART16550 peripheral, with the interrupt signal connected to the PS
|
||||
//! - A custom UART multiplexer component. This multiplexer has various configuration modes
|
||||
//! configurable via 3 EMIO pins:
|
||||
//! 1. Route UART0 RX to pin JA1 and TX to JA2.
|
||||
//! 2. Route AXI UARTLite RX to pin JA1 and TX to JA2.
|
||||
//! 3. Route AXI UART16550 RX to pin JA1 and TX to JA2.
|
||||
//! 4. Route UART0 to AXI UARTLite.
|
||||
//! 5. Route UART0 to AXI 16550.
|
||||
//! 6. Route AXI UARTLite to AXI 16550.
|
||||
//! - Options 4, 5 and 6 allow to test and show the non-blocking API without the need for external
|
||||
//! hardware.
|
||||
//!
|
||||
//! This example runs one embassy task for each UART, which periodically sends variable sized
|
||||
//! string content out via its TX port. Each UART peripheral also permanently listens to all
|
||||
//! incoming RX data via interrupts. Received data will be sent from the interrupt handler
|
||||
//! to the main thread and will be printed to the main console on UART 1.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::format;
|
||||
use axi_uart16550::AxiUart16550;
|
||||
use axi_uartlite::AxiUartlite;
|
||||
use core::{cell::RefCell, panic::PanicInfo};
|
||||
use cortex_ar::asm::nop;
|
||||
use critical_section::Mutex;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
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 zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::Clocks,
|
||||
configure_level_shifter,
|
||||
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||
gpio::{EmioPin, GpioPins, Mio7, MioPin, Output, PinState},
|
||||
gtc::Gtc,
|
||||
time::Hertz,
|
||||
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||
};
|
||||
|
||||
pub enum UartMode {
|
||||
Uart0ToUartlite,
|
||||
Uart0ToUart16550,
|
||||
UartliteToUart16550,
|
||||
}
|
||||
|
||||
const UART_MODE: UartMode = UartMode::Uart0ToUart16550;
|
||||
const INIT_STRING: &str = "-- Zynq 7000 Zedboard non-blocking UART example --\n\r";
|
||||
|
||||
#[global_allocator]
|
||||
static HEAP: Heap = Heap::empty();
|
||||
|
||||
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
|
||||
use zynq7000_rt as _;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
||||
|
||||
const AXI_UARTLITE_BASE_ADDR: u32 = 0x42C0_0000;
|
||||
const AXI_UAR16550_BASE_ADDR: u32 = 0x43C0_0000;
|
||||
|
||||
pub const UARTLITE_PL_INT_ID: usize = 0;
|
||||
pub const UART16550_PL_INT_ID: usize = 1;
|
||||
|
||||
const RB_SIZE: usize = 512;
|
||||
|
||||
// These queues are used to send all data received in the UART interrupt handlers to the main
|
||||
// processing thread.
|
||||
static QUEUE_UART_0: static_cell::ConstStaticCell<heapless::spsc::Queue<u8, RB_SIZE>> =
|
||||
static_cell::ConstStaticCell::new(Queue::new());
|
||||
static QUEUE_UARTLITE: static_cell::ConstStaticCell<heapless::spsc::Queue<u8, RB_SIZE>> =
|
||||
static_cell::ConstStaticCell::new(Queue::new());
|
||||
static QUEUE_UART16550: static_cell::ConstStaticCell<heapless::spsc::Queue<u8, RB_SIZE>> =
|
||||
static_cell::ConstStaticCell::new(Queue::new());
|
||||
|
||||
// Those are all used by the interrupt handler, so we have to do the Mutex dance.
|
||||
static RX_UART_0: Mutex<RefCell<Option<zynq7000_hal::uart::Rx>>> = Mutex::new(RefCell::new(None));
|
||||
|
||||
static UART_0_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8, RB_SIZE>>>> =
|
||||
Mutex::new(RefCell::new(None));
|
||||
static UARTLITE_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8, RB_SIZE>>>> =
|
||||
Mutex::new(RefCell::new(None));
|
||||
static UART16550_PROD: Mutex<RefCell<Option<heapless::spsc::Producer<'static, u8, RB_SIZE>>>> =
|
||||
Mutex::new(RefCell::new(None));
|
||||
|
||||
/// Entry point (not called like a normal main function)
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
if cpu_id != 0 {
|
||||
panic!("unexpected CPU ID {}", cpu_id);
|
||||
}
|
||||
main();
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum UartSel {
|
||||
Uart0 = 0b000,
|
||||
Uartlite = 0b001,
|
||||
Uart16550 = 0b010,
|
||||
Uart0ToUartlite = 0b011,
|
||||
Uart0ToUart16550 = 0b100,
|
||||
UartliteToUart16550 = 0b101,
|
||||
}
|
||||
|
||||
pub struct UartMultiplexer {
|
||||
sel_pins: [EmioPin; 3],
|
||||
}
|
||||
|
||||
impl UartMultiplexer {
|
||||
pub fn new(mut sel_pins: [EmioPin; 3]) -> Self {
|
||||
for pin in sel_pins.iter_mut() {
|
||||
pin.into_output(PinState::Low);
|
||||
}
|
||||
Self { sel_pins }
|
||||
}
|
||||
|
||||
pub fn select(&mut self, sel: UartSel) {
|
||||
// TODO: A pin group switcher would be nice to do this in one go.
|
||||
match sel {
|
||||
UartSel::Uart0 => {
|
||||
self.sel_pins[2].set_low().unwrap();
|
||||
self.sel_pins[1].set_low().unwrap();
|
||||
self.sel_pins[0].set_low().unwrap();
|
||||
}
|
||||
UartSel::Uartlite => {
|
||||
self.sel_pins[2].set_low().unwrap();
|
||||
self.sel_pins[1].set_low().unwrap();
|
||||
self.sel_pins[0].set_high().unwrap();
|
||||
}
|
||||
UartSel::Uart16550 => {
|
||||
self.sel_pins[2].set_low().unwrap();
|
||||
self.sel_pins[1].set_high().unwrap();
|
||||
self.sel_pins[0].set_low().unwrap();
|
||||
}
|
||||
UartSel::Uart0ToUartlite => {
|
||||
self.sel_pins[2].set_low().unwrap();
|
||||
self.sel_pins[1].set_high().unwrap();
|
||||
self.sel_pins[0].set_high().unwrap();
|
||||
}
|
||||
UartSel::Uart0ToUart16550 => {
|
||||
self.sel_pins[2].set_high().unwrap();
|
||||
self.sel_pins[1].set_low().unwrap();
|
||||
self.sel_pins[0].set_low().unwrap();
|
||||
}
|
||||
UartSel::UartliteToUart16550 => {
|
||||
self.sel_pins[2].set_high().unwrap();
|
||||
self.sel_pins[1].set_low().unwrap();
|
||||
self.sel_pins[0].set_high().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(spawner: Spawner) -> ! {
|
||||
// Enable PS-PL level shifters.
|
||||
configure_level_shifter(LevelShifterConfig::EnableAll);
|
||||
let dp = PsPeripherals::take().unwrap();
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
// Set up the global interrupt controller.
|
||||
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||
gic.enable_all_interrupts();
|
||||
gic.set_all_spi_interrupt_targets_cpu0();
|
||||
// AXI UARTLite documentation mentions that a rising-edge sensitive interrupt is generated,
|
||||
// but the GIC configures high-level sensitivity for the PL interrupts by default. We
|
||||
// need to change it to edge sensitivity.
|
||||
gic.set_pl_interrupt_sensitivity(UARTLITE_PL_INT_ID, zynq7000_hal::gic::SpiSensitivity::Edge)
|
||||
.unwrap();
|
||||
gic.enable();
|
||||
unsafe {
|
||||
gic.enable_interrupts();
|
||||
}
|
||||
let mut gpio_pins = GpioPins::new(dp.gpio);
|
||||
|
||||
// Set up global timer counter and embassy time driver.
|
||||
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let uart_tx = gpio_pins.mio.mio48.into_uart();
|
||||
let uart_rx = gpio_pins.mio.mio49.into_uart();
|
||||
let mut log_uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
(uart_tx, uart_rx),
|
||||
)
|
||||
.unwrap();
|
||||
log_uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||
|
||||
// Initialize the allocator BEFORE you use it
|
||||
{
|
||||
use core::mem::MaybeUninit;
|
||||
// 10 kB heap.
|
||||
const HEAP_SIZE: usize = 1024 * 10;
|
||||
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
|
||||
unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) }
|
||||
}
|
||||
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe { zynq7000_hal::log::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([
|
||||
gpio_pins.emio.take(8).unwrap(),
|
||||
gpio_pins.emio.take(9).unwrap(),
|
||||
gpio_pins.emio.take(10).unwrap(),
|
||||
]);
|
||||
match UART_MODE {
|
||||
UartMode::Uart0ToUartlite => uart_mux.select(UartSel::Uart0ToUartlite),
|
||||
UartMode::Uart0ToUart16550 => uart_mux.select(UartSel::Uart0ToUart16550),
|
||||
UartMode::UartliteToUart16550 => uart_mux.select(UartSel::UartliteToUart16550),
|
||||
}
|
||||
|
||||
// UART0 routed through EMIO to PL pins.
|
||||
let uart_0 =
|
||||
Uart::new_with_emio(dp.uart_0, UartConfig::new_with_clk_config(uart_clk_config)).unwrap();
|
||||
// Safety: Valid address of AXI UARTLITE.
|
||||
let mut uartlite = unsafe { AxiUartlite::new(AXI_UARTLITE_BASE_ADDR) };
|
||||
// We need to call this before splitting the structure, because the interrupt signal is
|
||||
// used for both TX and RX, so the API is only exposed for this structure.
|
||||
uartlite.enable_interrupt();
|
||||
|
||||
let (clk_config, error) =
|
||||
axi_uart16550::ClkConfig::new_autocalc_with_error(clocks.pl_clocks()[0], 115200).unwrap();
|
||||
assert!(error < 0.02);
|
||||
let _uart_16550 = unsafe {
|
||||
AxiUart16550::new(
|
||||
AXI_UAR16550_BASE_ADDR,
|
||||
axi_uart16550::UartConfig::new_with_clk_config(clk_config),
|
||||
)
|
||||
};
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let mio_led = gpio_pins.mio.mio7.into_output(PinState::Low);
|
||||
|
||||
let mut emio_leds: [EmioPin; 8] = [
|
||||
gpio_pins.emio.take(0).unwrap(),
|
||||
gpio_pins.emio.take(1).unwrap(),
|
||||
gpio_pins.emio.take(2).unwrap(),
|
||||
gpio_pins.emio.take(3).unwrap(),
|
||||
gpio_pins.emio.take(4).unwrap(),
|
||||
gpio_pins.emio.take(5).unwrap(),
|
||||
gpio_pins.emio.take(6).unwrap(),
|
||||
gpio_pins.emio.take(7).unwrap(),
|
||||
];
|
||||
for led in emio_leds.iter_mut() {
|
||||
led.into_output(PinState::Low);
|
||||
}
|
||||
|
||||
let (uart_0_tx, mut uart_0_rx) = uart_0.split();
|
||||
let (uartlite_tx, _uartlite_rx) = uartlite.split();
|
||||
let (uart_16550_tx, mut uart_16550_rx) = _uart_16550.split();
|
||||
let (uart_0_prod, mut uart_0_cons) = QUEUE_UART_0.take().split();
|
||||
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();
|
||||
// Use our helper function to start RX handling.
|
||||
uart_16550_rx.start_interrupt_driven_reception();
|
||||
critical_section::with(|cs| {
|
||||
UART_0_PROD.borrow(cs).borrow_mut().replace(uart_0_prod);
|
||||
UARTLITE_PROD.borrow(cs).borrow_mut().replace(uartlite_prod);
|
||||
UART16550_PROD
|
||||
.borrow(cs)
|
||||
.borrow_mut()
|
||||
.replace(uart16550_prod);
|
||||
RX_UART_0.borrow(cs).borrow_mut().replace(uart_0_rx);
|
||||
});
|
||||
spawner.spawn(led_task(mio_led, emio_leds)).unwrap();
|
||||
|
||||
match UART_MODE {
|
||||
UartMode::Uart0ToUartlite => {
|
||||
spawner.spawn(uartlite_task(uartlite_tx)).unwrap();
|
||||
spawner.spawn(uart_0_task(uart_0_tx)).unwrap();
|
||||
}
|
||||
UartMode::Uart0ToUart16550 => {
|
||||
spawner.spawn(uart_0_task(uart_0_tx)).unwrap();
|
||||
spawner.spawn(uart_16550_task(uart_16550_tx)).unwrap();
|
||||
}
|
||||
UartMode::UartliteToUart16550 => {
|
||||
spawner.spawn(uartlite_task(uartlite_tx)).unwrap();
|
||||
spawner.spawn(uart_16550_task(uart_16550_tx)).unwrap();
|
||||
}
|
||||
}
|
||||
let mut read_buf: [u8; RB_SIZE] = [0; RB_SIZE];
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_millis(200));
|
||||
loop {
|
||||
let read_bytes = uart_0_cons.len();
|
||||
(0..read_bytes).for_each(|i| {
|
||||
read_buf[i] = unsafe { uart_0_cons.dequeue_unchecked() };
|
||||
});
|
||||
if read_bytes > 0 {
|
||||
info!("Received {} bytes on UART0", read_bytes);
|
||||
info!(
|
||||
"Data: {:?}",
|
||||
core::str::from_utf8(&read_buf[0..read_bytes]).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
let read_bytes = uartlite_cons.len();
|
||||
(0..read_bytes).for_each(|i| {
|
||||
read_buf[i] = unsafe { uartlite_cons.dequeue_unchecked() };
|
||||
});
|
||||
if read_bytes > 0 {
|
||||
info!("Received {} bytes on UARTLITE", read_bytes);
|
||||
info!(
|
||||
"Data: {:?}",
|
||||
core::str::from_utf8(&read_buf[0..read_bytes]).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
let read_bytes = uart16550_cons.len();
|
||||
(0..read_bytes).for_each(|i| {
|
||||
read_buf[i] = unsafe { uart16550_cons.dequeue_unchecked() };
|
||||
});
|
||||
if read_bytes > 0 {
|
||||
info!("Received {} bytes on UART16550", read_bytes);
|
||||
info!(
|
||||
"Data: {:?}",
|
||||
core::str::from_utf8(&read_buf[0..read_bytes]).unwrap()
|
||||
);
|
||||
}
|
||||
ticker.next().await; // Wait for the next cycle of the ticker
|
||||
}
|
||||
}
|
||||
|
||||
fn build_print_string(prefix: &str, base_str: &str) -> alloc::string::String {
|
||||
format!("{} {}\n\r", prefix, base_str)
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn led_task(mut mio_led: MioPin<Mio7, Output>, mut emio_leds: [EmioPin; 8]) {
|
||||
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||
let mut led_idx = 0;
|
||||
loop {
|
||||
mio_led.toggle().unwrap();
|
||||
|
||||
emio_leds[led_idx].toggle().unwrap();
|
||||
led_idx += 1;
|
||||
if led_idx >= emio_leds.len() {
|
||||
led_idx = 0;
|
||||
}
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn uartlite_task(uartlite: axi_uartlite::Tx) {
|
||||
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||
let str0 = build_print_string("UARTLITE:", "Hello World");
|
||||
let str1 = build_print_string(
|
||||
"UARTLITE:",
|
||||
"Long Hello which should completely fill the FIFO",
|
||||
);
|
||||
let mut idx = 0;
|
||||
let print_strs = [str0.as_bytes(), str1.as_bytes()];
|
||||
let mut tx_async = axi_uartlite::tx_async::TxAsync::new(uartlite, 0).unwrap();
|
||||
loop {
|
||||
tx_async.write(print_strs[idx]).await;
|
||||
idx += 1;
|
||||
if idx == 2 {
|
||||
idx = 0;
|
||||
}
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn uart_0_task(uart_tx: zynq7000_hal::uart::Tx) {
|
||||
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||
let mut tx_async = zynq7000_hal::uart::TxAsync::new(uart_tx);
|
||||
let str0 = build_print_string("UART0:", "Hello World");
|
||||
let str1 = build_print_string(
|
||||
"UART0:",
|
||||
"Long Hello which should completely fill the 64 byte FIFO of the UART0 peripheral",
|
||||
);
|
||||
let mut idx = 0;
|
||||
let print_strs = [str0.as_bytes(), str1.as_bytes()];
|
||||
loop {
|
||||
tx_async.write(print_strs[idx]).await;
|
||||
idx += 1;
|
||||
if idx == 2 {
|
||||
idx = 0;
|
||||
}
|
||||
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn uart_16550_task(uart_tx: axi_uart16550::Tx) {
|
||||
let mut ticker = Ticker::every(Duration::from_millis(1000));
|
||||
let mut tx_async = axi_uart16550::TxAsync::new(uart_tx, 0, embassy_time::Delay).unwrap();
|
||||
let str0 = build_print_string("UART16550:", "Hello World");
|
||||
let str1 = build_print_string(
|
||||
"UART16550:",
|
||||
"Long Hello which should completely fill the FIFO",
|
||||
);
|
||||
let mut idx = 0;
|
||||
let print_strs = [str0.as_bytes(), str1.as_bytes()];
|
||||
loop {
|
||||
tx_async.write(print_strs[idx]).await;
|
||||
idx += 1;
|
||||
if idx == 2 {
|
||||
idx = 0;
|
||||
}
|
||||
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
|
||||
match irq_info.interrupt() {
|
||||
Interrupt::Sgi(_) => (),
|
||||
Interrupt::Ppi(ppi_interrupt) => {
|
||||
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||
unsafe {
|
||||
zynq7000_embassy::on_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
Interrupt::Spi(spi_interrupt) => match spi_interrupt {
|
||||
zynq7000_hal::gic::SpiInterrupt::Pl0 => {
|
||||
on_interrupt_axi_uartlite();
|
||||
}
|
||||
zynq7000_hal::gic::SpiInterrupt::Pl1 => {
|
||||
on_interrupt_axi_16550();
|
||||
}
|
||||
zynq7000_hal::gic::SpiInterrupt::Uart0 => {
|
||||
on_interrupt_uart_0();
|
||||
}
|
||||
|
||||
_ => (),
|
||||
},
|
||||
Interrupt::Invalid(_) => (),
|
||||
Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
pub fn on_interrupt_axi_uartlite() {
|
||||
let mut buf = [0; axi_uartlite::FIFO_DEPTH];
|
||||
let mut rx = unsafe { axi_uartlite::Rx::steal(AXI_UARTLITE_BASE_ADDR as usize) };
|
||||
// Handle RX first: Empty the FIFO content into local buffer.
|
||||
let read_bytes = rx.on_interrupt_rx(&mut buf);
|
||||
// Handle TX next: Handle pending asynchronous TX operations.
|
||||
let mut tx = unsafe { axi_uartlite::Tx::steal(AXI_UARTLITE_BASE_ADDR as usize) };
|
||||
axi_uartlite::on_interrupt_tx(&mut tx, 0);
|
||||
// Send received RX data to main task.
|
||||
if read_bytes > 0 {
|
||||
critical_section::with(|cs| {
|
||||
let mut prod_ref_mut = UARTLITE_PROD.borrow(cs).borrow_mut();
|
||||
let prod = prod_ref_mut.as_mut().unwrap();
|
||||
(0..read_bytes).for_each(|i| {
|
||||
prod.enqueue(buf[i]).expect("failed to enqueue byte");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_interrupt_axi_16550() {
|
||||
let mut buf = [0; axi_uart16550::FIFO_DEPTH];
|
||||
let mut read_bytes = 0;
|
||||
let mut rx = unsafe { axi_uart16550::Rx::steal(AXI_UAR16550_BASE_ADDR as usize) };
|
||||
let iir = rx.read_iir();
|
||||
if let Ok(int_id) = iir.int_id() {
|
||||
match int_id {
|
||||
axi_uart16550::registers::IntId2::ReceiverLineStatus => {
|
||||
let errors = rx.on_interrupt_receiver_line_status(iir);
|
||||
warn!("Receiver line status error: {:?}", errors);
|
||||
}
|
||||
axi_uart16550::registers::IntId2::RxDataAvailable
|
||||
| axi_uart16550::registers::IntId2::CharTimeout => {
|
||||
read_bytes = rx.on_interrupt_data_available_or_char_timeout(int_id, &mut buf);
|
||||
}
|
||||
axi_uart16550::registers::IntId2::ThrEmpty => {
|
||||
let mut tx = unsafe { axi_uart16550::Tx::steal(AXI_UAR16550_BASE_ADDR as usize) };
|
||||
axi_uart16550::tx_async::on_interrupt_tx(&mut tx, 0);
|
||||
}
|
||||
axi_uart16550::registers::IntId2::ModemStatus => (),
|
||||
}
|
||||
}
|
||||
// Send received RX data to main task.
|
||||
if read_bytes > 0 {
|
||||
critical_section::with(|cs| {
|
||||
let mut prod_ref_mut = UART16550_PROD.borrow(cs).borrow_mut();
|
||||
let prod = prod_ref_mut.as_mut().unwrap();
|
||||
(0..read_bytes).for_each(|i| {
|
||||
prod.enqueue(buf[i]).expect("failed to enqueue byte");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn on_interrupt_uart_0() {
|
||||
let mut buf = [0; zynq7000_hal::uart::FIFO_DEPTH];
|
||||
let mut read_bytes = 0;
|
||||
// Handle RX first: Empty the FIFO content into local buffer.
|
||||
critical_section::with(|cs| {
|
||||
let mut uart0 = RX_UART_0.borrow(cs).borrow_mut();
|
||||
read_bytes = uart0
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.on_interrupt(&mut buf, true)
|
||||
.read_bytes();
|
||||
});
|
||||
// Handle TX next: Handle pending asynchronous TX operations.
|
||||
zynq7000_hal::uart::on_interrupt_tx(zynq7000_hal::uart::UartId::Uart0);
|
||||
// Send received RX data to main task.
|
||||
if read_bytes > 0 {
|
||||
critical_section::with(|cs| {
|
||||
let mut prod_ref_mut = UART_0_PROD.borrow(cs).borrow_mut();
|
||||
let prod = prod_ref_mut.as_mut().unwrap();
|
||||
(0..read_bytes).for_each(|i| {
|
||||
prod.enqueue(buf[i]).expect("failed to enqueue byte");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic handler
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
error!("Panic: {:?}", info);
|
||||
loop {}
|
||||
}
|
5
examples/zedboard/src/lib.rs
Normal file
5
examples/zedboard/src/lib.rs
Normal file
@ -0,0 +1,5 @@
|
||||
#![no_std]
|
||||
use zynq7000_hal::time::Hertz;
|
||||
|
||||
// Define the clock frequency as a constant
|
||||
pub const PS_CLOCK_FREQUENCY: Hertz = Hertz::from_raw(33_333_300);
|
152
examples/zedboard/src/main.rs
Normal file
152
examples/zedboard/src/main.rs
Normal file
@ -0,0 +1,152 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
use cortex_ar::asm::nop;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use embedded_io::Write;
|
||||
use log::{error, info};
|
||||
use zedboard::PS_CLOCK_FREQUENCY;
|
||||
use zynq7000_hal::{
|
||||
BootMode,
|
||||
clocks::Clocks,
|
||||
configure_level_shifter,
|
||||
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
|
||||
gpio::{EmioPin, GpioPins, PinState},
|
||||
gtc::Gtc,
|
||||
uart::{ClkConfigRaw, Uart, UartConfig},
|
||||
};
|
||||
|
||||
use zynq7000::{PsPeripherals, slcr::LevelShifterConfig};
|
||||
use zynq7000_rt as _;
|
||||
|
||||
const INIT_STRING: &str = "-- Zynq 7000 Zedboard GPIO blinky example --\n\r";
|
||||
|
||||
/// Entry point (not called like a normal main function)
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn boot_core(cpu_id: u32) -> ! {
|
||||
if cpu_id != 0 {
|
||||
panic!("unexpected CPU ID {}", cpu_id);
|
||||
}
|
||||
main();
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
#[unsafe(export_name = "main")]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
// Enable PS-PL level shifters.
|
||||
configure_level_shifter(LevelShifterConfig::EnableAll);
|
||||
let dp = PsPeripherals::take().unwrap();
|
||||
// Clock was already initialized by PS7 Init TCL script or FSBL, we just read it.
|
||||
let clocks = Clocks::new_from_regs(PS_CLOCK_FREQUENCY).unwrap();
|
||||
// Set up the global interrupt controller.
|
||||
let mut gic = GicConfigurator::new_with_init(dp.gicc, dp.gicd);
|
||||
gic.enable_all_interrupts();
|
||||
gic.set_all_spi_interrupt_targets_cpu0();
|
||||
gic.enable();
|
||||
unsafe {
|
||||
gic.enable_interrupts();
|
||||
}
|
||||
let mut gpio_pins = GpioPins::new(dp.gpio);
|
||||
|
||||
// Set up global timer counter and embassy time driver.
|
||||
let gtc = Gtc::new(dp.gtc, clocks.arm_clocks());
|
||||
zynq7000_embassy::init(clocks.arm_clocks(), gtc);
|
||||
|
||||
// Set up the UART, we are logging with it.
|
||||
let uart_clk_config = ClkConfigRaw::new_autocalc_with_error(clocks.io_clocks(), 115200)
|
||||
.unwrap()
|
||||
.0;
|
||||
let uart_tx = gpio_pins.mio.mio48.into_uart();
|
||||
let uart_rx = gpio_pins.mio.mio49.into_uart();
|
||||
let mut uart = Uart::new_with_mio(
|
||||
dp.uart_1,
|
||||
UartConfig::new_with_clk_config(uart_clk_config),
|
||||
(uart_tx, uart_rx),
|
||||
)
|
||||
.unwrap();
|
||||
uart.write_all(INIT_STRING.as_bytes()).unwrap();
|
||||
// Safety: We are not multi-threaded yet.
|
||||
unsafe { zynq7000_hal::log::init_unsafe_single_core(uart, log::LevelFilter::Trace, false) };
|
||||
|
||||
let boot_mode = BootMode::new();
|
||||
info!("Boot mode: {:?}", boot_mode);
|
||||
|
||||
let mut ticker = Ticker::every(Duration::from_millis(200));
|
||||
let mut mio_led = gpio_pins.mio.mio7.into_output(PinState::Low);
|
||||
|
||||
let mut emio_leds: [EmioPin; 8] = [
|
||||
gpio_pins.emio.take(0).unwrap(),
|
||||
gpio_pins.emio.take(1).unwrap(),
|
||||
gpio_pins.emio.take(2).unwrap(),
|
||||
gpio_pins.emio.take(3).unwrap(),
|
||||
gpio_pins.emio.take(4).unwrap(),
|
||||
gpio_pins.emio.take(5).unwrap(),
|
||||
gpio_pins.emio.take(6).unwrap(),
|
||||
gpio_pins.emio.take(7).unwrap(),
|
||||
];
|
||||
for led in emio_leds.iter_mut() {
|
||||
led.into_output(PinState::Low);
|
||||
}
|
||||
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; // Wait for the next cycle of the ticker
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _irq_handler() {
|
||||
let mut gic_helper = GicInterruptHelper::new();
|
||||
let irq_info = gic_helper.acknowledge_interrupt();
|
||||
match irq_info.interrupt() {
|
||||
Interrupt::Sgi(_) => (),
|
||||
Interrupt::Ppi(ppi_interrupt) => {
|
||||
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
|
||||
unsafe {
|
||||
zynq7000_embassy::on_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
Interrupt::Spi(_spi_interrupt) => (),
|
||||
Interrupt::Invalid(_) => (),
|
||||
Interrupt::Spurious => (),
|
||||
}
|
||||
gic_helper.end_of_interrupt(irq_info);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _abort_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _undefined_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _prefetch_handler() {
|
||||
loop {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Panic handler
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
error!("Panic: {:?}", info);
|
||||
loop {}
|
||||
}
|
Reference in New Issue
Block a user