summaryrefslogtreecommitdiff
path: root/software/first/setup_timer_irq.S
blob: c3e75132cd8bbe0fe152c08d4127332fd3e7186a (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
95
96
97
98
99
.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, 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, 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, -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, 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
	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

	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