#[cfg(target_arch = "riscv32")]
use core::ptr::{read_volatile, write_volatile};
const INTERRUPT_CORE0_BASE: usize = 0x500D_6000;
pub const INT_MAP_SYSTIMER_TARGET0: *mut u32 = (INTERRUPT_CORE0_BASE + 0xD4) as *mut u32;
pub const INT_MAP_EMAC_SBD: *mut u32 = (INTERRUPT_CORE0_BASE + 0x170) as *mut u32;
#[cfg(target_arch = "riscv32")]
const CLIC_BASE: usize = 0x2080_0000;
#[allow(dead_code)]
const CLIC_CTRL_BASE: usize = 0x2080_1000;
#[cfg(target_arch = "riscv32")]
const CLIC_INT_THRESH_REG: *mut u32 = (CLIC_BASE + 0x08) as *mut u32;
pub const CLIC_EXT_INTR_NUM_OFFSET: u8 = 16;
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Trigger {
LevelPositive = 0b00,
EdgePositive = 0b01,
LevelNegative = 0b10,
EdgeNegative = 0b11,
}
#[inline]
pub const fn clic_idx_for_cpu_line(cpu_int_n: u8) -> u8 {
cpu_int_n + CLIC_EXT_INTR_NUM_OFFSET
}
#[inline]
pub const fn clic_ctrl_offset(idx: u8) -> usize {
(idx as usize) * 4
}
#[inline]
pub const fn clic_attr_byte(trigger: Trigger) -> u8 {
(3u8 << 6) | ((trigger as u8) << 1)
}
#[inline]
pub const fn clic_ctl_byte(priority: u8) -> u8 {
(priority & 0x07) << 5
}
#[inline]
pub const fn int_map_value(clic_idx: u8) -> u32 {
(clic_idx & 0x3F) as u32
}
#[inline]
pub const fn clic_threshold_value(thresh: u8) -> u32 {
(thresh as u32) << 24
}
#[allow(dead_code)]
#[inline]
fn clic_word_ptr(idx: u8) -> *mut u32 {
(CLIC_CTRL_BASE + clic_ctrl_offset(idx)) as *mut u32
}
#[inline]
fn clic_byte_ip(idx: u8) -> *mut u8 {
(CLIC_CTRL_BASE + clic_ctrl_offset(idx)) as *mut u8
}
#[inline]
fn clic_byte_ie(idx: u8) -> *mut u8 {
(CLIC_CTRL_BASE + clic_ctrl_offset(idx) + 1) as *mut u8
}
#[inline]
fn clic_byte_attr(idx: u8) -> *mut u8 {
(CLIC_CTRL_BASE + clic_ctrl_offset(idx) + 2) as *mut u8
}
#[inline]
fn clic_byte_ctl(idx: u8) -> *mut u8 {
(CLIC_CTRL_BASE + clic_ctrl_offset(idx) + 3) as *mut u8
}
#[inline]
pub fn route_to_clic(_int_map_reg: *mut u32, _clic_idx: u8) {
#[cfg(target_arch = "riscv32")]
unsafe {
write_volatile(_int_map_reg, int_map_value(_clic_idx));
}
}
#[inline]
pub fn route_systimer_target0(clic_idx: u8) {
route_to_clic(INT_MAP_SYSTIMER_TARGET0, clic_idx);
}
#[inline]
pub fn route_emac_sbd(clic_idx: u8) {
route_to_clic(INT_MAP_EMAC_SBD, clic_idx);
}
pub fn enable_cpu_int(_cpu_int_n: u8, _priority: u8) {
#[cfg(target_arch = "riscv32")]
{
let idx = clic_idx_for_cpu_line(_cpu_int_n);
let prio_byte = clic_ctl_byte(_priority);
let attr_byte = clic_attr_byte(Trigger::LevelPositive);
unsafe {
write_volatile(clic_byte_ie(idx), 0);
write_volatile(clic_byte_ip(idx), 0);
write_volatile(clic_byte_attr(idx), attr_byte);
write_volatile(clic_byte_ctl(idx), prio_byte);
write_volatile(clic_byte_ie(idx), 1);
}
}
}
#[inline]
pub fn disable_cpu_int(_cpu_int_n: u8) {
#[cfg(target_arch = "riscv32")]
{
let idx = clic_idx_for_cpu_line(_cpu_int_n);
unsafe { write_volatile(clic_byte_ie(idx), 0) };
}
}
#[inline]
pub fn clear_pending(_cpu_int_n: u8) {
#[cfg(target_arch = "riscv32")]
{
let idx = clic_idx_for_cpu_line(_cpu_int_n);
unsafe { write_volatile(clic_byte_ip(idx), 0) };
}
}
#[inline]
pub fn read_ctrl_word(_cpu_int_n: u8) -> u32 {
#[cfg(target_arch = "riscv32")]
{
let idx = clic_idx_for_cpu_line(_cpu_int_n);
return unsafe { read_volatile(clic_word_ptr(idx)) };
}
#[cfg(not(target_arch = "riscv32"))]
0
}
#[inline]
pub fn set_threshold(_thresh: u8) {
#[cfg(target_arch = "riscv32")]
unsafe {
write_volatile(CLIC_INT_THRESH_REG, clic_threshold_value(_thresh));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clic_ext_intr_num_offset_is_sixteen() {
assert_eq!(CLIC_EXT_INTR_NUM_OFFSET, 16);
}
#[test]
fn int_map_systimer_target0_address_matches_idf() {
assert_eq!(INT_MAP_SYSTIMER_TARGET0 as usize, 0x500D_60D4);
}
#[test]
fn int_map_emac_sbd_address_matches_idf() {
assert_eq!(INT_MAP_EMAC_SBD as usize, 0x500D_6170);
}
#[test]
fn clic_idx_for_cpu_line_is_offset_by_sixteen() {
assert_eq!(clic_idx_for_cpu_line(0), 16);
assert_eq!(clic_idx_for_cpu_line(1), 17);
assert_eq!(clic_idx_for_cpu_line(2), 18);
assert_eq!(clic_idx_for_cpu_line(31), 47);
}
#[test]
fn clic_ctrl_offset_is_four_bytes_per_entry() {
assert_eq!(clic_ctrl_offset(0), 0);
assert_eq!(clic_ctrl_offset(1), 4);
assert_eq!(clic_ctrl_offset(17), 17 * 4);
assert_eq!(clic_ctrl_offset(255), 255 * 4);
}
#[test]
fn clic_byte_offsets_are_ip_ie_attr_ctl_in_order() {
let idx = 17;
let ip = clic_byte_ip(idx) as usize;
let ie = clic_byte_ie(idx) as usize;
let attr = clic_byte_attr(idx) as usize;
let ctl = clic_byte_ctl(idx) as usize;
let word = clic_word_ptr(idx) as usize;
assert_eq!(ip, word);
assert_eq!(ie, word + 1);
assert_eq!(attr, word + 2);
assert_eq!(ctl, word + 3);
}
#[test]
fn clic_word_address_for_systimer_entry_matches_layout() {
assert_eq!(clic_word_ptr(17) as usize, 0x2080_1044);
assert_eq!(clic_word_ptr(18) as usize, 0x2080_1048);
}
#[test]
fn clic_attr_byte_machine_mode_level_positive_is_0xc0() {
assert_eq!(clic_attr_byte(Trigger::LevelPositive), 0b1100_0000);
}
#[test]
fn clic_attr_byte_encodes_each_trigger_distinctly() {
let lp = clic_attr_byte(Trigger::LevelPositive);
let ep = clic_attr_byte(Trigger::EdgePositive);
let ln = clic_attr_byte(Trigger::LevelNegative);
let en = clic_attr_byte(Trigger::EdgeNegative);
assert_ne!(lp, ep);
assert_ne!(lp, ln);
assert_ne!(lp, en);
assert_ne!(ep, ln);
assert_ne!(ep, en);
assert_ne!(ln, en);
let mode_shv_mask = !0b0000_0110;
assert_eq!(lp & mode_shv_mask, ep & mode_shv_mask);
assert_eq!(lp & mode_shv_mask, ln & mode_shv_mask);
assert_eq!(lp & mode_shv_mask, en & mode_shv_mask);
}
#[test]
fn clic_ctl_byte_packs_priority_into_top_three_bits() {
assert_eq!(clic_ctl_byte(0), 0b0000_0000);
assert_eq!(clic_ctl_byte(1), 0b0010_0000);
assert_eq!(clic_ctl_byte(7), 0b1110_0000);
}
#[test]
fn clic_ctl_byte_clamps_priority_to_three_bits() {
assert_eq!(clic_ctl_byte(8), 0);
assert_eq!(clic_ctl_byte(0xFF), 0b1110_0000);
for p in 0..=255u8 {
assert_eq!(
clic_ctl_byte(p) & 0b0001_1111,
0,
"priority {} leaked into reserved bottom bits",
p
);
}
}
#[test]
fn int_map_value_writes_clic_index_in_low_six_bits() {
assert_eq!(int_map_value(17), 17);
assert_eq!(int_map_value(18), 18);
assert_eq!(int_map_value(0xC0 | 17), 17);
assert_eq!(int_map_value(0x3F), 0x3F);
}
#[test]
fn clic_threshold_value_packs_threshold_byte_into_high_byte() {
assert_eq!(clic_threshold_value(0x00), 0x0000_0000);
assert_eq!(clic_threshold_value(0xFF), 0xFF00_0000);
for t in 0..=255u8 {
let v = clic_threshold_value(t);
assert_eq!(v & 0x00FF_FFFF, 0, "threshold {:#x} leaked", t);
assert_eq!(v >> 24, t as u32);
}
}
}