diff --git a/examples/mps3-an536/Cargo.toml b/examples/mps3-an536/Cargo.toml index 1f31588..b3cfe3b 100644 --- a/examples/mps3-an536/Cargo.toml +++ b/examples/mps3-an536/Cargo.toml @@ -19,8 +19,9 @@ version = "0.0.0" cortex-ar = { path = "../../cortex-ar", features = ["critical-section-multi-core"] } cortex-r-rt = { path = "../../cortex-r-rt" } semihosting = { version = "0.1.18", features = ["stdio"] } -arm-gic = { git = "https://github.com/google/arm-gic.git", rev = "46a8fc1720f5c28fccf4dfb5953b88dab7012e9c", optional = true } +arm-gic = { version = "0.6.1", optional = true } critical-section = "1.2.0" +heapless = "0.9.1" [build-dependencies] arm-targets = {version = "0.2.0", path = "../../arm-targets"} @@ -30,5 +31,13 @@ eabi-fpu = ["cortex-r-rt/eabi-fpu"] gic = ["arm-gic"] [[bin]] -name = "gic" +name = "gic-map" +required-features = ["gic"] + +[[bin]] +name = "gic-static-section-irq" +required-features = ["gic"] + +[[bin]] +name = "gic-unified-irq" required-features = ["gic"] diff --git a/examples/mps3-an536/memory.x b/examples/mps3-an536/memory.x index 6ac99e2..44d2d9a 100644 --- a/examples/mps3-an536/memory.x +++ b/examples/mps3-an536/memory.x @@ -12,3 +12,22 @@ MEMORY { REGION_ALIAS("VECTORS", QSPI); REGION_ALIAS("CODE", QSPI); REGION_ALIAS("DATA", DDR); + +SECTIONS { + /* ### Interrupt Handler Entries + * + * The IRQ handler walks this section to find registered + * interrupt handlers + */ + .irq_entries : ALIGN(4) + { + /* We put this in the header */ + __irq_entries_start = .; + /* Here are the entries */ + KEEP(*(.irq_entries)); + /* Keep this block a nice round size */ + . = ALIGN(4); + /* We put this in the header */ + __irq_entries_end = .; + } > CODE +} INSERT AFTER .text; diff --git a/examples/mps3-an536/reference/abt-exception-armv8r-none-eabihf.out b/examples/mps3-an536/reference/abt-exception-armv8r-none-eabihf.out deleted file mode 100644 index b12baf8..0000000 --- a/examples/mps3-an536/reference/abt-exception-armv8r-none-eabihf.out +++ /dev/null @@ -1,9 +0,0 @@ -Hello, this is an data abort exception example -data abort occurred -DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0010 Status=0b00001 } -DFSR Status: Ok(AlignmentFault) -DFAR (Faulting Address Register): Dfar(4097) -data abort occurred -DFSR (Fault Status Register): DFSR { ext=false wnr=false Domain=0b0010 Status=0b00001 } -DFSR Status: Ok(AlignmentFault) -DFAR (Faulting Address Register): Dfar(4097) diff --git a/examples/mps3-an536/reference/gic-map-armv8r-none-eabihf.out b/examples/mps3-an536/reference/gic-map-armv8r-none-eabihf.out new file mode 100644 index 0000000..db56b21 --- /dev/null +++ b/examples/mps3-an536/reference/gic-map-armv8r-none-eabihf.out @@ -0,0 +1,20 @@ +Found PERIPHBASE 0xf0000000 +Creating GIC driver @ 0xf0000000 / 0xf0100000 +Calling git.setup(0) +Configure low-prio SGI... +Configure high-prio SGI... +gic.enable_interrupt() +Enabling interrupts... +CPSR: CPSR { N=0 Z=1 C=1 V=0 Q=0 J=0 E=0 A=0 I=1 F=1 T=0 MODE=Ok(Sys) } +CPSR: CPSR { N=0 Z=1 C=1 V=0 Q=0 J=0 E=0 A=0 I=0 F=1 T=0 MODE=Ok(Sys) } +Send lo-prio SGI +> IRQ +- handle_interrupt_with_id(SGI 3) +- got SGI 3, sending hi-prio SGI 4 +> IRQ +- handle_interrupt_with_id(SGI 4) +- got hi-prio SGI 4! +< IRQ +- finished sending hi-prio! +< IRQ +IRQ test completed OK diff --git a/examples/mps3-an536/reference/gic-static-section-irq-armv8r-none-eabihf.out b/examples/mps3-an536/reference/gic-static-section-irq-armv8r-none-eabihf.out new file mode 100644 index 0000000..db56b21 --- /dev/null +++ b/examples/mps3-an536/reference/gic-static-section-irq-armv8r-none-eabihf.out @@ -0,0 +1,20 @@ +Found PERIPHBASE 0xf0000000 +Creating GIC driver @ 0xf0000000 / 0xf0100000 +Calling git.setup(0) +Configure low-prio SGI... +Configure high-prio SGI... +gic.enable_interrupt() +Enabling interrupts... +CPSR: CPSR { N=0 Z=1 C=1 V=0 Q=0 J=0 E=0 A=0 I=1 F=1 T=0 MODE=Ok(Sys) } +CPSR: CPSR { N=0 Z=1 C=1 V=0 Q=0 J=0 E=0 A=0 I=0 F=1 T=0 MODE=Ok(Sys) } +Send lo-prio SGI +> IRQ +- handle_interrupt_with_id(SGI 3) +- got SGI 3, sending hi-prio SGI 4 +> IRQ +- handle_interrupt_with_id(SGI 4) +- got hi-prio SGI 4! +< IRQ +- finished sending hi-prio! +< IRQ +IRQ test completed OK diff --git a/examples/mps3-an536/reference/gic-armv8r-none-eabihf.out b/examples/mps3-an536/reference/gic-unified-irq-armv8r-none-eabihf.out similarity index 100% rename from examples/mps3-an536/reference/gic-armv8r-none-eabihf.out rename to examples/mps3-an536/reference/gic-unified-irq-armv8r-none-eabihf.out diff --git a/examples/mps3-an536/src/bin/gic-map.rs b/examples/mps3-an536/src/bin/gic-map.rs new file mode 100644 index 0000000..de4361f --- /dev/null +++ b/examples/mps3-an536/src/bin/gic-map.rs @@ -0,0 +1,166 @@ +//! # GIC example for Arm Cortex-R52 on an MPS2-AN336 +//! +//! Uses a run-time map of interrupt handlers. + +#![no_std] +#![no_main] + +// pull in our start-up code +use cortex_r_rt::{entry, irq}; + +// pull in our library +use mps3_an536::InterruptHandler; + +use arm_gic::{ + gicv3::{GicV3, Group, InterruptGroup, SgiTarget, SgiTargetGroup}, + IntId, +}; +use core::cell::RefCell; +use heapless::linear_map::LinearMap; +use semihosting::println; + +/// Offset from PERIPHBASE for GIC Distributor +const GICD_BASE_OFFSET: usize = 0x0000_0000usize; + +/// Offset from PERIPHBASE for the first GIC Redistributor +const GICR_BASE_OFFSET: usize = 0x0010_0000usize; + +const SGI_INTID_LO: IntId = IntId::sgi(3); +const SGI_INTID_HI: IntId = IntId::sgi(4); + +static INTERRUPT_HANDLERS: critical_section::Mutex>> = + critical_section::Mutex::new(RefCell::new(LinearMap::new())); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up code in `cortex-r-rt`. +#[entry] +fn main() -> ! { + // Get the GIC address by reading CBAR + let periphbase = cortex_ar::register::ImpCbar::read().periphbase(); + println!("Found PERIPHBASE {:010p}", periphbase); + let gicd_base = periphbase.wrapping_byte_add(GICD_BASE_OFFSET); + let gicr_base = periphbase.wrapping_byte_add(GICR_BASE_OFFSET); + + // Initialise the GIC. + println!( + "Creating GIC driver @ {:010p} / {:010p}", + gicd_base, gicr_base + ); + let mut gic: GicV3 = unsafe { GicV3::new(gicd_base.cast(), gicr_base.cast(), 1, false) }; + println!("Calling git.setup(0)"); + gic.setup(0); + GicV3::set_priority_mask(0x80); + + // Configure a Software Generated Interrupt for Core 0 + println!("Configure low-prio SGI..."); + gic.set_interrupt_priority(SGI_INTID_LO, Some(0), 0x31); + gic.set_group(SGI_INTID_LO, Some(0), Group::Group1NS); + + println!("Configure high-prio SGI..."); + gic.set_interrupt_priority(SGI_INTID_HI, Some(0), 0x10); + gic.set_group(SGI_INTID_HI, Some(0), Group::Group1NS); + + println!("gic.enable_interrupt()"); + gic.enable_interrupt(SGI_INTID_LO, Some(0), true); + gic.enable_interrupt(SGI_INTID_HI, Some(0), true); + + critical_section::with(|cs| { + let mut handlers = INTERRUPT_HANDLERS.borrow_ref_mut(cs); + handlers + .insert( + SGI_INTID_LO, + InterruptHandler::new(SGI_INTID_LO, handle_sgi_lo), + ) + .unwrap(); + handlers + .insert( + SGI_INTID_HI, + InterruptHandler::new(SGI_INTID_HI, handle_sgi_hi), + ) + .unwrap(); + }); + + println!("Enabling interrupts..."); + dump_cpsr(); + unsafe { + cortex_ar::interrupt::enable(); + } + dump_cpsr(); + + // Send it + println!("Send lo-prio SGI"); + GicV3::send_sgi( + SGI_INTID_LO, + SgiTarget::List { + affinity3: 0, + affinity2: 0, + affinity1: 0, + target_list: 0b1, + }, + SgiTargetGroup::CurrentGroup1, + ); + + for _ in 0..1_000_000 { + cortex_ar::asm::nop(); + } + + println!("IRQ test completed OK"); + + semihosting::process::exit(0); +} + +fn dump_cpsr() { + let cpsr = cortex_ar::register::Cpsr::read(); + println!("CPSR: {:?}", cpsr); +} + +/// Handles the low-prio SGI +fn handle_sgi_lo(int_id: IntId) { + println!("- got {:?}, sending hi-prio {:?}", int_id, SGI_INTID_HI); + GicV3::send_sgi( + SGI_INTID_HI, + SgiTarget::List { + affinity3: 0, + affinity2: 0, + affinity1: 0, + target_list: 0b1, + }, + SgiTargetGroup::CurrentGroup1, + ); + println!("- finished sending hi-prio!"); +} + +/// Handles the high-prio SGI +fn handle_sgi_hi(int_id: IntId) { + println!("- got hi-prio {:?}!", int_id); +} + +/// Called when the Arm CPU gets an IRQ +/// +/// Talks to the GICv3 to find out which interrupts are pending and calls +/// [`handle_interrupt_with_id`] for each of them, with interrupts re-enabled. +#[cfg(feature = "gic")] +#[irq] +fn irq_handler() { + println!("> IRQ"); + while let Some(next_int_id) = GicV3::get_and_acknowledge_interrupt(InterruptGroup::Group1) { + // handle the interrupt + println!("- handle_interrupt_with_id({:?})", next_int_id); + let handler = critical_section::with(|cs| { + let handlers_map = INTERRUPT_HANDLERS.borrow_ref(cs); + handlers_map.get(&next_int_id).cloned() + }); + if let Some(irq_entry) = handler { + // let's go re-entrant + unsafe { + cortex_ar::interrupt::enable(); + } + irq_entry.execute(); + // turn interrupts off again + cortex_ar::interrupt::disable(); + } + GicV3::end_interrupt(next_int_id, InterruptGroup::Group1); + } + println!("< IRQ"); +} diff --git a/examples/mps3-an536/src/bin/gic-static-section-irq.rs b/examples/mps3-an536/src/bin/gic-static-section-irq.rs new file mode 100644 index 0000000..51fe6b1 --- /dev/null +++ b/examples/mps3-an536/src/bin/gic-static-section-irq.rs @@ -0,0 +1,161 @@ +//! # GIC example for Arm Cortex-R52 on an MPS2-AN336 +//! +//! Uses a linker section to store InterruptHandler objects. + +#![no_std] +#![no_main] + +// pull in our start-up code +use cortex_r_rt::{entry, irq}; + +// pull in our library +use mps3_an536::InterruptHandler; + +use arm_gic::{ + gicv3::{GicV3, Group, InterruptGroup, SgiTarget, SgiTargetGroup}, + IntId, +}; +use semihosting::println; + +/// Offset from PERIPHBASE for GIC Distributor +const GICD_BASE_OFFSET: usize = 0x0000_0000usize; + +/// Offset from PERIPHBASE for the first GIC Redistributor +const GICR_BASE_OFFSET: usize = 0x0010_0000usize; + +const SGI_INTID_LO: IntId = IntId::sgi(3); +const SGI_INTID_HI: IntId = IntId::sgi(4); + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up code in `cortex-r-rt`. +#[entry] +fn main() -> ! { + // Get the GIC address by reading CBAR + let periphbase = cortex_ar::register::ImpCbar::read().periphbase(); + println!("Found PERIPHBASE {:010p}", periphbase); + let gicd_base = periphbase.wrapping_byte_add(GICD_BASE_OFFSET); + let gicr_base = periphbase.wrapping_byte_add(GICR_BASE_OFFSET); + + // Initialise the GIC. + println!( + "Creating GIC driver @ {:010p} / {:010p}", + gicd_base, gicr_base + ); + let mut gic: GicV3 = unsafe { GicV3::new(gicd_base.cast(), gicr_base.cast(), 1, false) }; + println!("Calling git.setup(0)"); + gic.setup(0); + GicV3::set_priority_mask(0x80); + + // Configure a Software Generated Interrupt for Core 0 + println!("Configure low-prio SGI..."); + gic.set_interrupt_priority(SGI_INTID_LO, Some(0), 0x31); + gic.set_group(SGI_INTID_LO, Some(0), Group::Group1NS); + + println!("Configure high-prio SGI..."); + gic.set_interrupt_priority(SGI_INTID_HI, Some(0), 0x10); + gic.set_group(SGI_INTID_HI, Some(0), Group::Group1NS); + + println!("gic.enable_interrupt()"); + gic.enable_interrupt(SGI_INTID_LO, Some(0), true); + gic.enable_interrupt(SGI_INTID_HI, Some(0), true); + + println!("Enabling interrupts..."); + dump_cpsr(); + unsafe { + cortex_ar::interrupt::enable(); + } + dump_cpsr(); + + // Send it + println!("Send lo-prio SGI"); + GicV3::send_sgi( + SGI_INTID_LO, + SgiTarget::List { + affinity3: 0, + affinity2: 0, + affinity1: 0, + target_list: 0b1, + }, + SgiTargetGroup::CurrentGroup1, + ); + + for _ in 0..1_000_000 { + cortex_ar::asm::nop(); + } + + println!("IRQ test completed OK"); + + semihosting::process::exit(0); +} + +fn dump_cpsr() { + let cpsr = cortex_ar::register::Cpsr::read(); + println!("CPSR: {:?}", cpsr); +} + +#[unsafe(link_section = ".irq_entries")] +#[used] +pub static HANDLE_SGI_LO: InterruptHandler = InterruptHandler::new(SGI_INTID_LO, handle_sgi_lo); + +/// Handles the low-prio SGI +fn handle_sgi_lo(int_id: IntId) { + println!("- got {:?}, sending hi-prio {:?}", int_id, SGI_INTID_HI); + GicV3::send_sgi( + SGI_INTID_HI, + SgiTarget::List { + affinity3: 0, + affinity2: 0, + affinity1: 0, + target_list: 0b1, + }, + SgiTargetGroup::CurrentGroup1, + ); + println!("- finished sending hi-prio!"); +} + +#[unsafe(link_section = ".irq_entries")] +#[used] +pub static HANDLE_SGI_HI: InterruptHandler = InterruptHandler::new(SGI_INTID_HI, handle_sgi_hi); + +/// Handles the high-prio SGI +fn handle_sgi_hi(int_id: IntId) { + println!("- got hi-prio {:?}!", int_id); +} + +/// Called when the Arm CPU gets an IRQ +/// +/// Talks to the GICv3 to find out which interrupts are pending and calls +/// [`handle_interrupt_with_id`] for each of them, with interrupts re-enabled. +#[cfg(feature = "gic")] +#[irq] +fn irq_handler() { + println!("> IRQ"); + while let Some(next_int_id) = GicV3::get_and_acknowledge_interrupt(InterruptGroup::Group1) { + // let's go re-entrant + unsafe { + cortex_ar::interrupt::enable(); + } + // handle the interrupt + println!("- handle_interrupt_with_id({:?})", next_int_id); + extern "Rust" { + static __irq_entries_start: InterruptHandler; + static __irq_entries_end: InterruptHandler; + } + let irq_entries_start: *const InterruptHandler = core::ptr::addr_of!(__irq_entries_start); + let irq_entries_end: *const InterruptHandler = core::ptr::addr_of!(__irq_entries_end); + let mut p = irq_entries_start; + while p != irq_entries_end { + let irq_entry = unsafe { p.read() }; + if irq_entry.matches(next_int_id) { + irq_entry.execute(); + break; + } + p = unsafe { p.offset(1) }; + } + // turn interrupts off again + cortex_ar::interrupt::disable(); + GicV3::end_interrupt(next_int_id, InterruptGroup::Group1); + } + println!("< IRQ"); +} diff --git a/examples/mps3-an536/src/bin/gic.rs b/examples/mps3-an536/src/bin/gic-unified-irq.rs similarity index 85% rename from examples/mps3-an536/src/bin/gic.rs rename to examples/mps3-an536/src/bin/gic-unified-irq.rs index 05c834e..8f46637 100644 --- a/examples/mps3-an536/src/bin/gic.rs +++ b/examples/mps3-an536/src/bin/gic-unified-irq.rs @@ -1,4 +1,6 @@ //! GIC example for Arm Cortex-R52 on an MPS2-AN336 +//! +//! As a single, unified, `#[irq]` handler. #![no_std] #![no_main] @@ -10,13 +12,11 @@ use cortex_r_rt::{entry, irq}; use mps3_an536 as _; use arm_gic::{ - gicv3::{Group, SgiTarget}, + gicv3::{GicV3, Group, InterruptGroup, SgiTarget, SgiTargetGroup}, IntId, }; use semihosting::println; -type SingleCoreGic = arm_gic::gicv3::GicV3<1>; - /// Offset from PERIPHBASE for GIC Distributor const GICD_BASE_OFFSET: usize = 0x0000_0000usize; @@ -42,11 +42,10 @@ fn main() -> ! { "Creating GIC driver @ {:010p} / {:010p}", gicd_base, gicr_base ); - let mut gic: SingleCoreGic = - unsafe { SingleCoreGic::new(gicd_base.cast(), [gicr_base.cast()]) }; + let mut gic: GicV3 = unsafe { GicV3::new(gicd_base.cast(), gicr_base.cast(), 1, false) }; println!("Calling git.setup(0)"); gic.setup(0); - SingleCoreGic::set_priority_mask(0x80); + GicV3::set_priority_mask(0x80); // Configure a Software Generated Interrupt for Core 0 println!("Configure low-prio SGI..."); @@ -70,7 +69,7 @@ fn main() -> ! { // Send it println!("Send lo-prio SGI"); - SingleCoreGic::send_sgi( + GicV3::send_sgi( SGI_INTID_LO, SgiTarget::List { affinity3: 0, @@ -78,6 +77,7 @@ fn main() -> ! { affinity1: 0, target_list: 0b1, }, + SgiTargetGroup::CurrentGroup1, ); for _ in 0..1_000_000 { @@ -97,7 +97,7 @@ fn dump_cpsr() { #[irq] fn irq_handler() { println!("> IRQ"); - while let Some(int_id) = SingleCoreGic::get_and_acknowledge_interrupt() { + while let Some(int_id) = GicV3::get_and_acknowledge_interrupt(InterruptGroup::Group1) { // let's go re-entrant unsafe { cortex_ar::interrupt::enable(); @@ -108,7 +108,7 @@ fn irq_handler() { "- IRQ got {:?}, sending hi-prio {:?}", SGI_INTID_LO, SGI_INTID_HI ); - SingleCoreGic::send_sgi( + GicV3::send_sgi( SGI_INTID_HI, SgiTarget::List { affinity3: 0, @@ -116,12 +116,13 @@ fn irq_handler() { affinity1: 0, target_list: 0b1, }, + SgiTargetGroup::CurrentGroup1, ); println!("- IRQ finished sending hi-prio!"); } // turn interrupts off again cortex_ar::interrupt::disable(); - SingleCoreGic::end_interrupt(int_id); + GicV3::end_interrupt(int_id, InterruptGroup::Group1); } println!("< IRQ"); } diff --git a/examples/mps3-an536/src/lib.rs b/examples/mps3-an536/src/lib.rs index 289b1c7..75484f4 100644 --- a/examples/mps3-an536/src/lib.rs +++ b/examples/mps3-an536/src/lib.rs @@ -1,4 +1,55 @@ //! Common code for all examples +//! +//! ## Interrupt Map +//! +//! | Interrupt ID | Description | +//! |--------------|------------------------------| +//! | EXTPPI0[0] | UART 0 Receive Interrupt | +//! | EXTPPI0[1] | UART 0 Transmit Interrupt | +//! | EXTPPI0[2] | UART 0 Combined Interrupt | +//! | EXTPPI0[3] | UART 0 Overflow | +//! | EXTPPI1[0] | UART 1 Receive Interrupt | +//! | EXTPPI1[1] | UART 1 Transmit Interrupt | +//! | EXTPPI1[2] | UART 1 Combined Interrupt | +//! | EXTPPI1[3] | UART 1 Overflow | +//! | SP[0] | WDG | +//! | SP[1] | DualTimer 1 | +//! | SP[2] | DualTimer 2 | +//! | SP[3] | DualTimer Combined | +//! | SP[4] | RTC | +//! | SP[5] | UART 2 Receive Interrupt | +//! | SP[6] | UART 2 Transmit Interrupt | +//! | SP[7] | UART 3 Receive Interrupt | +//! | SP[8] | UART 3 Transmit Interrupt | +//! | SP[9] | UART 4 Receive Interrupt | +//! | SP[10] | UART 4 Transmit Interrupt | +//! | SP[11] | UART 5 Receive Interrupt | +//! | SP[12] | UART 5 Transmit Interrupt | +//! | SP[13] | UART 2 Combined Interrupt | +//! | SP[14] | UART 3 Combined Interrupt | +//! | SP[15] | UART 4 Combined Interrupt | +//! | SP[16] | UART 5 Combined Interrupt | +//! | SP[17] | UART Overflow (2, 3, 4 & 5) | +//! | SP[18] | Ethernet | +//! | SP[19] | USB | +//! | SP[20] | FPGA Audio I2S | +//! | SP[21] | Touch Screen | +//! | SP[22] | SPI ADC | +//! | SP[23] | SPI Shield 0 | +//! | SP[24] | SPI Shield 1 | +//! | SP[25] | HDCLCD Interrupt | +//! | SP[26] | GPIO 0 Combined Interrupt | +//! | SP[27] | GPIO 1 Combined Interrupt | +//! | SP[28] | GPIO 2 Combined Interrupt | +//! | SP[29] | GPIO 3 Combined Interrupt | +//! | SP[30..=45] | GPIO 0.x Interrupt | +//! | SP[46..=61] | GPIO 1.x Interrupt | +//! | SP[62..=77] | GPIO 2.x Interrupt | +//! | SP[78..=93] | GPIO 3.x Interrupt | +//! +//! * Interrupt ID `SP[x]` are shared across cores +//! * Interrupt ID `EXTPPI0[x]` is only available on Core 0 +//! * Interrupt ID `EXTPPI1[x]` is only available on Core 1 #![no_std] @@ -15,3 +66,34 @@ fn panic(info: &core::panic::PanicInfo) -> ! { semihosting::println!("PANIC: {:#?}", info); semihosting::process::abort(); } + +#[cfg(feature = "gic")] +#[derive(Clone, Debug)] +/// Represents a handler for an interrupt +pub struct InterruptHandler { + int_id: arm_gic::IntId, + function: fn(arm_gic::IntId), +} + +#[cfg(feature = "gic")] +impl InterruptHandler { + /// Create a new `InterruptHandler`, associating an `IntId` with a function to call + pub const fn new(int_id: arm_gic::IntId, function: fn(arm_gic::IntId)) -> InterruptHandler { + InterruptHandler { int_id, function } + } + + /// Get the [`arm_gic::IntId`] this handler is for + pub const fn int_id(&self) -> arm_gic::IntId { + self.int_id + } + + /// Is this handler for this [`arm_gic::IntId`]? + pub fn matches(&self, int_id: arm_gic::IntId) -> bool { + self.int_id == int_id + } + + /// Execute the handler + pub fn execute(&self) { + (self.function)(self.int_id); + } +}