From d9c26aa52565ca814fdcbde9dfee990e63a165cb Mon Sep 17 00:00:00 2001 From: Tropical <42101043+Tropix126@users.noreply.github.com> Date: Tue, 20 Aug 2024 17:16:29 -0500 Subject: [PATCH] feat: UART receive interrupt --- kernel/src/hardware/uart.rs | 22 +++++++++++++++++++++- kernel/src/main.rs | 21 +++++++++++---------- kernel/src/peripherals.rs | 33 ++++++++++++++++++++++++++++++++- kernel/src/vectors.rs | 25 ++++++++++++++++++------- 4 files changed, 82 insertions(+), 19 deletions(-) diff --git a/kernel/src/hardware/uart.rs b/kernel/src/hardware/uart.rs index af6cc81..eb54a1a 100644 --- a/kernel/src/hardware/uart.rs +++ b/kernel/src/hardware/uart.rs @@ -1,4 +1,4 @@ -use core::convert::Infallible; +use core::{convert::Infallible, ffi::c_void}; use embedded_io::{ErrorType, Read, Write}; use snafu::Snafu; @@ -44,6 +44,8 @@ pub struct UartDriver { } impl UartDriver { + pub const INTERRUPT_ID: u32 = 82; + /// Initialize the UART driver with the given base address. /// /// # Parameters @@ -75,6 +77,24 @@ impl UartDriver { UartDriverError::try_from_xst_status(status) } + pub fn set_interrupt_mask(&mut self, mask: u32) { + unsafe { + XUartPs_SetInterruptMask(&mut self.instance, mask); + } + } + + #[allow(clippy::not_unsafe_ptr_arg_deref)] + pub fn set_handler(&mut self, handler: XUartPs_Handler, data: *mut c_void) { + unsafe { + XUartPs_SetHandler(&mut self.instance, handler, data); + } + } + + #[inline] + pub unsafe extern "C" fn interrupt_handler(data: *mut c_void) { + unsafe { XUartPs_InterruptHandler(core::mem::transmute::<*mut c_void, *mut XUartPs>(data)) } + } + pub fn raw_mut(&mut self) -> &mut XUartPs { &mut self.instance } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 7d7f0b3..ce6851e 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -72,17 +72,9 @@ pub extern "C" fn _start() -> ! { allocator::init_heap(); } - // Force-initialize all peripherals. - // - // If they fail to initialize, we want them to fail now rather than whenever - // they're first accessed. - GIC.force(); - PRIVATE_TIMER.force(); + // The watchdog timer isn't locked until vexSystemWatchdogReinitRtos, which could be + // a while into program execution so we'll force initialize it here. WATCHDOG_TIMER.force(); - UART1.force(); - - // Initialize UART kernel logger - LOGGER.init(LevelFilter::Debug).unwrap(); // Setup private timer peripheral and register a tick interrupt handler using // the GIC. @@ -92,10 +84,19 @@ pub extern "C" fn _start() -> ! { // FreeRTOS if needed. peripherals::setup_private_timer().unwrap(); + // Configure UART1 to fire an interrupt when new data is received from the host. + peripherals::setup_uart1().unwrap(); + + // Initialize UART kernel logger + LOGGER.init(LevelFilter::Debug).unwrap(); + // Send/receive a handshake packet with the host to ensure that packet transfer // is viable. log::debug!("Handshaking with host..."); protocol::send_packet(HostBoundPacket::Handshake).expect("Failed to handshake with host."); + while !WAITING_FOR_HANDSHAKE { + core::hint::spin_loop(); + } while protocol::recv_packet().expect("Failed to receive handshake packet from host.") != Some(KernelBoundPacket::Handshake) { diff --git a/kernel/src/peripherals.rs b/kernel/src/peripherals.rs index c9ebd5f..12d3367 100644 --- a/kernel/src/peripherals.rs +++ b/kernel/src/peripherals.rs @@ -16,7 +16,7 @@ use crate::{ XScuTimer, XScuTimer_ClearInterruptStatus, XScuTimer_IsExpired, XPAR_XSCUTIMER_0_BASEADDR, }, - uart::XPAR_XUARTPS_1_BASEADDR, + uart::{XUartPs, XPAR_XUARTPS_1_BASEADDR, XUARTPS_EVENT_RECV_DATA, XUARTPS_IXR_MASK, XUARTPS_IXR_RXOVR, XUARTPS_IXR_TTRIG}, wdt::XPAR_XSCUWDT_0_BASEADDR, }, }; @@ -76,6 +76,12 @@ pub extern "C" fn timer_interrupt_handler(timer: *mut c_void) { // [`vexSystemTimerReinitForRtos`] anyways... } +pub extern "C" fn uart_interrupt_handler(_: *mut c_void, event: u32, event_data: u32) { + assert!(event == XUARTPS_EVENT_RECV_DATA); + + log::debug!("UART Interrupt"); +} + /// Configures the Private Timer peripheral and registers an interrupt handler /// for timer ticks using the Generic Interrupt Controller (GIC). pub fn setup_private_timer() -> Result<(), GicError> { @@ -124,3 +130,28 @@ pub fn setup_private_timer() -> Result<(), GicError> { Ok(()) } + +pub fn setup_uart1() -> Result<(), GicError> { + let mut uart = UART1.lock(); + let mut gic = GIC.lock(); + + // Mask out all interrupts except receives. + uart.set_interrupt_mask(XUARTPS_IXR_MASK | XUARTPS_IXR_RXOVR); + + // Register the driver's interrupt handler with the GIC. + gic.set_handler( + UartDriver::INTERRUPT_ID, + 0, // priority + InterruptTrigger::RisingEdge, + UartDriver::interrupt_handler, + uart.raw_mut() as *mut XUartPs as _, + )?; + + // Handle interrupts through [`uart_interrupt_handler`]. + uart.set_handler(uart_interrupt_handler, core::ptr::null_mut()); + + // Enable UART IRQ. + gic.enable_interrupt(UartDriver::INTERRUPT_ID); + + Ok(()) +} diff --git a/kernel/src/vectors.rs b/kernel/src/vectors.rs index f01e9ec..fdf6c07 100644 --- a/kernel/src/vectors.rs +++ b/kernel/src/vectors.rs @@ -8,9 +8,20 @@ //! In most cases, these functions are copied off of Xilinx's `asm_vectors.s` //! file: -use core::{arch::{asm, global_asm}, ffi::c_void}; +use core::{ + arch::{asm, global_asm}, + ffi::c_void, +}; -use crate::{sdk::{vexSystemDataAbortInterrupt, vexSystemPrefetchAbortInterrupt, vexSystemUndefinedException}, xil::exception::{Xil_ExceptionRegisterHandler, XIL_EXCEPTION_ID_DATA_ABORT_INT, XIL_EXCEPTION_ID_PREFETCH_ABORT_INT, XIL_EXCEPTION_ID_UNDEFINED_INT}}; +use crate::{ + sdk::{ + vexSystemDataAbortInterrupt, vexSystemPrefetchAbortInterrupt, vexSystemUndefinedException, + }, + xil::exception::{ + Xil_ExceptionRegisterHandler, XIL_EXCEPTION_ID_DATA_ABORT_INT, + XIL_EXCEPTION_ID_PREFETCH_ABORT_INT, XIL_EXCEPTION_ID_UNDEFINED_INT, + }, +}; // The exception vector table. // @@ -265,12 +276,12 @@ pub extern "C" fn fiq() -> ! { } } -/// VEX handles the user-facing part of exceptions through xilinx's own exception -/// table API, so this function registers those on the table. +/// VEX handles the user-facing part of exceptions through xilinx's own +/// exception table API, so this function registers those on the table. /// -/// Those functions are what actually convey to the user that an exception occurs -/// (for example vexSystemDataAbortInterrupt is responsible for drawing a red box -/// to the screen). +/// Those functions are what actually convey to the user that an exception +/// occurs (for example vexSystemDataAbortInterrupt is responsible for drawing a +/// red box to the screen). pub fn register_sdk_exception_handlers() { #[inline] pub extern "C" fn data_abort_handler_thunk(_: *mut c_void) {