Some checks failed
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
575 lines
20 KiB
Rust
575 lines
20 KiB
Rust
//! 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::GlobalTimerCounter,
|
|
l2_cache,
|
|
time::Hertz,
|
|
uart::{ClockConfigRaw, 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) -> ! {
|
|
let mut dp = PsPeripherals::take().unwrap();
|
|
l2_cache::init_with_defaults(&mut dp.l2c);
|
|
|
|
// Enable PS-PL level shifters.
|
|
configure_level_shifter(LevelShifterConfig::EnableAll);
|
|
|
|
// 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 = GlobalTimerCounter::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 = ClockConfigRaw::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_from_regs();
|
|
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 {read_bytes} bytes on UART0");
|
|
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 {read_bytes} bytes on UARTLITE");
|
|
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 {read_bytes} bytes on UART16550");
|
|
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!("{prefix} {base_str}\n\r")
|
|
}
|
|
|
|
#[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 {}
|
|
}
|