init commit

This commit is contained in:
2025-02-19 11:00:04 +01:00
commit f842673e3a
104 changed files with 21595 additions and 0 deletions

View File

@ -0,0 +1,39 @@
[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" }
# dht-sensor = { git = "https://github.com/robamu/dht-sensor.git", branch = "bump-embedded-hal-deps-update-async", features = ["async"] }
dht-sensor = { path = "../../../../Rust/dht-sensor", features = ["async"] }
static_cell = "2"
critical-section = "1"
heapless = "0.8"
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",
"task-arena-size-65536"
]}
embassy-time = { path = "/home/rmueller/Rust/embassy/embassy-time", version = "0.4", features = ["tick-hz-1_000_000"] }
[profile.release]
codegen-units = 1
debug = true
lto = true

View File

@ -0,0 +1,207 @@
//! Open-drain pin mode examples
#![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::{delay::DelayNs, digital::StatefulOutputPin};
use embedded_io::Write;
use log::{error, info, warn};
use zynq7000_hal::{
clocks::Clocks,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
gpio::{mio, Flex, Output, PinState},
gtc::Gtc,
time::Hertz,
uart::{ClkConfigRaw, Uart, UartConfig},
BootMode,
};
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();
}
/// Try to talk to a DHT22 sensor connected at MIO0.
const DHT22_AT_MIO0: bool = true;
/// Open drain pin testing. MIO9 needs to be tied to MIO14.
const OPEN_DRAIN_PINS_MIO9_TO_MIO14: bool = false;
#[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 = mio::Pins::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 mut uart = Uart::new_with_mio(
dp.uart_1,
UartConfig::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
)
.unwrap();
uart.write_all(b"-- Zynq 7000 DHT22 --\n\r").unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
let mut delay = Delay;
let mut one_wire_pin = Flex::new_for_mio(mio_pins.mio0);
one_wire_pin.configure_as_output_open_drain(PinState::High, true);
if OPEN_DRAIN_PINS_MIO9_TO_MIO14 {
let mut flex_pin_0 = Flex::new_for_mio(mio_pins.mio9);
flex_pin_0.configure_as_output_open_drain(PinState::High, true);
let mut flex_pin_1 = Flex::new_for_mio(mio_pins.mio14);
flex_pin_1.configure_as_input_floating().unwrap();
// Should be high because of pull up.
info!(
"Flex Pin 1 state (should be high): {}",
flex_pin_1.is_high()
);
info!(
"Flex Pin 0 state (should be high): {}",
flex_pin_0.is_high()
);
flex_pin_0.set_low();
info!("Flex Pin 1 state (should be low): {}", flex_pin_1.is_high());
info!("Flex Pin 0 state (should be low): {}", flex_pin_0.is_high());
flex_pin_0.set_high();
delay.delay_us(5);
info!(
"Flex Pin 1 state (should be high): {}",
flex_pin_1.is_high()
);
info!(
"Flex Pin 0 state (should be high): {}",
flex_pin_0.is_high()
);
flex_pin_1.configure_as_output_open_drain(PinState::Low, true);
info!("Flex Pin 1 state (should be low): {}", flex_pin_1.is_high());
info!("Flex Pin 0 state (should be low): {}", flex_pin_0.is_high());
flex_pin_1.set_high();
delay.delay_us(5);
info!(
"Flex Pin 1 state (should be high): {}",
flex_pin_1.is_high()
);
info!(
"Flex Pin 0 state (should be high): {}",
flex_pin_0.is_high()
);
flex_pin_1.set_low();
info!("Flex Pin 1 state (should be low): {}", flex_pin_1.is_high());
info!("Flex Pin 0 state (should be low): {}", flex_pin_0.is_high());
}
let boot_mode = BootMode::new();
info!("Boot mode: {:?}", boot_mode);
let mut ticker = Ticker::every(Duration::from_millis(1000));
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
loop {
if DHT22_AT_MIO0 {
let result = dht_sensor::dht22::asynch::read(&mut delay, &mut one_wire_pin).await;
match result {
Ok(reading) => {
info!("Temperature: {} C", reading.temperature);
info!("Humidity: {} %", reading.relative_humidity);
}
Err(err) => {
warn!("Reading error: {:?}", err);
}
}
}
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,149 @@
//! Example which uses the UART1 to send log messages.
#![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::PsPeripherals;
use zynq7000_hal::{
BootMode,
clocks::Clocks,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
gpio::{Output, PinState, mio},
gtc::Gtc,
time::Hertz,
uart::{ClkConfigRaw, TxAsync, Uart, UartConfig, on_interrupt_tx},
};
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();
}
#[unsafe(export_name = "main")]
#[embassy_executor::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();
}
// 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);
let mio_pins = mio::Pins::new(dp.gpio);
// 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 uart = Uart::new_with_mio(
dp.uart_1,
UartConfig::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
)
.unwrap();
uart.write_all(b"-- Zynq 7000 Logging example --\n\r")
.unwrap();
uart.flush().unwrap();
let (tx, _rx) = uart.split();
let mut logger = TxAsync::new(tx);
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
let boot_mode = BootMode::new();
info!("Boot mode: {:?}", boot_mode);
let led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
spawner.spawn(led_task(led)).unwrap();
let mut log_buf: [u8; 2048] = [0; 2048];
let frame_queue = zynq7000_hal::log::rb::get_frame_queue();
loop {
let next_frame_len = frame_queue.receive().await;
zynq7000_hal::log::rb::read_next_frame(next_frame_len, &mut log_buf);
logger.write(&log_buf[0..next_frame_len]).await;
}
}
#[embassy_executor::task]
async fn led_task(mut mio_led: Output) {
let mut ticker = Ticker::every(Duration::from_millis(1000));
loop {
mio_led.toggle().unwrap();
info!("Toggling LED");
ticker.next().await;
}
}
#[unsafe(no_mangle)]
pub extern "C" fn _irq_handler() {
let mut gic_helper = GicInterruptHelper::new();
let irq_info = gic_helper.acknowledge_interrupt();
match irq_info.interrupt() {
Interrupt::Sgi(_) => (),
Interrupt::Ppi(ppi_interrupt) => {
if ppi_interrupt == zynq7000_hal::gic::PpiInterrupt::GlobalTimer {
unsafe {
zynq7000_embassy::on_interrupt();
}
}
}
Interrupt::Spi(spi_interrupt) => {
if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Uart1 {
on_interrupt_tx(zynq7000_hal::uart::UartId::Uart1);
}
}
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
}
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,157 @@
//! PWM example which uses a PWM pin routed through EMIO.
//!
//! This example puts the PWM output on the EMIO channel of TTC0 channel 0.
//!
//! On the Zedboard, the PWM waveform output will be on the W12 pin of PMOD JB1. The Zedboard
//! reference FPGA design must be flashed onto the Zedboard for this to work.
#![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, pwm::SetDutyCycle};
use embedded_io::Write;
use fugit::RateExtU32;
use log::{error, info};
use zynq7000_hal::{
BootMode,
clocks::Clocks,
gic::{GicConfigurator, GicInterruptHelper, Interrupt},
gpio::{Output, PinState, mio},
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();
}
#[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 = mio::Pins::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);
// Unwrap is okay, the address is definitely valid.
let ttc_0 = zynq7000_hal::ttc::Ttc::new(dp.ttc_0).unwrap();
let mut pwm =
zynq7000_hal::ttc::Pwm::new_with_cpu_clk(ttc_0.ch0, clocks.arm_clocks(), 1000.Hz())
.unwrap();
pwm.set_duty_cycle_percent(50).unwrap();
// 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 uart = Uart::new_with_mio(
dp.uart_1,
UartConfig::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
)
.unwrap();
uart.write_all(b"-- Zynq 7000 Embassy Hello World --\n\r")
.unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
let boot_mode = BootMode::new();
info!("Boot mode: {:?}", boot_mode);
let mut ticker = Ticker::every(Duration::from_millis(1000));
let mut led = Output::new_for_mio(mio_pins.mio7, PinState::Low);
let mut current_duty = 0;
loop {
led.toggle().unwrap();
pwm.set_duty_cycle_percent(current_duty).unwrap();
info!("Setting duty cycle to {}%", current_duty);
current_duty += 5;
if current_duty > 100 {
current_duty = 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) => (),
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,136 @@
#![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::{Output, PinState, mio},
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 = mio::Pins::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 mut uart = Uart::new_with_mio(
dp.uart_1,
UartConfig::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
)
.unwrap();
uart.write_all(b"-- Zynq 7000 Embassy Hello World --\n\r")
.unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
let boot_mode = BootMode::new();
info!("Boot mode: {:?}", boot_mode);
let mut ticker = Ticker::every(Duration::from_millis(1000));
let mut led = Output::new_for_mio(mio_pins.mio7, 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,24 @@
[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"
[profile.release]
codegen-units = 1
debug = true
lto = true

View File

@ -0,0 +1,136 @@
//! 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::{Output, PinState, mio},
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 = mio::Pins::new(dp.gpio);
let mut uart = Uart::new_with_mio(
dp.uart_1,
UartConfig::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
)
.unwrap();
uart.write_all(b"-- Zynq 7000 GTC Ticks example --\n\r")
.unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
let mut led = Output::new_for_mio(mio_pins.mio7, 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,139 @@
//! 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::{Output, PinState, mio},
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 = mio::Pins::new(dp.gpio);
let mut uart = Uart::new_with_mio(
dp.uart_1,
UartConfig::new_with_clk_config(uart_clk_config),
(mio_pins.mio48, mio_pins.mio49),
)
.unwrap();
uart.write_all(b"-- Zynq 7000 Logging example --\n\r")
.unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
let boot_mode = BootMode::new();
info!("Boot mode: {:?}", boot_mode);
let mut led = Output::new_for_mio(mio_pins.mio7, 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::{Output, PinState, mio};
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 = mio::Pins::new(dp.gpio);
let mut led = Output::new_for_mio(mio_pins.mio7, 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,43 @@
[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/rust-embedded/cortex-ar", branch = "main", 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-async-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 = "/home/rmueller/Rust/axi-uartlite-rs" }
axi-uart16550 = { path = "/home/rmueller/Rust/axi-uart16550-rs" }
[profile.release]
codegen-units = 1
debug = true
lto = true

View File

@ -0,0 +1,203 @@
//! 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::{GpioPins, Output, 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 mut uart = uart::Uart::new_with_mio(
dp.uart_1,
uart::UartConfig::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
uart.write_all(b"-- Zynq 7000 Zedboard I2C L3GD20H example --\n\r")
.unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
let boot_mode = BootMode::new();
info!("Boot mode: {:?}", boot_mode);
let pin_sel = match I2C_ADDR_SEL {
I2cAddr::Sa0Low => PinState::Low,
I2cAddr::Sa0High => PinState::High,
};
let _sa0_pin = Output::new_for_mio(gpio_pins.mio.mio11, pin_sel);
// The CS pin must be pulled high.
let _cs_pin = Output::new_for_mio(gpio_pins.mio.mio10, 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,
(gpio_pins.mio.mio12, gpio_pins.mio.mio13),
)
.unwrap();
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 delay = Delay;
let mut ticker = Ticker::every(Duration::from_millis(400));
let mut mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
let mut emio_leds: [Output; 8] = [
Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), PinState::Low),
];
for (idx, led) in emio_leds.iter_mut().enumerate() {
if idx % 2 == 0 {
led.set_high();
} else {
led.set_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,292 @@
//! 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::{GpioPins, Output, PinState},
gtc::Gtc,
spi::{self, SpiAsync, SpiId, SpiWithHwCs, SpiWithHwCsAsync, on_interrupt},
time::Hertz,
uart::{self, TxAsync, on_interrupt_tx},
};
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;
const BLOCKING: 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 mut uart = uart::Uart::new_with_mio(
dp.uart_1,
uart::UartConfig::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
uart.write_all(b"-- Zynq 7000 Zedboard SPI L3GD20H example --\n\r")
.unwrap();
zynq7000_hal::log::rb::init(log::LevelFilter::Trace);
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 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,
),
(
gpio_pins.mio.mio12,
gpio_pins.mio.mio10,
gpio_pins.mio.mio11,
),
gpio_pins.mio.mio13,
)
.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.inner().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 mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
let mut emio_leds: [Output; 8] = [
Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), PinState::Low),
];
for (idx, led) in emio_leds.iter_mut().enumerate() {
if idx % 2 == 0 {
led.set_high();
} else {
led.set_low();
}
}
spawner.spawn(logger_task(uart)).unwrap();
if BLOCKING {
blocking_application(mio_led, emio_leds, spi).await;
} else {
non_blocking_application(mio_led, emio_leds, spi).await;
}
}
#[embassy_executor::task]
pub async fn logger_task(uart: uart::Uart) {
let (tx, _) = uart.split();
let mut tx_async = TxAsync::new(tx);
let frame_queue = zynq7000_hal::log::rb::get_frame_queue();
let mut log_buf: [u8; 2048] = [0; 2048];
loop {
let next_frame_len = frame_queue.receive().await;
zynq7000_hal::log::rb::read_next_frame(next_frame_len, &mut log_buf);
tx_async.write(&log_buf[0..next_frame_len]).await;
}
}
pub async fn blocking_application(
mut mio_led: Output,
mut emio_leds: [Output; 8],
spi: spi::Spi,
) -> ! {
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));
// 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
}
}
pub async fn non_blocking_application(
mut mio_led: Output,
mut emio_leds: [Output; 8],
spi: spi::Spi,
) -> ! {
let mut delay = Delay;
let spi_async = SpiAsync::new(spi);
let spi_dev = SpiWithHwCsAsync::new(spi_async, spi::ChipSelect::Slave0, delay.clone());
let mut l3gd20 = l3gd20::asynchronous::spi::L3gd20::new(spi_dev)
.await
.unwrap();
let who_am_i = l3gd20.who_am_i().await.unwrap();
info!("L3GD20 WHO_AM_I: 0x{:02X}", who_am_i);
let mut ticker = Ticker::every(Duration::from_millis(400));
// Power up time for the sensor to get good readings.
delay.delay_ms(50).await;
loop {
mio_led.toggle().unwrap();
let measurements = l3gd20.all().await.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) => {
if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Spi1 {
on_interrupt(SpiId::Spi1);
} else if spi_interrupt == zynq7000_hal::gic::SpiInterrupt::Uart1 {
on_interrupt_tx(zynq7000_hal::uart::UartId::Uart1);
}
}
Interrupt::Invalid(_) => (),
Interrupt::Spurious => (),
}
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,264 @@
#![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::{GpioPins, Output, 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: [Output; 3],
}
impl UartMultiplexer {
pub fn new(mut sel_pins: [Output; 3]) -> Self {
for pin in sel_pins.iter_mut() {
pin.set_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();
self.sel_pins[1].set_low();
self.sel_pins[0].set_low();
}
UartSel::Uartlite => {
self.sel_pins[2].set_low();
self.sel_pins[1].set_low();
self.sel_pins[0].set_high();
}
UartSel::Uart16550 => {
self.sel_pins[2].set_low();
self.sel_pins[1].set_high();
self.sel_pins[0].set_low();
}
UartSel::Uart0ToUartlite => {
self.sel_pins[2].set_low();
self.sel_pins[1].set_high();
self.sel_pins[0].set_high();
}
UartSel::Uart0ToUart16550 => {
self.sel_pins[2].set_high();
self.sel_pins[1].set_low();
self.sel_pins[0].set_low();
}
UartSel::UartliteToUart16550 => {
self.sel_pins[2].set_high();
self.sel_pins[1].set_low();
self.sel_pins[0].set_high();
}
}
}
}
#[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 mut log_uart = Uart::new_with_mio(
dp.uart_1,
UartConfig::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
log_uart.write_all(INIT_STRING.as_bytes()).unwrap();
// Safety: Co-operative multi-tasking is used.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
log_uart,
log::LevelFilter::Trace,
false,
)
};
// 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 = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
let mut emio_leds: [Output; 8] = [
Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), PinState::Low),
];
let mut uart_mux = UartMultiplexer::new([
Output::new_for_emio(gpio_pins.emio.take(8).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(9).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(10).unwrap(), PinState::Low),
]);
let mut 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,570 @@
//! 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::{GpioPins, 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: [Output; 3],
}
impl UartMultiplexer {
pub fn new(mut sel_pins: [Output; 3]) -> Self {
for pin in sel_pins.iter_mut() {
pin.set_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();
self.sel_pins[1].set_low();
self.sel_pins[0].set_low();
}
UartSel::Uartlite => {
self.sel_pins[2].set_low();
self.sel_pins[1].set_low();
self.sel_pins[0].set_high();
}
UartSel::Uart16550 => {
self.sel_pins[2].set_low();
self.sel_pins[1].set_high();
self.sel_pins[0].set_low();
}
UartSel::Uart0ToUartlite => {
self.sel_pins[2].set_low();
self.sel_pins[1].set_high();
self.sel_pins[0].set_high();
}
UartSel::Uart0ToUart16550 => {
self.sel_pins[2].set_high();
self.sel_pins[1].set_low();
self.sel_pins[0].set_low();
}
UartSel::UartliteToUart16550 => {
self.sel_pins[2].set_high();
self.sel_pins[1].set_low();
self.sel_pins[0].set_high();
}
}
}
}
#[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 mut log_uart = Uart::new_with_mio(
dp.uart_1,
UartConfig::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.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::uart_blocking::init_unsafe_single_core(
log_uart,
log::LevelFilter::Trace,
false,
)
};
// Set up UART multiplexing before creating and configuring the UARTs.
let mut uart_mux = UartMultiplexer::new([
Output::new_for_emio(gpio_pins.emio.take(8).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(9).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(10).unwrap(), PinState::Low),
]);
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 = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
let emio_leds: [Output; 8] = [
Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), 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: Output, mut emio_leds: [Output; 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,153 @@
#![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::{GpioPins, Output, 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 mut uart = Uart::new_with_mio(
dp.uart_1,
UartConfig::new_with_clk_config(uart_clk_config),
(gpio_pins.mio.mio48, gpio_pins.mio.mio49),
)
.unwrap();
uart.write_all(INIT_STRING.as_bytes()).unwrap();
// Safety: We are not multi-threaded yet.
unsafe {
zynq7000_hal::log::uart_blocking::init_unsafe_single_core(
uart,
log::LevelFilter::Trace,
false,
)
};
let boot_mode = BootMode::new();
info!("Boot mode: {:?}", boot_mode);
let mut ticker = Ticker::every(Duration::from_millis(200));
let mut mio_led = Output::new_for_mio(gpio_pins.mio.mio7, PinState::Low);
let mut emio_leds: [Output; 8] = [
Output::new_for_emio(gpio_pins.emio.take(0).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(1).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(2).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(3).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(4).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(5).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(6).unwrap(), PinState::Low),
Output::new_for_emio(gpio_pins.emio.take(7).unwrap(), 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 {}
}