#![cfg_attr(not(target_arch = "riscv32"), allow(unused_unsafe, unused_variables, unused_mut))]
pub use crate::soc::ws63::Interrupt;
const SYS_VECTOR_CNT: u16 = 26;
const LOCAL_IRQ_VECTOR_CNT: u16 = 32;
const LOCIEN_IRQ_NUM: u16 = 32;
const LOCIPRI_IRQ_NUM: u16 = 8;
const LOCIPRI_IRQ_BITS: u16 = 4;
const LOCIPRI_FIELD_MASK: u32 = 0xF;
const PRIORITY_IRQ_END: u16 = SYS_VECTOR_CNT + 16 * LOCIPRI_IRQ_NUM;
const LOCIPRI_DEFAULT_VAL: u32 = 0x1111_1111;
#[cfg(target_arch = "riscv32")]
macro_rules! csr_set {
($csr:literal, $v:expr) => {
core::arch::asm!(concat!("csrs ", $csr, ", {0}"), in(reg) $v, options(nomem, nostack))
};
}
#[cfg(target_arch = "riscv32")]
macro_rules! csr_clear {
($csr:literal, $v:expr) => {
core::arch::asm!(concat!("csrc ", $csr, ", {0}"), in(reg) $v, options(nomem, nostack))
};
}
#[cfg(target_arch = "riscv32")]
macro_rules! csr_write {
($csr:literal, $v:expr) => {
core::arch::asm!(concat!("csrw ", $csr, ", {0}"), in(reg) $v, options(nomem, nostack))
};
}
#[cfg(target_arch = "riscv32")]
macro_rules! csr_read {
($csr:literal) => {{
let v: u32;
core::arch::asm!(concat!("csrr {0}, ", $csr), out(reg) v, options(nomem, nostack));
v
}};
}
#[cfg(not(target_arch = "riscv32"))]
macro_rules! csr_set {
($csr:literal, $v:expr) => {{
let _ = $v;
}};
}
#[cfg(not(target_arch = "riscv32"))]
macro_rules! csr_clear {
($csr:literal, $v:expr) => {{
let _ = $v;
}};
}
#[cfg(not(target_arch = "riscv32"))]
macro_rules! csr_write {
($csr:literal, $v:expr) => {{
let _ = $v;
}};
}
#[cfg(not(target_arch = "riscv32"))]
macro_rules! csr_read {
($csr:literal) => {{ 0u32 }};
}
#[inline]
unsafe fn locien_write(idx: u16, mask: u32, set: bool) {
unsafe {
match (idx, set) {
(0, true) => csr_set!("0xbe0", mask),
(0, false) => csr_clear!("0xbe0", mask),
(1, true) => csr_set!("0xbe1", mask),
(1, false) => csr_clear!("0xbe1", mask),
(2, true) => csr_set!("0xbe2", mask),
(2, false) => csr_clear!("0xbe2", mask),
_ => {}
}
}
}
#[inline]
fn locien_read(idx: u16) -> u32 {
unsafe {
match idx {
0 => csr_read!("0xbe0"),
1 => csr_read!("0xbe1"),
2 => csr_read!("0xbe2"),
_ => 0,
}
}
}
#[inline]
fn locipd_read(idx: u16) -> u32 {
unsafe {
match idx {
0 => csr_read!("0xbe8"),
1 => csr_read!("0xbe9"),
2 => csr_read!("0xbea"),
_ => 0,
}
}
}
#[inline]
fn locipri_set_field(idx: u16, shift: u16, value: u32) {
let clear = LOCIPRI_FIELD_MASK << shift;
let set = (value & LOCIPRI_FIELD_MASK) << shift;
unsafe {
macro_rules! rmw {
($csr:literal) => {{
csr_clear!($csr, clear);
csr_set!($csr, set);
}};
}
match idx {
0 => rmw!("0xbc0"),
1 => rmw!("0xbc1"),
2 => rmw!("0xbc2"),
3 => rmw!("0xbc3"),
4 => rmw!("0xbc4"),
5 => rmw!("0xbc5"),
6 => rmw!("0xbc6"),
7 => rmw!("0xbc7"),
8 => rmw!("0xbc8"),
9 => rmw!("0xbc9"),
10 => rmw!("0xbca"),
11 => rmw!("0xbcb"),
12 => rmw!("0xbcc"),
13 => rmw!("0xbcd"),
14 => rmw!("0xbce"),
15 => rmw!("0xbcf"),
_ => {}
}
}
}
#[inline]
fn locipri_read(idx: u16) -> u32 {
unsafe {
match idx {
0 => csr_read!("0xbc0"),
1 => csr_read!("0xbc1"),
2 => csr_read!("0xbc2"),
3 => csr_read!("0xbc3"),
4 => csr_read!("0xbc4"),
5 => csr_read!("0xbc5"),
6 => csr_read!("0xbc6"),
7 => csr_read!("0xbc7"),
8 => csr_read!("0xbc8"),
9 => csr_read!("0xbc9"),
10 => csr_read!("0xbca"),
11 => csr_read!("0xbcb"),
12 => csr_read!("0xbcc"),
13 => csr_read!("0xbcd"),
14 => csr_read!("0xbce"),
15 => csr_read!("0xbcf"),
_ => 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Priority(u8);
impl Priority {
pub const P1: Self = Priority(1);
pub const P2: Self = Priority(2);
pub const P3: Self = Priority(3);
pub const P4: Self = Priority(4);
pub const P5: Self = Priority(5);
pub const P6: Self = Priority(6);
pub const P7: Self = Priority(7);
pub const LOWEST: Self = Priority(1);
pub const HIGHEST: Self = Priority(7);
pub const fn new(level: u8) -> Self {
let l = if level < 1 {
1
} else if level > 7 {
7
} else {
level
};
Priority(l)
}
pub const fn level(self) -> u8 {
self.0
}
}
#[inline]
fn irq_num(irq: Interrupt) -> u16 {
irq as u16
}
pub unsafe fn enable(irq: Interrupt) {
let n = irq_num(irq);
if (SYS_VECTOR_CNT..LOCAL_IRQ_VECTOR_CNT).contains(&n) {
unsafe { csr_set!("mie", 1u32 << n) };
} else if n >= LOCAL_IRQ_VECTOR_CNT {
let bit = (n - LOCAL_IRQ_VECTOR_CNT) % LOCIEN_IRQ_NUM;
let reg = (n - LOCAL_IRQ_VECTOR_CNT) / LOCIEN_IRQ_NUM;
unsafe { locien_write(reg, 1u32 << bit, true) };
}
}
pub unsafe fn disable(irq: Interrupt) {
let n = irq_num(irq);
if (SYS_VECTOR_CNT..LOCAL_IRQ_VECTOR_CNT).contains(&n) {
unsafe { csr_clear!("mie", 1u32 << n) };
} else if n >= LOCAL_IRQ_VECTOR_CNT {
let bit = (n - LOCAL_IRQ_VECTOR_CNT) % LOCIEN_IRQ_NUM;
let reg = (n - LOCAL_IRQ_VECTOR_CNT) / LOCIEN_IRQ_NUM;
unsafe { locien_write(reg, 1u32 << bit, false) };
}
clear_pending(irq);
}
pub fn is_enabled(irq: Interrupt) -> bool {
let n = irq_num(irq);
if (SYS_VECTOR_CNT..LOCAL_IRQ_VECTOR_CNT).contains(&n) {
let mie: u32 = unsafe { csr_read!("mie") };
(mie & (1u32 << n)) != 0
} else if n >= LOCAL_IRQ_VECTOR_CNT {
let bit = (n - LOCAL_IRQ_VECTOR_CNT) % LOCIEN_IRQ_NUM;
let reg = (n - LOCAL_IRQ_VECTOR_CNT) / LOCIEN_IRQ_NUM;
(locien_read(reg) & (1u32 << bit)) != 0
} else {
false
}
}
pub fn set_priority(irq: Interrupt, priority: Priority) {
let n = irq_num(irq);
if (SYS_VECTOR_CNT..PRIORITY_IRQ_END).contains(&n) {
let order = (n - SYS_VECTOR_CNT) % LOCIPRI_IRQ_NUM;
let reg = (n - SYS_VECTOR_CNT) / LOCIPRI_IRQ_NUM;
locipri_set_field(reg, order * LOCIPRI_IRQ_BITS, priority.0 as u32);
}
}
pub fn priority(irq: Interrupt) -> Priority {
let n = irq_num(irq);
if (SYS_VECTOR_CNT..PRIORITY_IRQ_END).contains(&n) {
let order = (n - SYS_VECTOR_CNT) % LOCIPRI_IRQ_NUM;
let reg = (n - SYS_VECTOR_CNT) / LOCIPRI_IRQ_NUM;
let shift = order * LOCIPRI_IRQ_BITS;
let field = (locipri_read(reg) >> shift) & LOCIPRI_FIELD_MASK;
Priority::new(field as u8)
} else {
Priority::LOWEST
}
}
pub fn set_threshold(level: u8) {
unsafe { csr_write!("0xbfe", (level & 0x7) as u32) };
}
pub fn threshold() -> u8 {
(unsafe { csr_read!("0xbfe") } & 0x7) as u8
}
pub fn clear_pending(irq: Interrupt) {
let n = irq_num(irq) as u32;
unsafe {
#[cfg(target_arch = "riscv32")]
core::arch::asm!("fence", options(nostack));
csr_write!("0xbf0", n);
#[cfg(target_arch = "riscv32")]
core::arch::asm!("fence", options(nostack));
}
}
pub fn is_pending(irq: Interrupt) -> bool {
let n = irq_num(irq);
if (SYS_VECTOR_CNT..LOCAL_IRQ_VECTOR_CNT).contains(&n) {
let mip: u32 = unsafe { csr_read!("mip") };
(mip & (1u32 << n)) != 0
} else if n >= LOCAL_IRQ_VECTOR_CNT {
let bit = (n - LOCAL_IRQ_VECTOR_CNT) % LOCIEN_IRQ_NUM;
let reg = (n - LOCAL_IRQ_VECTOR_CNT) / LOCIEN_IRQ_NUM;
(locipd_read(reg) & (1u32 << bit)) != 0
} else {
false
}
}
pub fn init() {
unsafe {
csr_write!("0xbc0", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbc1", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbc2", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbc3", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbc4", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbc5", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbc6", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbc7", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbc8", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbc9", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbca", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbcb", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbcc", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbcd", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbce", LOCIPRI_DEFAULT_VAL);
csr_write!("0xbcf", LOCIPRI_DEFAULT_VAL);
}
}
pub unsafe fn enable_global() {
#[cfg(target_arch = "riscv32")]
unsafe {
core::arch::asm!("csrsi mstatus, 0x8", options(nomem, nostack))
};
}
pub fn disable_global() {
#[cfg(target_arch = "riscv32")]
unsafe {
core::arch::asm!("csrci mstatus, 0x8", options(nomem, nostack))
};
}
pub fn free<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
#[cfg(target_arch = "riscv32")]
let prev: usize;
#[cfg(target_arch = "riscv32")]
unsafe {
core::arch::asm!("csrrci {0}, mstatus, 0x8", out(reg) prev, options(nomem, nostack))
};
let r = f();
#[cfg(target_arch = "riscv32")]
if prev & 0x8 != 0 {
unsafe { core::arch::asm!("csrsi mstatus, 0x8", options(nomem, nostack)) };
}
r
}