How to properly get the timer interrupt to work?


(Ben Mezger) #1

I’ve been trying to get the timer interrupt handler to work, with some partial success. My timer interrupt initialization (seems?) to be correctly initialized, and my interrupt handler does get called (sometimes, mostly once in every 4 execution), however, when on GDB, it seems to be called quite a lot, every once in an ns, but on real execution it does not. Why and what exactly am I doing it wrong?

For some background, I am trying to simulate a context switch mechanism, so ideally I am looking for setting the timer to raise an exception every second, but it seems to be working the opposite of what I am looking for.

typedef struct kctxt_s {
	struct metal_cpu *cpu;
	struct metal_interrupt *cpu_intr;
	struct metal_interrupt *sw_intr;
	int sw_intrid;
	int cpu_intrid;
	int cur_cpu;

	struct metal_interrupt *timer_intr;
	unsigned long long timebase;
	int timer_intrid;

	struct metal_pmp *pmp;
	struct metal_pmp_config *pmp_config;

	struct metal_register_file *regf;

	uctxt_t *user_context;
} kctxt_t;
void
_init_timer_intrp(kctxt_t *kctxt)
{
	kctxt->timebase = 0;

	unsigned long long timeval = metal_cpu_get_timer(kctxt->cpu);
	kctxt->timebase = metal_cpu_get_timebase(kctxt->cpu);

	if ((timeval == 0) || (kctxt->timebase == 0)) {
		log_fatal("[_init_timer_intrp] | Timeval %d timebase %d",
		    timeval, kctxt->timebase);
		return;
	}

	kctxt->timer_intr = metal_cpu_timer_interrupt_controller(kctxt->cpu);

	if (!kctxt->timer_intr) {
		log_warn(
		    "[_init_timer_intrp] | Timer interrupt controller is NULL");
		return;
	}

	metal_interrupt_init(kctxt->timer_intr);
	log_info(
	    "[_init_timer_intrp] | Address of Timer interrupt controller is %p",
	    kctxt->timer_intr);

	kctxt->timer_intrid = metal_cpu_timer_get_interrupt_id(kctxt->cpu);
	int rc = metal_interrupt_register_handler(kctxt->timer_intr,
	    kctxt->timer_intrid, kexcep_timer_handler, kctxt);

	if (rc < 0) {
		log_fatal(
		    "[_init_timer_intrp] | Failed. Timer %p interrupt handler %p registration failed",
		    kctxt->timer_intr, kexcep_timer_handler);
		return;
	}

	log_info(
	    "[_init_timer_intrp] | Timer interrupt EXCP registered for %p id %d",
	    kctxt->timer_intr, kctxt->timer_intrid);

	// metal_cpu_set_mtimecmp(kctxt->cpu, metal_cpu_get_timebase(kctxt->cpu)
	// + RTC_FREQ * 1.5);
	metal_cpu_set_mtimecmp(
	    kctxt->cpu, metal_cpu_get_mtime(kctxt->cpu) + 3 * RTC_FREQ);

	metal_interrupt_enable(kctxt->timer_intr, kctxt->timer_intrid);
	log_info("[_init_timer_intrp] | Timer interrupt %p enabled for cpu %p",
	    kctxt->timer_intr, kctxt->cpu);
}
void
kexcep_timer_handler(int id, void *data)
{
	kctxt_t *kctxt = (kctxt_t *)data;

	metal_interrupt_disable(kctxt->timer_intr, kctxt->timer_intrid);
	metal_interrupt_enable(kctxt->timer_intr, kctxt->timer_intrid);

	metal_cpu_set_mtimecmp(
	    kctxt->cpu, metal_cpu_get_mtime(kctxt->cpu) + RTC_FREQ / 2);

	log_warn(
	    "[kexcep_timer_handler] | Exception | Timer interrupt %p | id %d | mtime %ld | timebase %ld",
	    kctxt->timer_intr, id, metal_cpu_get_mtime(kctxt->cpu),
	    metal_cpu_get_timebase(kctxt->cpu));

	// return_from_excp(kctxt->cpu, id);
}