Machine Timer Interrupt (mtime) problem

Hey guys from SiFive team,

I’m facing some issues to enable mtime to generate local interrupts, also I cannot read exactly the value inside the mtime register. What steps exactly I need to run before start waiting for machine time interrupt by compare? I’m using the sample code below with coreplex IP E31 (based on SiFive examples):

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include “platform.h”
#include “plic/plic_driver.h”
#include “gpio.h”
#include “uart.h”

typedef void (*interrupt_function_ptr_t) (void);
interrupt_function_ptr_t localISR[32];

void set_timer()
{
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;

printf("\n\rMTIME:    %d",*mtime);

printf(“\n\rMTIMECMP: %d”,*mtimecmp);
printf(“\n\rMachine timer interrupt!”);
}

/Entry Point for Machine Timer Interrupt Handler/
void machine_timer_int_isr()
{
set_timer();
}

void invalid_local_isr()
{
printf(“\n\rUnexpected local interrupt!”);
}

int main(void)
{
for (int lisr = 0; lisr < 32; lisr++) {
localISR[lisr] = invalid_local_isr;
}

localISR[IRQ_M_TIMER] = machine_timer_int_isr;

set_timer();

// Enable timer interrupts.
set_csr(mie, MIP_MTIP);

// Enable all interrupts
set_csr(mstatus, MSTATUS_MIE);

while(1) {
	//rdtime();
	asm volatile ("wfi");
}

return 0;

}

I don’t know if this is the problem here, but anyway, the E31 registers are 32-bit, only the E51 registers can be safely addressed as 64-bit.

Hello @ild,

Thanks for the answer, about the bit width that you’ve mentioned I think that the compiler do the job of allocating/manipulate two variables each time in 64 through E31. But what I have noted is that when you connect other interruptions in the global interruption port (at E31 eval RTL) the machine timer interrupt seems not work as expected…probably it’s related how PLIC managed this.

it does, but if you care about atomicity, you have to do it manually, as explained in the specs.

Besides this, aignacios example does not show the code how the mtvec csr is set. He also did not tell about the hardware he is using. For example the HiFive1 does not support vectored mode in mtvec, it just supports one global interrupt handler which has to check mcause.

When it is not working there are two possibilities:
a) The interrupt is not triggered
b) The interrupt is triggered but the service routine is not called because of incorrectly set up interrupt handler.

To check a) it is best to just poll the mip csr in the main while(1) loop and check if ithe mtip bit is set.
If this happens the problem must be b)

Hey Thomas,
thanks for the answer, about the hardware that I’m using, it’s in the first question “E31 RTL eval”. And about the mtvec set, check the following code below. As I’m using the default init.c from freedom-e-sdk with a fewer modifications, the only defined symbol its USE_LOCAR_ISR, so in my understanding what happens it’s that mtvec is set with my handler trap entry function that’ll just call the local function defined in the vector (localISR[mcause & MCAUSE_CAUSE] ()). So, in this case (mcause == IRQ_M_TIMER) it should call machine_timer_int_isr(), right?

//See LICENSE for license details.
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>

#include “platform.h”
#include “encoding.h”

#define CPU_FREQ 60000000
#define XSTR(x) #x
#define STR(x) XSTR(x)

#ifndef VECT_IRQ
#define TRAP_ENTRY trap_entry
#else
#define TRAP_ENTRY vtrap_entry
#endif

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

unsigned long get_cpu_freq()
{
return CPU_FREQ;
}

unsigned long get_timer_freq()
{
return get_cpu_freq();
}

uint64_t get_timer_value()
{
#if __riscv_xlen == 32
while (1) {
uint32_t hi = read_csr(mcycleh);
uint32_t lo = read_csr(mcycle);
if (hi == read_csr(mcycleh))
return ((uint64_t)hi << 32) | lo;
}
#else
return read_csr(mcycle);
#endif
}

static void uart_init(size_t baud_rate)
{
//UART0_REG(UART_REG_DIV) = (get_cpu_freq() / 2) / baud_rate - 1;
//UART0_REG(UART_REG_TXCTRL) |= UART_TXEN;
}

#ifdef USE_PLIC
extern void handle_m_ext_interrupt();
#endif

#ifdef USE_M_TIME
extern void set_timer();
#endif

#ifdef USE_LOCAL_ISR
typedef void (*my_interrupt_function_ptr_t) (void);
extern my_interrupt_function_ptr_t localISR;
#endif

#ifndef VECT_IRQ
uintptr_t handle_trap(uintptr_t mcause, uintptr_t epc) attribute((noinline));
uintptr_t handle_trap(uintptr_t mcause, uintptr_t epc)
{
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)){
set_timer();
#endif
#ifdef USE_LOCAL_ISR
} else if (mcause & MCAUSE_INT) {
localISR[mcause & MCAUSE_CAUSE] ();
#endif
}
else {
write(1, “Unhandled Trap:\n”, 16);
_exit(1 + mcause);
}
return epc;
}
#endif

#ifdef USE_CLIC
void trap_entry(void) attribute((interrupt(“SiFive-CLIC-preemptible”), aligned(64)));
void trap_entry(void)
{
unsigned long mcause = read_csr(mcause);
unsigned long mepc = read_csr(mepc);
handle_trap(mcause, mepc);
}
#endif

void _init()
{
#ifndef NO_INIT
uart_set_cfg(0,15);
printf(“\n\rCore freq at " STR(CPU_FREQ) " Hz\n”);

#ifdef USE_CLIC
write_csr(mtvec, ((unsigned long)&trap_entry | MTVEC_CLIC));
#else
write_csr(mtvec, ((unsigned long)&TRAP_ENTRY | MTVEC_VECTORED));
#endif

#endif
}

void _fini()
{
}

@thornschuh and @ilg ,

One thing that I’ve noted it’s the connection of .global_interrupts() input in the E31 RTL code because if I use the scheme like image below, machine timer starts counting as expected, but If I use the commented way, it just does not happens…I don’t know how the slack parameters affect the mtimer reg synthesis.
Screenshot from 2018-10-31 09-51-11