CLINT and sleep?

I’ve got the basics working with Hifive1 rev B using Rust crates from https://github.com/riscv-rust

Serial, build-in LEDs all work fine. I suspect GPIO will work fine too but I didn’t check yet.

I’m trying to find out how timing/sleep work with the hardware interrupts. Metal is a bit difficult to parse out for me with all the abstractions and vtable stuff.

I tried reading into the CLINT part of the spec for hifive1 but I don’t fully understand how it’s supposed to work on the lowest level (e.g. register sets).

Do you set the mtimecmp registers to current_time (taken from the time register) + “cycles needed to sleep” to cause a hardware sleep & wake? How do you actually “fire it off” ?

Thanks

1 Like

I’m learning about these things too.
On https://www.sifive.com/documentation the document “SiFive E31 Core Complex Manual” might help you.

Section 6.3 Timer Registers says:
“mtime is a 64-bit read-write register that contains the number of cycles counted from the rtc_toggle
signal described in the E31 Core Complex User Guide. A timer interrupt is pending whenever mtime is greater than or equal to the value in the mtimecmp register.”
rtc_toggle has a 32 kHz frequency for HiFive1. mtime is constantly increasing, just set mtimecmp to create a future exception.

Yes, you need to enable interrupts and you need to set an interrupt handler.

Global Interrupts must be enabled.
Section 5.3.1 Machine Status Register (mstatus) describes it.
The bit 3 is called MIE, or Machine Interrupt Enable.

Individual interrupts are enabled by setting the appropriate bit in the mie register.
Section 5.3.3 Machine Interrupt Enable Register (mie) describes it.
The bit 7 is called MTIE, or Machine Timer Interrupt Enable.

IMPORTANT: mstatus.mie is a bit and mie is a register. Both need to be used in order to enable interrupts.

And finally set your handler using mtvec register, described on Section 5.3.2 Machine Trap Vector (mtvec): “By default, all interrupts trap to a single address defined in the mtvec register.”

1 Like

Thanks! This is a really nice explanation and I think I get the logic. You wouldn’t happen to be also playing around with Rust and their abstraction for embeded-hal would you?

I’m having trouble finding out for example where mstatus or mie would be in their abstraction, I only see things like mtime and mtimer and mtimecmp.

Is this just missing or am I not seeing some part of their abstraction logic?

NVM: solved, I didn’t realize that e310x-hal (and e310x) wouldn’t contain base CPU stuff like mstatus registers and that they would be in the riscv crate :slight_smile:

Got things to work, since rust is very peculiar about sharing resources (even on single core) I didn’t figure out how to get to Peripherals from an interrupt handler but since I only care about causing a non-cycling sleep I just used an empty handler as a block in a loop (with mtimecmp reset to new value each time).

Works fine for what I need atm. One thing other people might find useful is that in the riscv crate you can “wait until interrupt” using the riscv::asm::wfi() call. NOTE this is just a soft sleep, there’s a better option for longer periods using the PMU.