RTC Not Firing Machine Interrupt

I will be as detailed as possible. I have tried and debugged everything I can possibly think of. Either RTC does not cause a machine level timer interrupt, or I’m missing something.
Here is the start of the code. _play is called from main.c. This is all in sifive freedom studio, under a blank solution.

.equ .sys.high_mem,		0x80004000

.align 4
.text
.globl _play
_play:
    lui sp, %pcrel_hi(.sys.high_mem)
    jal ra, _set_mtvec
    jal ra, _set_plic
    jal ra, _gpio_init
    jal ra, _gpio_green
    jal ra, _set_rccmp
    wfi
    j .

First I enable machine level interrupts and define a global _fail_handler, this handler has a breakpoint. It never gets hit.

.align 4
.globl _set_mtvec
_set_mtvec:
    csrrci t0, mstatus, 3                   # disable interrupts
    la a0, _fail_handler                    # loads fail handler
    csrrw a1, mtvec, a0                     # get address of fail handler
    csrrci t1, mtvec, 1                     # set to direct mode
    csrrsi t0, mstatus, 3                   # enable interrupts
	ret
.align 8
_fail_handler:
    addi t0, x0, 0xFFFFFFFF
    ...

I have confirmed it does get hit if i tried to write to ROM causing a synchronous machine exception.

Next is plic and aon.

.equ PLIC_BASE,         0x0C000008
.equ PLIC_ENABLE,       0x0C002000
.equ PLIC_THRESHOLD,    0x0C200000
.equ PLIC_CLAIM,		0x0C200004
.globl _set_plic
_set_plic:
    la t0, PLIC_BASE
    lw t1, 0(t0)
    ori t1, t1, 0x7
    sw t1, 0(t0)
    la t0, PLIC_ENABLE
    lw t1, 0(t0)
    ori t1, t1, 0x4
    sw t1, 0(t0)                # right after doing this there appear to be some bits set in the PLIC_CLAIM, but not the 3rd bit. appears to be wdog
    la t0, PLIC_THRESHOLD
    lw t1, 0(t0)
    and t1, t1, x0
    sw t1, 0(t0)
    la t0, PLIC_CLAIM				#Set all claims to 0
    lw t1, 0(t0)
    sw t1, 0(t0)
    ret

Next is setting up RTC.

.globl _set_rccmp
_set_rccmp:
    li t0, AONBASE          # gets the AON base address
    lw t1, rtccfg(t0)       # loads the data at config which is rtcscale
    li t2, 0x100F
    or t2, t1, t2         	# ori the rtcscale to seconds in t1, and enable
    sw t2, rtccfg(t0)       # store the new rtcscale
.align 8                    # do this just in case, rtcscale needs a few cycles. disassembly does show several nops inbetween.
    lw t1, rtcs(t0)         # loads the current clock time
    addi t1, t1, 0x30        # set to 0x30 to ensure rtcs doesn't go above rtccmp until after this proc is exited.
    sw t1, rtccmp0(t0)      # sets the time to compare several seconds higher
    ret

And through lots of debugging, i have confirmed that the rtccmp will eventually trigger the rtcip0 flag, every setup address/register seems to be set exactly how the riscv gods intended the docs to be interpreted, but control will never flow to the fail_handler. So I feel as though I have failed the riscv gods and misinterpreted their work.

Are you touching the mie CSR at all?

There is a bare metal example you could check your work against here.

@dconn That example appears to just read time, and does not set up PLIC + rtc interrupts. As far as your question.
I debugged my _set_mtvec function and it looks like the mie bit doesn’t get enabled for some reason!

_set_mtvec:
    csrrci t0, mstatus, 3                   # disable interrupts
    la a0, _fail_handler                    # loads fail handler
    csrrw a1, mtvec, a0                     # get address of fail handler
    csrrci t1, mtvec, 1                     # set to direct mode
    csrrsi t2, mcause, 0					# just read the value
    csrrsi a0, mstatus, 3                   # enable interrupts
	ret

A breakpoint at ret indicated that a0 was 0x1800. AND mstatus itself was 0x1800. I have no idea how it got in that state, so I decided to throw back in the code that gave me my initial _fail_handler trigger. After _set_mtvec I used

lui sp, %pcrel_hi(.sys.high_mem)
jal ra, _set_mtvec
jal ra, _set_plic
jal ra, _gpio_init
jal ra, _gpio_green
#    jal ra, _set_rccmp
#    jal ra, _set_wdog
# cause an interrupt
la a1, 0x20010FFD
addi a0, x0, 1
sw a0, (a1)

This still triggers the fail handler, when I let mret do its job and return to the sw a0, (a1), mstatus was set! it was 0x6. I didn’t let it execute again and turned off the device.

mstatus is still 0x1800 on reboot, and not getting set to enabled. So it seems to me the documentation is incorrect in the 8.2.1 section of fe310-g002.

Interrupts can be re-enabled by explicitly setting mstatus.MIE or by executing an MRET instruction to exit the handler.

This doesn’t appear to be the case, as my csrrsi is not working, and synchronous machine exceptions are still happening, even with interrupts disabled.

There is no disable bit for exceptions, as they are enabled by default out of reset.

Hardware will actually disable mstatus.MIE upon handler entry by default. Also, I would suggest writing mstatus.MIE (global interrupt enable) as the last step until you get everything else configured. Ensure mie.MEIP is also enabled.

The previous example was suggested as a reference for the interrupt set up - but there also exists low level PLIC example as well.


Cheers

I’m at a loss @dconn. Every boot, mie is 0, every csrrsi for the 3rd bit on mstatus.MIE is 0. exceptions are the only thing being trapped.

In the lower left of the image you can see mstatus is 0x1800.

Not only that but the documentation clearly says you can set the mstatus.MIE to renable interrupts as per my previous response. It will take me a day or two to go over your C code you posted. I will try to glean anything I may have missed.

Also double check your PLIC priorities are programmed to be higher than the threshold.