Local Timer Interrupts (MTIP)

Hi Everyone,

I’m trying to handle local timer interrupts but I’ve got some problems.

I wrote an ISR which is quite similar to the example in ‘demo_gpio’. For debugging I toggle the blue LED.

In ‘init.c’ I changed two things:

  • Also activate the green LED at the beginning of ‘handle_trap()’ to check whether I jump into this function or not.

  • #define USE_M_TIME’

When I start this program on the HiFive 1 the red LED blinks for two seconds.
After this the program freezes.

So why there is no jump into ‘handle_trap()’ -> ‘handle_m_time_interrupt()’?
What I have to do?

Thank you!

main.c

#include <stdio.h>
#include <stdint.h>
#include "stdatomic.h"
#include <unistd.h>

#include "platform.h"
#include "encoding.h"
#include "plic/plic_driver.h"

// Entry Point for Machine Timer Interrupt Handler
void handle_m_time_interrupt()
{
	// Debugging
	GPIO_REG(GPIO_OUTPUT_VAL) ^= (1 << BLUE_LED_OFFSET);

	clear_csr(mie, MIP_MTIP);

	volatile uint64_t * mtime       = (uint64_t*) (CLINT_CTRL_ADDR + CLINT_MTIME);
	volatile uint64_t * mtimecmp    = (uint64_t*) (CLINT_CTRL_ADDR + CLINT_MTIMECMP);
	uint64_t now = *mtime;
	uint64_t then = now + 2 * RTC_FREQ;
	*mtimecmp = then;

	set_csr(mie, MIP_MTIP);
}

void waitFor(unsigned long time)
{
	for(volatile unsigned long i = 0; i < time; i++);
}

// Red LED
void ledRed(unsigned long time)
{
	GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << RED_LED_OFFSET);
	waitFor(time);
	GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << RED_LED_OFFSET);
	waitFor(time);
}

// Green LED
void ledGreen(unsigned long time)
{
	GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << GREEN_LED_OFFSET);
	waitFor(time);
	GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << GREEN_LED_OFFSET);
	waitFor(time);
}

// Blue LED
void ledBlue(unsigned long time)
{
	GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << BLUE_LED_OFFSET);
	waitFor(time);
	GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << BLUE_LED_OFFSET);
	waitFor(time);
}


int main(int argc, char** argv)
{
	clear_csr(mie, MIP_MEIP);			// Extern PLIC
	clear_csr(mie, MIP_MTIP);			// Local Timer
	clear_csr(mie, MIP_MSIP);			// Software
	
	// Timer Config
	volatile uint64_t * mtime       = (uint64_t*) (CLINT_CTRL_ADDR + CLINT_MTIME);
	volatile uint64_t * mtimecmp    = (uint64_t*) (CLINT_CTRL_ADDR + CLINT_MTIMECMP);
	uint64_t now = *mtime;
	uint64_t then = now + 2*RTC_FREQ;
	*mtimecmp = then;
	
	set_csr(mie, MIP_MTIP);				// Timer
	set_csr(mstatus, MSTATUS_MIE);			// General
	
	GPIO_REG(GPIO_OUTPUT_EN) |= (1 << GREEN_LED_OFFSET);
	GPIO_REG(GPIO_OUTPUT_EN) |= (1 << RED_LED_OFFSET);
	GPIO_REG(GPIO_OUTPUT_EN) |= (1 << BLUE_LED_OFFSET);

	// LEDs off
	GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << BLUE_LED_OFFSET);
	GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << RED_LED_OFFSET);
	GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << GREEN_LED_OFFSET);

	while(1)
	{

		ledRed(300000);		// Debugging
	}

	return 0;
}

init.c

#include <stdint.h>
#include <stdio.h>
#include <unistd.h>

#include "platform.h"
#include "encoding.h"

#define USE_M_TIME

extern int main(int argc, char** argv);
extern void trap_entry();

..........

#ifdef USE_PLIC
extern void handle_m_ext_interrupt();
#endif

#ifdef USE_M_TIME
extern void handle_m_time_interrupt();
#endif

uintptr_t handle_trap(uintptr_t mcause, uintptr_t epc)
{
	// Green LED
	GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << GREEN_LED_OFFSET);
	
	if (0){
#ifdef USE_PLIC
	// External Machine-Level interrupt from PLIC
	} else if ((mcause & MCAUSE_INT) && ((mcause & MCAUSE_CAUSE) == IRQ_M_EXT)) {
	handle_m_ext_interrupt();
#endif
#ifdef USE_M_TIME
	// External Machine-Level interrupt from PLIC
	} else if ((mcause & MCAUSE_INT) && ((mcause & MCAUSE_CAUSE) == IRQ_M_TIMER)){
	handle_m_time_interrupt();
#endif
	}
	else {
	write(1, "trap\n", 5);
	_exit(1 + mcause);
	}
	return epc;
}

void _init()
{
  
  #ifndef NO_INIT
  use_default_clocks();
  use_pll(0, 0, 1, 31, 1);
  uart_init(115200);

  printf("core freq at %d Hz\n", get_cpu_freq());

  write_csr(mtvec, &trap_entry);
  if (read_csr(misa) & (1 << ('F' - 'A'))) { // if F extension is present
	write_csr(mstatus, MSTATUS_FS); // allow FPU instructions without trapping
	write_csr(fcsr, 0); // initialize rounding mode, undefined at reset
  }
  #endif
  
}

void _fini()
{
}

Probably an unhelpful answer but I pasted your code directly and it worked just fine. Have you tried running it with GDB to spot if it’s trapping?

Thanks for your reply!

What do you mean with ‘running it with GDB’ exactly?

I’m using FreedomStudio under Windows 10.
For downloading a program to the HiFive 1 I’m using GDB OpenOCD Debugging:
–> Debug Configurations --> GDB OpenOCD Debugging --> ‘project name’ Debug --> Debug
–> Run --> Resume

Ah, so you are running GDB behind the scenes. What does the debugger view show after your light only blinks twice? What if you hit the “pause” button, where are you in the code (what is your $pc value)?

When I’m pressing the “pause” button right after the red light began to freeze this is the last output:

Console:

(4159 csr4094 (/32)
(4160) csr4095 (/32)
(4161) priv (/8)
halted at 0x20400f9c due to hardware trigger
halted at 0x20400f9e due to step
halted at 0x0 due to debug interrupt

Debugger Console:

Temporary breakpoint 2, main (argc=0, argv=0x0) at …\main.c:62
62 clear_csr(mie, MIP_MEIP); // Extern PLIC

Program received signal SIGINT, Interrupt.
0x00000000 in ?? ()

Debug:

When I’m not pressing the “pause” button:

Hmm, I wonder if your trap_entry function is not properly aligned (you must have a properly aligned trap vector). In your Freedom Studio, you can see the disassembly. Can you find the assembly for the trap_entry code? What address is it at? What is your $mtvec register set to?

[EDIT: I meant trap_entry, not handle_trap]

I have good news, the Timer Interrupts are working now.

The only thing I have done is to call the function ‘_init()’ from ‘init.c’ before my infinite loop in ‘main.c’.
(I also deleted #ifndef NO_INIT and #endif in ‘_init()’).

So now it is jumping into my ISR :slight_smile:

But why I have to do this manually?
In the example ‘demo_gpio’ I also can’t find the call for ‘_init()’.