Interrupt Controller & System Events
Modeling an Interrupt Controller (GIC/NVIC) and bridging hardware IRQs into TLM software interrupts.
Interrupt Controllers & System Events
In a real System-on-Chip (SoC), peripherals do not expect the CPU to constantly poll them. When a Timer expires or a UART receives data, it asserts a hardware interrupt line (IRQ).
The Interrupt Controller (like the ARM GIC or Cortex-M NVIC) receives dozens of these raw hardware lines, prioritizes them, and signals the CPU. In a Virtual Platform, we must model this exact behavior.
The Architecture
- Peripheral: Asserts a standard
sc_signal<bool>representing the IRQ line. - Interrupt Controller (INTC): Contains a TLM socket (for the CPU to read status/acknowledge) and standard
sc_in<bool>ports for the incoming IRQ lines. It evaluates priority and asserts a singlesc_out<bool>to the CPU. - CPU ISS: A thread monitoring the
sc_in<bool>from the INTC, triggering an asynchronous exception routine in the simulated software.
Complete Interrupt Controller Example
This complete sc_main demonstrates a peripheral generating an interrupt, the controller routing it, and the CPU responding.
#include <systemc>
#include <tlm>
#include <tlm_utils/simple_target_socket.h>
// 1. Mock Peripheral (Timer that fires an IRQ)
SC_MODULE(Timer_IRQ) {
sc_core::sc_out<bool> irq_out{"irq_out"};
SC_CTOR(Timer_IRQ) {
SC_THREAD(run);
}
void run() {
wait(20, sc_core::SC_NS);
std::cout << "@" << sc_core::sc_time_stamp() << " [Timer] Firing IRQ." << std::endl;
irq_out.write(true); // Assert Interrupt
}
};
// 2. The Interrupt Controller
SC_MODULE(InterruptController) {
tlm_utils::simple_target_socket<InterruptController> socket;
sc_core::sc_in<bool> irq_in{"irq_in"};
sc_core::sc_out<bool> cpu_irq{"cpu_irq"};
bool irq_pending = false;
SC_CTOR(InterruptController) : socket("socket") {
socket.register_b_transport(this, &InterruptController::b_transport);
SC_METHOD(eval_interrupts);
sensitive << irq_in;
}
private:
void eval_interrupts() {
if (irq_in.read() == true) {
std::cout << "@" << sc_core::sc_time_stamp() << " [INTC] IRQ Received. Forwarding to CPU." << std::endl;
irq_pending = true;
cpu_irq.write(true);
}
}
void b_transport(tlm::tlm_generic_payload& trans, sc_core::sc_time& delay) {
// Mocking the CPU acknowledging and clearing the interrupt
if (trans.get_command() == tlm::TLM_WRITE_COMMAND && trans.get_address() == 0x10) { // 0x10 = Clear Reg
std::cout << "@" << sc_core::sc_time_stamp() << " [INTC] CPU Cleared IRQ via TLM." << std::endl;
irq_pending = false;
cpu_irq.write(false);
}
trans.set_response_status(tlm::TLM_OK_RESPONSE);
}
};
// 3. Mock CPU
SC_MODULE(MockCPU_IRQ) {
tlm_utils::simple_initiator_socket<MockCPU_IRQ> socket;
sc_core::sc_in<bool> irq_in{"irq_in"};
SC_CTOR(MockCPU_IRQ) : socket("socket") {
SC_THREAD(cpu_loop);
sensitive << irq_in.pos(); // Wake up asynchronously on interrupt
}
void cpu_loop() {
while(true) {
wait(); // Wait for IRQ
std::cout << "@" << sc_core::sc_time_stamp() << " [CPU] INTERRUPT DETECTED! Jumping to ISR." << std::endl;
// Send TLM transaction to INTC to clear the interrupt
tlm::tlm_generic_payload trans;
sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
uint32_t val = 1;
trans.set_command(tlm::TLM_WRITE_COMMAND);
trans.set_address(0x10);
trans.set_data_ptr(reinterpret_cast<unsigned char*>(&val));
trans.set_data_length(4);
trans.set_response_status(tlm::TLM_INCOMPLETE_RESPONSE);
socket->b_transport(trans, delay);
wait(delay); // Advance time for bus latency
}
}
};
int sc_main(int argc, char* argv[]) {
// Hardware wires
sc_core::sc_signal<bool> timer_irq_wire;
sc_core::sc_signal<bool> cpu_irq_wire;
// Instantiate Modules
Timer_IRQ timer("timer");
InterruptController intc("intc");
MockCPU_IRQ cpu("cpu");
// Bind Wires
timer.irq_out(timer_irq_wire);
intc.irq_in(timer_irq_wire);
intc.cpu_irq(cpu_irq_wire);
cpu.irq_in(cpu_irq_wire);
// Bind TLM Socket
cpu.socket.bind(intc.socket);
sc_core::sc_start(100, sc_core::SC_NS);
return 0;
}This structural pattern correctly synchronizes continuous hardware events with the loosely timed TLM environment, ensuring interrupt latency and causality are accurately preserved.
Comments and Corrections