RISC-V timer interrupt

I am trying to write a short assembly program for timer interrupt on HiFive rev b. So far its was very unsuccessful. If possible could anyone provide a sample program with a handler? Or at least some reading i can do to figure this out?

1 Like

By timer interrupt you mean the CLINT timer?

The steps needed to create a “sleep” are:

  1. Set your desired “wake time” in the MTIMECMP register (64bit so low and high sets).
  2. Enable timer bit on the MIE register.
  3. Call the “wait for interrupt” instruction to go to sleep
  4. Check if you got woken up by the timer by checking the wakeup cause in the MIP register (timer bit)
  5. If done clear the MIE timer bit.

You can see a basic LED blink using this timer here. It’s a Rust quickstart example.

Following that into the source, you can see that the Sleep struct is implemented in the e310-hal crate here which is getting a bit closer to the metal.

You can follow the basics even further into the e310 crate itself however that mostly just encapsulates the basic register access abstraction.

@Almindor Thank you for the explanation! I was thinking on more about interrupting a running program. For example: LED blinking program cycling through LEDs. Have the interrupt timer interrupt the program and blink lets say RED LED 3 times and go back to the main routine. I know you can do this without interrupts, but i want to see how to write an interrupt handler. General register set up I understand, writing the handler is what im not sure about. I understand that when interrupt is triggered the PC is copied in to MEPC and the PC is set to the BASE address? I am a bit confused on how to write the interrupt handler at that BASE address or how to set that BASE address.
Thank you!

Well you can use what I wrote to interrupt the running program too, but in order to go to the handler you’d need to install that as well. I haven’t really done that yet, but IIRC the main thing to do is to set the MTVEC register address and mode.

For basic operation you can just set the BASE address to your handler function’s address and set the MODE bits to 0x0 which means “direct”. This should make it so that any interrupt jumps to your handler.

If you need to differentiate at the handler call directly (e.g. for some reason you don’t or can’t depend on the cause check) you can use the “vectored” mode by setting the MODE bits to 0x1. You need to set individual handlers after the BASE address with offsets equalling to the cause value so each cause has a separate handler.

Hope this helps, it’s all described in the FE310-G002 manual btw.

Okay thank you! I did read the manual, just wanted to see if I understood it correctly! I think i understand it at the basic level. Still not quiet sure about vectored mode.

Vectored mode is IMO a useless complication since you can always check MCAUSE to see why an interrupt was triggered. It’s just a way to have separate handler functions for each cause by having each be on a different offset address from BASE + 4 * <cause value>

I guess if you have a lot of interrupts its easier to just separate them this way instead of having branch conditions for every one of them. One more question, I read about this MSIP register, could you explain what that does? and more importantly what “hart” is? I tried to look online about harts and just found a communication protocol (Highway Addressable Remote Transducer). Im pretty sure its not it.

from what i can see there are only 8 harts in RISC-V? because the MSIP register is from 0x2000000 to 0x2000007

@Almindor oh and sorry for bombardment of questions but in mtvec register, the base address of the handler requires 64 bit alignment. and somehow it has to fit in 30 bits? how is that even done?

A “hart” is just a “hardware thread”. In case of RISC-V MCUs it’s usually a almost stand-alone CPU. Hifive1 and Hifive1-revB only has one hart tho.

AFAIK a MSIP is used to generate software interrupts by writing to it, but I never used it.

It is a 32 bit address that just happens to have zeros in the low order 6 bits. In assembly langue this is accomplished with an “.align 6” pseudo op. In C it is probably some sort of directive in square brackets or a #pragma.

@LightBulb I am completely new to assembly language and programming embedded systems in general, so forgive my questions. How do I go about writing the handler and obtaining its base address? And could you please show example assembly code for assigning this address to the mtvec? I am not sure what .align does in all honesty. All I know is that all my code starts out with “.align 2”.

Here is a description of all the assembler pseudo-operations like .align.