.section .text .align 2 .global setup_timer_irq .global wait_for_irq .include "gpio.inc" .include "memory_map.inc" setup_timer_irq: addi sp, sp, -16 # Allocate stack frame sw ra, 12(sp) # save return address to the stack la t0, irq_handler # load address of irq_handler csrw mtvec, t0 # write handler address and mode # Here we cannot use csrsi because it can only set up to 5 bits li t0, 0x88 # set 4rd and 8th bit csrw mie, t0 # activate machine timer interrupt # write high value to the timercmp just to be sure we don't # trigger an interrupt by mistake li t0, 0x7FFFFFFF # high number li t1, 0xFFFFFFFF # high number li t2, MTIMECMP # load address of timecmp register sw t0, 4(t2) # store the first number to the upper part first sw t1, 0(t2) # then the second to the lower part csrsi mstatus, 0x8 # set 4rd bit to activate machine interrupts. lw ra, 12(sp) # load return address addi sp, sp, 16 # deallocate stack frame ret # Unix C ABI has high context switch cost. The current RISC-V ABI # requires (ignoring floating-point registers; from # https://content.riscv.org/wp-content/uploads/2018/05/08.45-09.10-RISCV-20180509-FastInts.pdf # ): # # addi sp, sp, -FRAMESIZE # sw ra, x(sp) # 1 return address # sw t0-t6, x(sp) # 7 temporaries # sw a0-a7, x(sp) # 8 arguments # # jal c_handler # # lw a0-a7, x(sp) # 8 arguments # lw t0-t6, x(sp) # 7 temporaries # lw ra, x(sp) # 1 return address # addi sp, sp, FRAMESIZE # mret .align 6 irq_handler: # save only the registers we use in this function addi sp, sp, -28 # Allocate stack frame sw ra, 24(sp) # save return address to the stack sw t0, 20(sp) # save temporary sw t1, 16(sp) # save temporary sw t2, 12(sp) # save temporary sw t3, 8(sp) # save temporary sw a0, 4(sp) # save first argument to function li t0, 0x80000007 # irqcause + 7 (machine timer interrupt) csrr t1, mcause # load cause CSR and t1, t1, t0 # check if it's a timer interrupt bne t1, t0, exit li t0, GPIO_CTRL_ADDR # load base GPIO address lw t1, GPIO_OUTPUT_VAL(t0) # load state beqz t1, switchOnRed # if none is blinking yet, blink the red one li t3, GPIO_RED_LED beq t1, t3, switchOnBlue # if red is on, switch on blue LED li t3, GPIO_BLUE_LED beq t1, t3, switchOnGreen # if blue is on, switch on green LED # here, neither the red or blue are on, so the one that is on # must be the green one. In that case we just fall through to # switch on the red LED switchOnRed: li t2, GPIO_RED_LED # load value indicating red LED j setAndexit switchOnBlue: li t2, GPIO_BLUE_LED # load value indicating blue LED j setAndexit switchOnGreen: li t2, GPIO_GREEN_LED # load value indicating green LED j setAndexit setAndexit: sw t2, GPIO_OUTPUT_VAL(t0) # write new LED values to the right address li a0, 200 # load the desired delay value (in ms) as the first arg to the function jal delay # call delay function to set new delay exit: lw ra, 24(sp) # load return address lw t0, 20(sp) # restore temporary lw t1, 16(sp) # restore temporary lw t2, 12(sp) # restore temporary lw t3, 8(sp) # restore temporary lw a0, 4(sp) # restore first argument to function addi sp, sp, 28 # deallocate stack frame mret # exit IRQ handler wait_for_irq: wfi ret