diff --git a/libs/xous-pio/src/lib.rs b/libs/xous-pio/src/lib.rs index 471204b42..12b6e30f0 100644 --- a/libs/xous-pio/src/lib.rs +++ b/libs/xous-pio/src/lib.rs @@ -947,6 +947,11 @@ impl PioSm { (self.pio.r(rp_pio::SFR_IRQ) & (1 << int_number)) != 0 } + pub fn sm_interrupt_clear(&mut self, int_number: usize) { + assert!(int_number < 8); + self.pio.wo(rp_pio::SFR_IRQ, 1 << int_number) + } + pub fn sm_drain_tx_fifo(&mut self) { let sm_offset = self.sm_to_stride_offset(); let instr = { diff --git a/services/cram-hal-service/Cargo.toml b/services/cram-hal-service/Cargo.toml index cd9e48e51..87e98926a 100644 --- a/services/cram-hal-service/Cargo.toml +++ b/services/cram-hal-service/Cargo.toml @@ -31,6 +31,6 @@ rkyv = { version = "0.4.3", default-features = false, features = [ ] } [features] -pio = [] -pio-memlcd = [] +# add this feature to enable pre-emption +quantum-timer = ["utralib"] default = [] diff --git a/services/cram-hal-service/src/main.rs b/services/cram-hal-service/src/main.rs index 85c310fe7..5c9100fdd 100644 --- a/services/cram-hal-service/src/main.rs +++ b/services/cram-hal-service/src/main.rs @@ -1,12 +1,28 @@ mod api; use api::*; -#[cfg(feature = "pio")] -mod pio; use cramium_hal::{ iox, udma::{EventChannel, GlobalConfig, PeriphId}, }; +#[cfg(feature = "quantum-timer")] +use utralib::utra; +#[cfg(feature = "quantum-timer")] +use utralib::*; use xous::sender::Sender; +use xous_pio::*; + +#[cfg(feature = "quantum-timer")] +fn timer_tick(_irq_no: usize, arg: *mut usize) { + let sm_a = unsafe { &mut *(arg as *mut PioSm) }; + // this call forces preemption every timer tick + // rsyscalls are "raw syscalls" -- used for syscalls that don't have a friendly wrapper around them + // since ReturnToParent is only used here, we haven't wrapped it, so we use an rsyscall + xous::rsyscall(xous::SysCall::ReturnToParent(xous::PID::new(1).unwrap(), 0)) + .expect("couldn't return to parent"); + + // acknowledge the timer + sm_a.sm_interrupt_clear(1); +} fn try_alloc(ifram_allocs: &mut Vec>, size: usize, sender: Sender) -> Option { let mut size_pages = size / 4096; @@ -97,6 +113,49 @@ fn main() { .expect("couldn't map UDMA global control"); let mut udma_global = GlobalConfig::new(udma_global_csr.as_mut_ptr() as *mut u32); + #[cfg(feature = "quantum-timer")] + { + let mut pio_ss = xous_pio::PioSharedState::new(); + let mut sm_a = pio_ss.alloc_sm().unwrap(); + // claim the IRQ for the quanta timer + xous::claim_interrupt( + utralib::LITEX_IRQARRAY18_INTERRUPT, + timer_tick, + &mut sm_a as *mut PioSm as *mut usize, + ) + .expect("couldn't claim IRQ"); + + pio_ss.clear_instruction_memory(); + pio_ss.pio.rmwf(utra::rp_pio::SFR_CTRL_EN, 0); + #[rustfmt::skip] + let timer_code = pio_proc::pio_asm!( + "restart:", + "set x, 6", // 4 cycles overhead gets us to 10 iterations per pulse + "waitloop:", + "jmp x-- waitloop", + "irq set 0", + "jmp restart", + ); + let a_prog = LoadedProg::load(timer_code.program, &mut pio_ss).unwrap(); + sm_a.sm_set_enabled(false); + a_prog.setup_default_config(&mut sm_a); + sm_a.config_set_clkdiv(50_000.0f32); // set to 1ms per iteration + sm_a.sm_init(a_prog.entry()); + sm_a.sm_irq0_source_enabled(PioIntSource::Sm, true); + sm_a.sm_set_enabled(true); + + // map and enable the interrupt for the PIO system timer + let irq18_page = xous::syscall::map_memory( + xous::MemoryAddress::new(utralib::generated::HW_IOX_BASE), + None, + 4096, + xous::MemoryFlags::R | xous::MemoryFlags::W, + ) + .expect("couldn't claim irq18 csr"); + let mut irq18_csr = CSR::new(irq18_page.as_mut_ptr() as *mut u32); + irq18_csr.wfo(utra::irqarray18::EV_ENABLE_PIOIRQ0_DUPE, 1); + } + let mut msg_opt = None; log::debug!("Starting main loop"); loop { diff --git a/services/cram-hal-service/src/pio.rs b/services/cram-hal-service/src/pio.rs deleted file mode 100644 index a262b9920..000000000 --- a/services/cram-hal-service/src/pio.rs +++ /dev/null @@ -1,31 +0,0 @@ -use cramium_hal::iox; -use xous_pio::*; - -/// This is the start of the memory LCD driver program, but aborted because we now -/// realize the UDMA SPI block is much better suited for this task. -fn pio_memlcd_program() { - #[rustfmt::skip] - let memlcd_line = pio_proc::pio_asm!( - // The PIO engine is configured as a simple 16-bit, LSB-first shift register - // to send data to the LCD at a rate of 2 MHz, requesting a new word via DMA - // whenever the shift register is empty. It takes 2 PIO cycles to send one - // bit, so the PIO engine should be configure to run at 4 MHz. - // - // In addition to the shift function, the PIO asserts the chip select signal - // high for 12 cycles (3us @ 4MHz) before the first word starts shifting - // - // At the end of the DMA transfer, CS & clock should be dropped manually. - ".side_set 2 opt", - - ".define CS_SETUP_DELAY 12 ", // setup time for CS - "setup_line:", - " set y, CS_SETUP_DELAY side 0", - "setup_cs:", - " jmp y-- setup_cs side 1", // respect setup time - ".wrap_target", - " out pins, 1 side 1", // assert data/clock low - " nop side 3", // clock high - ".wrap", - // after data transfer is done, clock and then CS should be dropped manually. - ); -}