summaryrefslogtreecommitdiff
path: root/software/first/setup_timer_irq.S
blob: 97b5f6c42757d7df3fa22e4953a89164cdc44dcf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
.section .text
.align 2
.global setup_timer_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

	lw t0, irq_handler  # load address of irq_handler 
	slli t0, t0, 1      # shift address left by one; LSB will be 0 which is direct mode (what we want)
	csrw mtvec, t0      # write handler address and mode

  # Here we cannot use csrsi because it can only set up to 5 bits
	li t0, 0x80     # set 7th bit (indexed from 0)
	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, 0x4  # set 3rd 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


irq_handler:
	# save only the registers we use in this function
	addi sp, sp, -24 # Allocate stack frame
	sw ra, 20(sp)    # save return address to the stack
	sw t0, 16(sp)    # save temporary
	sw t1, 12(sp)    # save temporary
	sw t2, 8(sp)    # save temporary
	sw t3, 4(sp)    # save temporary

	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 exit

switchOnBlue:
	li t2, GPIO_BLUE_LED   # load value indicating blue LED
	j exit

switchOnGreen:
	li t2, GPIO_GREEN_LED   # load value indicating green LED
	j exit


exit:
	sw t2, GPIO_OUTPUT_VAL(t0)   # write new LED values to the right address
	lw ra, 20(sp)   # load return address
	lw t0, 16(sp)   # restore temporary
	lw t1, 12(sp)   # restore temporary
	lw t2, 8(sp)    # restore temporary
	lw t3, 4(sp)    # restore temporary
	addi sp, sp, 24 # deallocate stack frame
	mret            # exit IRQ handler