blob: 187ef5cc977f6b9baf110d3d5f759b32582bace9 [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! End-to-end check of external interrupt delivery on VeeR.
//!
//! The emulated core vectors machine-external interrupts through the MEIVT
//! redirect table (`handle_external_int` in the Caliptra CPU model reads
//! `*(MEIVT + 4 * claimid)` and jumps there), so an interrupt that reaches
//! `flash_error_handler` proves the table was programmed with a usable trap
//! vector.
//!
//! The primary flash controller is used as the interrupt source because it is
//! always instantiated by the emulator and needs no host-side driving. The PIC
//! has no software trigger (`VeerPic::trigger_interrupt` panics), so the shared
//! `pw_kernel/tests/interrupts` body cannot be reused here.
#![no_std]
#![no_main]
use core::ptr::with_exposed_provenance_mut;
use core::sync::atomic::{AtomicBool, Ordering};
use arch_riscv::Arch;
use entry::exit;
use target_common::{declare_target, TargetInterface};
use {codegen as _, console_backend as _};
// Primary flash controller, emulator memory map (PRIMARY_FLASH_CTRL_ADDR).
const FLASH_CTRL_BASE: usize = 0x2000_8000;
const INT_STATE: usize = FLASH_CTRL_BASE;
const INT_ENABLE: usize = FLASH_CTRL_BASE + 0x04;
const CONTROL: usize = FLASH_CTRL_BASE + 0x14;
const INT_ERROR: u32 = 1 << 0;
// FlControl: Start at bit 0, Op at bits 2:1. Op 3 is reserved, so the
// controller fails the operation with InvalidOp and raises the error interrupt.
const CONTROL_START_INVALID_OP: u32 = 0b111;
// Must match the IRQ number in system.json5.
const TEST_IRQ: u32 = 19;
// rv32imc has no A extension, so plain load/store rather than a counter.
static IRQ_FIRED: AtomicBool = AtomicBool::new(false);
pub fn flash_error_handler<K: kernel::Kernel>(_kernel: K) {
// Write-one-to-clear the error status; this also drops the controller's
// interrupt line, so the handler is not re-entered.
unsafe { with_exposed_provenance_mut::<u32>(INT_STATE).write_volatile(INT_ERROR) };
IRQ_FIRED.store(true, Ordering::SeqCst);
}
pub struct Target {}
impl TargetInterface for Target {
const NAME: &'static str = "Caliptra-MCU Kernel Interrupts";
fn main() -> ! {
use kernel::interrupt_controller::InterruptController;
<Arch as kernel::Arch>::InterruptController::enable_interrupt(TEST_IRQ);
unsafe {
with_exposed_provenance_mut::<u32>(INT_ENABLE).write_volatile(INT_ERROR);
with_exposed_provenance_mut::<u32>(CONTROL).write_volatile(CONTROL_START_INVALID_OP);
}
let mut timeout = 1_000_000;
while !IRQ_FIRED.load(Ordering::SeqCst) && timeout > 0 {
timeout -= 1;
core::hint::spin_loop();
}
if !IRQ_FIRED.load(Ordering::SeqCst) {
pw_log::info!("FAIL: no external interrupt delivered");
exit(1);
}
pw_log::info!("PASS");
exit(0);
}
}
codegen::declare_kernel_interrupt_handlers!();
declare_target!(Target);