summaryrefslogtreecommitdiff
path: root/libcpu/arm/cortex-r4/context_ccs.asm
blob: 6a734cb494aa41b632042597bc4dae18668bad68 (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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
;/*
; * Copyright (c) 2006-2022, RT-Thread Development Team
; *
; * SPDX-License-Identifier: Apache-2.0
; *
; * Change Logs:
; * Date           Author       Notes
; * 2009-01-20     Bernard      first version
; * 2011-07-22     Bernard      added thumb mode porting
; * 2013-05-24     Grissiom     port to CCS
; * 2013-05-26     Grissiom     optimize for ARMv7
; */

   .text
   .arm
   .ref rt_thread_switch_interrupt_flag
   .ref rt_interrupt_from_thread
   .ref rt_interrupt_to_thread
   .ref rt_interrupt_enter
   .ref rt_interrupt_leave
   .ref rt_hw_trap_irq

;/*
; * rt_base_t rt_hw_interrupt_disable();
; */
    .def rt_hw_interrupt_disable
    .asmfunc
rt_hw_interrupt_disable
    MRS r0, cpsr
    CPSID IF
    BX  lr
    .endasmfunc

;/*
; * void rt_hw_interrupt_enable(rt_base_t level);
; */
    .def rt_hw_interrupt_enable
    .asmfunc
rt_hw_interrupt_enable
    MSR cpsr_c, r0
    BX  lr
    .endasmfunc

;/*
; * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
; * r0 --> from
; * r1 --> to
; */
    .def rt_hw_context_switch
    .asmfunc
rt_hw_context_switch
    STMDB   sp!, {lr}           ; push pc (lr should be pushed in place of PC)
    STMDB   sp!, {r0-r12, lr}   ; push lr & register file

    MRS     r4, cpsr
    TST     lr, #0x01
    ORRNE   r4, r4, #0x20       ; it's thumb code

    STMDB   sp!, {r4}           ; push cpsr

    .if (__TI_VFP_SUPPORT__)
        VMRS    r4,  fpexc
        TST     r4,  #0x40000000
        BEQ     __no_vfp_frame1
        VSTMDB  sp!, {d0-d15}
        VMRS    r5, fpscr
        ; TODO: add support for Common VFPv3.
        ;       Save registers like FPINST, FPINST2
        STMDB   sp!, {r5}
__no_vfp_frame1
        STMDB   sp!, {r4}
    .endif

    STR     sp, [r0]            ; store sp in preempted tasks TCB
    LDR     sp, [r1]            ; get new task stack pointer

    .if (__TI_VFP_SUPPORT__)
        LDMIA   sp!, {r0}       ; get fpexc
        VMSR    fpexc,  r0      ; restore fpexc
        TST     r0,  #0x40000000
        BEQ     __no_vfp_frame2
        LDMIA   sp!, {r1}       ; get fpscr
        VMSR    fpscr, r1
        VLDMIA  sp!, {d0-d15}
__no_vfp_frame2
    .endif

    LDMIA   sp!, {r4}           ; pop new task cpsr to spsr
    MSR     spsr_cxsf, r4

    LDMIA   sp!, {r0-r12, lr, pc}^ ; pop new task r0-r12, lr & pc, copy spsr to cpsr
    .endasmfunc

;/*
; * void rt_hw_context_switch_to(rt_uint32 to);
; * r0 --> to
; */
    .def rt_hw_context_switch_to
    .asmfunc
rt_hw_context_switch_to
    LDR     sp, [r0]            ; get new task stack pointer

    .if (__TI_VFP_SUPPORT__)
        LDMIA   sp!, {r0}       ; get fpexc
        VMSR    fpexc, r0
        TST     r0,  #0x40000000
        BEQ     __no_vfp_frame_to
        LDMIA   sp!, {r1}       ; get fpscr
        VMSR    fpscr, r1
        VLDMIA  sp!, {d0-d15}
__no_vfp_frame_to
    .endif

    LDMIA   sp!, {r4}           ; pop new task cpsr to spsr
    MSR     spsr_cxsf, r4

    LDMIA   sp!, {r0-r12, lr, pc}^ ; pop new task r0-r12, lr & pc, copy spsr to cpsr
    .endasmfunc

;/*
; * void rt_hw_context_switch_interrupt(rt_uint32 from, rt_uint32 to);
; */

    .def rt_hw_context_switch_interrupt
    .asmfunc
rt_hw_context_switch_interrupt
    LDR r2, pintflag
    LDR r3, [r2]
    CMP r3, #1
    BEQ _reswitch
    MOV r3, #1              ; set rt_thread_switch_interrupt_flag to 1
    STR r3, [r2]
    LDR r2, pfromthread     ; set rt_interrupt_from_thread
    STR r0, [r2]
_reswitch
    LDR r2, ptothread       ; set rt_interrupt_to_thread
    STR r1, [r2]
    BX  lr
    .endasmfunc

    .def IRQ_Handler
IRQ_Handler
    STMDB   sp!, {r0-r12,lr}

    .if (__TI_VFP_SUPPORT__)
        VMRS    r0,  fpexc
        TST     r0,  #0x40000000
        BEQ     __no_vfp_frame_str_irq
        VSTMDB  sp!, {d0-d15}
        VMRS    r1, fpscr
        ; TODO: add support for Common VFPv3.
        ;       Save registers like FPINST, FPINST2
        STMDB   sp!, {r1}
__no_vfp_frame_str_irq
        STMDB   sp!, {r0}
    .endif

    BL  rt_interrupt_enter
    BL  rt_hw_trap_irq
    BL  rt_interrupt_leave

    ; if rt_thread_switch_interrupt_flag set, jump to
    ; rt_hw_context_switch_interrupt_do and don't return
    LDR r0, pintflag
    LDR r1, [r0]
    CMP r1, #1
    BEQ rt_hw_context_switch_interrupt_do

    .if (__TI_VFP_SUPPORT__)
        LDMIA   sp!, {r0}       ; get fpexc
        VMSR    fpexc, r0
        TST     r0,  #0x40000000
        BEQ     __no_vfp_frame_ldr_irq
        LDMIA   sp!, {r1}       ; get fpscr
        VMSR    fpscr, r1
        VLDMIA  sp!, {d0-d15}
__no_vfp_frame_ldr_irq
    .endif

    LDMIA   sp!, {r0-r12,lr}
    SUBS    pc, lr, #4

; /*
; * void rt_hw_context_switch_interrupt_do(rt_base_t flag)
; */
    .def rt_hw_context_switch_interrupt_do
rt_hw_context_switch_interrupt_do
    MOV     r1,  #0           ; clear flag
    STR     r1,  [r0]

    .if (__TI_VFP_SUPPORT__)
        LDMIA   sp!, {r0}       ; get fpexc
        VMSR    fpexc, r0
        TST     r0,  #0x40000000
        BEQ     __no_vfp_frame_do1
        LDMIA   sp!, {r1}       ; get fpscr
        VMSR    fpscr, r1
        VLDMIA  sp!, {d0-d15}
__no_vfp_frame_do1
    .endif

    LDMIA   sp!, {r0-r12,lr}  ; reload saved registers
    STMDB   sp, {r0-r3}       ; save r0-r3. We will restore r0-r3 in the SVC
                              ; mode so there is no need to update SP.
    SUB     r1,  sp, #16      ; save the right SP value in r1, so we could restore r0-r3.
    SUB     r2,  lr, #4       ; save old task's pc to r2

    MRS     r3,  spsr         ; get cpsr of interrupt thread

    ; switch to SVC mode and no interrupt
    CPSID   IF, #0x13

    STMDB   sp!, {r2}         ; push old task's pc
    STMDB   sp!, {r4-r12,lr}  ; push old task's lr,r12-r4
    LDMIA   r1!, {r4-r7}      ; restore r0-r3 of the interrupted thread
    STMDB   sp!, {r4-r7}      ; push old task's r3-r0. We don't need to push/pop them to
                              ; r0-r3 because we just want to transfer the data and don't
                              ; use them here.
    STMDB   sp!, {r3}         ; push old task's cpsr

    .if (__TI_VFP_SUPPORT__)
        VMRS    r0,  fpexc
        TST     r0,  #0x40000000
        BEQ     __no_vfp_frame_do2
        VSTMDB  sp!, {d0-d15}
        VMRS    r1, fpscr
        ; TODO: add support for Common VFPv3.
        ;       Save registers like FPINST, FPINST2
        STMDB   sp!, {r1}
__no_vfp_frame_do2
        STMDB   sp!, {r0}
    .endif

    LDR     r4,  pfromthread
    LDR     r5,  [r4]
    STR     sp,  [r5]         ; store sp in preempted tasks's TCB

    LDR     r6,  ptothread
    LDR     r6,  [r6]
    LDR     sp,  [r6]         ; get new task's stack pointer

    .if (__TI_VFP_SUPPORT__)
        LDMIA   sp!, {r0}       ; get fpexc
        VMSR    fpexc, r0
        TST     r0,  #0x40000000
        BEQ     __no_vfp_frame_do3
        LDMIA   sp!, {r1}       ; get fpscr
        VMSR    fpscr, r1
        VLDMIA  sp!, {d0-d15}
__no_vfp_frame_do3
    .endif

    LDMIA   sp!, {r4}         ; pop new task's cpsr to spsr
    MSR     spsr_cxsf, r4

    LDMIA   sp!, {r0-r12,lr,pc}^ ; pop new task's r0-r12,lr & pc, copy spsr to cpsr

pintflag     .word   rt_thread_switch_interrupt_flag
pfromthread  .word   rt_interrupt_from_thread
ptothread    .word   rt_interrupt_to_thread