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
|
/**
* @file rspboot.s
*
* This RSP microcode program acts as a loader for other microcodes. This ucode:
* - (Optionally) Waits for the RDP to go idle
* - Loads a ucode's .data section to the start of DMEM
* - Loads a ucode's .text section to RSPBOOT_ENTRYPOINT
* - Jumps to the loaded ucode
*
* Along the way it also checks whether the task scheduler running on the CPU has
* requested a yield and, if so, halts the RSP.
*/
.rsp
#include "rcp.h"
#include "sptask.h"
#include "rspboot.h"
// $1 is expected to contain the OSTask pointer by many ucodes
OSTask_reg equ $1
.create CODE_FILE, IMEM_START_VA
entry:
j start
addi OSTask_reg, $zero, OSTASK_ADDR
load_ucode_text_and_enter:
// Read the ucode .text DRAM address from the OSTask
lw $2, OS_TASK_OFF_UCODE(OSTask_reg)
// Always DMA as much as will fit in IMEM to RSPBOOT_ENTRYPOINT
addi $3, $zero, (IMEM_SIZE - (RSPBOOT_ENTRYPOINT - IMEM_START)) - 1
addi $7, $zero, RSPBOOT_ENTRYPOINT
// Start the DMA, assuming it's not busy having waited for it while
// loading the ucode .data
mtc0 $7, SP_MEM_ADDR
mtc0 $2, SP_DRAM_ADDR
mtc0 $3, SP_RD_LEN
// Wait for .text to load
@@while_dma_busy:
mfc0 $4, SP_DMA_BUSY
bnez $4, @@while_dma_busy
nop
// Check yield one more time
jal check_yield
nop
// Release the semaphore just in case a prior task or the CPU failed to release it
// and jump to the ucode that was loaded
jr $7
mtc0 $zero, SP_SEMAPHORE
check_yield:
// Check the YIELD bit in SP_STATUS: if it is set enter yield_break,
// otherwise return
mfc0 $8, SP_STATUS
andi $8, $8, SP_STATUS_YIELD
bnez $8, yield_break
nop
// No need to yield, return to caller
jr $ra
yield_break:
// Release the semaphore (note this happens even if there is no yield as it
// is in the delay slot of the above instruction)
mtc0 $zero, SP_SEMAPHORE
// Update the RSP status to signal task complete and yielded, clear the
// yield request
li $8, (SP_SET_TASKDONE | SP_SET_YIELDED | SP_CLR_YIELD)
mtc0 $8, SP_STATUS
// Halt the RSP
break
nop
// Everything up to here needs to end before RSPBOOT_ENTRYPOINT so it's not overwritten by the .text loader
.if . > RSPBOOT_ENTRYPOINT_VA
.error "Not enough reserved IMEM for .text bootstrapper"
.endif
start:
// Check the DP_WAIT flag, if not set branch straight to loading ucode .data
lw $2, OS_TASK_OFF_FLAGS(OSTask_reg)
andi $2, $2, OS_TASK_DP_WAIT
beqz $2, load_ucode_data
nop
// If the flag is set, we need to wait for the RDP to become idle just in case
// it's still reading commands from DMEM in XBUS mode. First check if we need
// to yield to another task.
jal check_yield
nop
// Now poll RDP DMA busy
mfc0 $2, DPC_STATUS
andi $2, $2, DPC_STATUS_DMA_BUSY
// If the RDP is still busy, check yield again. This will return to the same
// return address as the above jal since $ra has not been clobbered since then.
bgtz $2, check_yield
nop
load_ucode_data:
// Load the DRAM address and size of the ucode .data from the OSTask
lw $2, OS_TASK_OFF_UDATA(OSTask_reg)
lw $3, OS_TASK_OFF_UDATA_SZ(OSTask_reg)
addi $3, $3, -1
// Wait for a DMA slot to free
@@while_dma_full:
mfc0 $30, SP_DMA_FULL
bnez $30, @@while_dma_full
nop
// Submit the DMA transfer for the ucode .data, targeting the start of DMEM
mtc0 $zero, SP_MEM_ADDR
mtc0 $2, SP_DRAM_ADDR
mtc0 $3, SP_RD_LEN
// Wait for the transfer to complete
@@while_dma_busy:
mfc0 $4, SP_DMA_BUSY
bnez $4, @@while_dma_busy
nop
// Check whether we need to yield, does not return if we do
jal check_yield
nop
// Jump to ucode .text loader
j load_ucode_text_and_enter
nop
.align 0x10
.if . > IMEM_END_VA
.error "Not enough room in IMEM"
.endif
.close
|