init commit

This commit is contained in:
2025-02-19 11:00:04 +01:00
commit 0f2bda8ca1
111 changed files with 21616 additions and 0 deletions

View 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" }

View 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 {}
}

View 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"

View 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 {}
}

View 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 {}
}

View 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();
}
}

View 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" }

View 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 {}
}

View 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 {}
}

View 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 {}
}

View 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 {}
}

View 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);

View 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 {}
}