|
10 | 10 | .section .text.call_start_cpu0 |
11 | 11 | 1: |
12 | 12 | .long _stack_top |
| 13 | +.Lmain_addr: |
| 14 | + .long main |
| 15 | +.Lrom_mmu_init: |
| 16 | + .long 0x400095a4 // mmu_init(int cpu_no) |
| 17 | +.Lrom_cache_flash_mmu_set: |
| 18 | + .long 0x400095e0 // cache_flash_mmu_set(cpu, pid, vaddr, paddr, pgsz, pgcnt) |
| 19 | +.Lrom_Cache_Read_Enable: |
| 20 | + .long 0x40009a84 // Cache_Read_Enable(int cpu_no) |
| 21 | +.Lrom_Cache_Read_Disable: |
| 22 | + .long 0x40009ab8 // Cache_Read_Disable(int cpu_no) |
| 23 | +.Lrom_Cache_Flush: |
| 24 | + .long 0x40009a14 // Cache_Flush(int cpu_no) |
| 25 | +.Lrodata_start: |
| 26 | + .long _rodata_start |
| 27 | +.Lrodata_end: |
| 28 | + .long _rodata_end |
| 29 | +.Ltext_start: |
| 30 | + .long _text_start |
| 31 | +.Ltext_end: |
| 32 | + .long _text_end |
| 33 | +.Ldport_pro_cache_ctrl1: |
| 34 | + .long 0x3FF00044 // DPORT_PRO_CACHE_CTRL1_REG |
| 35 | +.Ldrom_paddr_ptr: |
| 36 | + .long _drom_flash_addr // pointer to builder-patched DROM flash offset |
| 37 | +.Ldrom_vaddr: |
| 38 | + .long 0x3F400000 // DROM virtual base address |
| 39 | +.Lirom_vaddr: |
| 40 | + .long 0x400D0000 // IROM virtual base address |
| 41 | +.Lmmu_table_base: |
| 42 | + .long 0x3FF10000 // PRO CPU Flash MMU table |
| 43 | +.Lrtc_wdt_protect: |
| 44 | + .long 0x3FF480A4 // RTC_CNTL_WDTWPROTECT_REG |
| 45 | +.Lrtc_wdt_key: |
| 46 | + .long 0x50D83AA1 // WDT write-protect key |
| 47 | +.Lrtc_wdt_config0: |
| 48 | + .long 0x3FF4808C // RTC_CNTL_WDTCONFIG0_REG |
| 49 | +.Ltimg0_wdt_protect: |
| 50 | + .long 0x3FF5F064 // TIMG0_WDTWPROTECT_REG |
| 51 | +.Ltimg0_wdt_config0: |
| 52 | + .long 0x3FF5F048 // TIMG0_WDTCONFIG0_REG |
| 53 | +.Lvector_table: |
| 54 | + .long _vector_table |
| 55 | + |
13 | 56 | .global call_start_cpu0 |
14 | 57 | call_start_cpu0: |
15 | 58 | // We need to set the stack pointer to a different value. This is somewhat |
@@ -47,11 +90,203 @@ call_start_cpu0: |
47 | 90 | wsr.cpenable a2 |
48 | 91 | rsync |
49 | 92 |
|
50 | | - // Jump to the runtime start function written in Go. |
51 | | - call4 main |
| 93 | + // Disable the RTC and TIMG0 watchdogs before configuring the flash cache. |
| 94 | + // The ROM bootloader leaves them running; a fault during cache setup would |
| 95 | + // otherwise reset the chip. The Go runtime re-disables them once it starts. |
| 96 | + l32r a2, .Lrtc_wdt_protect |
| 97 | + l32r a3, .Lrtc_wdt_key |
| 98 | + s32i a3, a2, 0 // unlock WDT write-protect |
| 99 | + memw |
| 100 | + l32r a2, .Lrtc_wdt_config0 |
| 101 | + movi a3, 0 |
| 102 | + s32i a3, a2, 0 // disable WDT (write 0 to config0) |
| 103 | + memw |
| 104 | + |
| 105 | + // Disable TG0 WDT (Timer Group 0 Main Watchdog). |
| 106 | + // TIMG0_WDTWPROTECT_REG = 0x3FF5F064, TIMG0_WDTCONFIG0_REG = 0x3FF5F048 |
| 107 | + l32r a2, .Ltimg0_wdt_protect |
| 108 | + l32r a3, .Lrtc_wdt_key // same unlock key 0x50D83AA1 |
| 109 | + s32i a3, a2, 0 |
| 110 | + memw |
| 111 | + l32r a2, .Ltimg0_wdt_config0 |
| 112 | + movi a3, 0 |
| 113 | + s32i a3, a2, 0 |
| 114 | + memw |
| 115 | + |
| 116 | + // Set VECBASE to our vector table. Must happen before any callx4 so that |
| 117 | + // register-window overflow exceptions route to our handlers. |
| 118 | + l32r a2, .Lvector_table |
| 119 | + wsr.vecbase a2 |
| 120 | + rsync |
| 121 | + |
| 122 | + // Clear PS.EXCM so window overflow exceptions work properly. |
| 123 | + rsr.ps a2 |
| 124 | + movi a3, ~0x1F |
| 125 | + and a2, a2, a3 |
| 126 | + movi a3, 0x20 // PS.UM = 1 |
| 127 | + or a2, a2, a3 |
| 128 | + wsr.ps a2 |
| 129 | + rsync |
| 130 | + |
| 131 | + // ---- Configure flash cache and MMU ---- |
| 132 | + // The ROM bootloader only loaded IRAM/DRAM segments. We must now set up |
| 133 | + // the ICache MMU to map DROM (.rodata) and IROM (.text) from flash. |
| 134 | + |
| 135 | + // 0. Disable cache and flush before reconfiguring MMU. |
| 136 | + // The ROM bootloader may leave the cache enabled. |
| 137 | + movi a6, 0 // cpu_no = 0 |
| 138 | + mov a5, a1 |
| 139 | + l32r a4, .Lrom_Cache_Read_Disable |
| 140 | + callx4 a4 |
| 141 | + |
| 142 | + movi a6, 0 // cpu_no = 0 |
| 143 | + mov a5, a1 |
| 144 | + l32r a4, .Lrom_Cache_Flush |
| 145 | + callx4 a4 |
| 146 | + |
| 147 | + // 1. Reset MMU tables for PRO CPU (clear all entries to invalid). |
| 148 | + movi a6, 0 // cpu_no = 0 (PRO) |
| 149 | + mov a5, a1 |
| 150 | + l32r a4, .Lrom_mmu_init |
| 151 | + callx4 a4 |
| 152 | + |
| 153 | + // 2. Map DROM pages via ROM cache_flash_mmu_set: |
| 154 | + // vaddr=0x3F400000, paddr=0x10000, page_size=64KB, count=ceil(size/64KB) |
| 155 | + l32r a2, .Lrodata_end |
| 156 | + l32r a3, .Lrodata_start |
| 157 | + sub a2, a2, a3 // a2 = rodata size in bytes |
| 158 | + beqz a2, .Lskip_drom // skip if no rodata |
| 159 | + addi a2, a2, -1 |
| 160 | + srli a2, a2, 16 |
| 161 | + addi a2, a2, 1 // a2 = drom page count |
| 162 | + |
| 163 | + // cache_flash_mmu_set(cpu=0, pid=0, vaddr, paddr, psize=64, num) |
| 164 | + movi a6, 0 |
| 165 | + movi a7, 0 |
| 166 | + l32r a8, .Ldrom_vaddr // 0x3F400000 |
| 167 | + l32r a9, .Ldrom_paddr_ptr |
| 168 | + l32i a9, a9, 0 // a9 = DROM flash offset (builder-patched) |
| 169 | + movi a10, 64 |
| 170 | + mov a11, a2 |
| 171 | + mov a5, a1 |
| 172 | + l32r a4, .Lrom_cache_flash_mmu_set |
| 173 | + callx4 a4 |
| 174 | +.Lskip_drom: |
52 | 175 |
|
| 176 | + // 3. Map IROM pages via ROM cache_flash_mmu_set: |
| 177 | + // vaddr=0x400D0000, paddr=0x10000 + drom_pages*64KB |
| 178 | + l32r a2, .Ltext_end |
| 179 | + l32r a3, .Ltext_start |
| 180 | + sub a2, a2, a3 // a2 = text size in bytes |
| 181 | + beqz a2, .Lskip_irom // skip if no text |
| 182 | + addi a2, a2, -1 |
| 183 | + srli a2, a2, 16 |
| 184 | + addi a2, a2, 1 // a2 = irom page count |
| 185 | + |
| 186 | + // Compute IROM paddr into a3 = DROM flash offset + drom_pages * 64KB |
| 187 | + l32r a9, .Lrodata_end |
| 188 | + l32r a3, .Lrodata_start |
| 189 | + sub a9, a9, a3 // a9 = rodata size |
| 190 | + l32r a3, .Ldrom_paddr_ptr |
| 191 | + l32i a3, a3, 0 // a3 = DROM flash offset (builder-patched) |
| 192 | + beqz a9, .Lirom_paddr_ready |
| 193 | + addi a9, a9, -1 |
| 194 | + srli a9, a9, 16 |
| 195 | + addi a9, a9, 1 // a9 = drom page count |
| 196 | + slli a9, a9, 16 // a9 = drom_pages * 64KB |
| 197 | + add a3, a3, a9 // a3 = drom_flash_addr + drom_pages * 64KB |
| 198 | +.Lirom_paddr_ready: |
| 199 | + |
| 200 | + movi a6, 0 |
| 201 | + movi a7, 0 |
| 202 | + l32r a8, .Lirom_vaddr // 0x400D0000 |
| 203 | + mov a9, a3 // irom paddr |
| 204 | + movi a10, 64 |
| 205 | + mov a11, a2 |
| 206 | + mov a5, a1 |
| 207 | + l32r a4, .Lrom_cache_flash_mmu_set |
| 208 | + callx4 a4 |
| 209 | +.Lskip_irom: |
| 210 | + |
| 211 | + // 4. Unmask DROM0 and IRAM0 cache buses in DPORT_PRO_CACHE_CTRL1_REG: |
| 212 | + // bit 4: PRO_CACHE_MASK_DROM0 (data flash, 0x3F400000) |
| 213 | + // bit 0: PRO_CACHE_MASK_IRAM0 (instruction flash, 0x400D0000 window) |
| 214 | + l32r a2, .Ldport_pro_cache_ctrl1 |
| 215 | + l32i a3, a2, 0 |
| 216 | + movi a4, ~0x11 // clear bit 0 (IRAM0) and bit 4 (DROM0) |
| 217 | + and a3, a3, a4 |
| 218 | + s32i a3, a2, 0 |
| 219 | + memw |
| 220 | + |
| 221 | + // 5. Enable flash cache for PRO CPU. |
| 222 | + movi a6, 0 // cpu_no = 0 |
| 223 | + mov a5, a1 |
| 224 | + l32r a4, .Lrom_Cache_Read_Enable |
| 225 | + callx4 a4 |
| 226 | + |
| 227 | + isync |
| 228 | + |
| 229 | + // ---- Jump to main (in IROM/flash, now accessible) ---- |
| 230 | + mov a5, a1 |
| 231 | + l32r a4, .Lmain_addr |
| 232 | + callx4 a4 |
| 233 | + |
| 234 | + // If main returns, loop forever. |
| 235 | +1: j 1b |
| 236 | + |
| 237 | +// ----------------------------------------------------------------------- |
| 238 | +// tinygo_scanCurrentStack — Spill all Xtensa register windows to the |
| 239 | +// stack, then call tinygo_scanstack(sp) so the conservative GC can |
| 240 | +// discover live heap pointers that are currently in physical registers. |
| 241 | +// |
| 242 | +// On RISC-V / ARM the equivalent function pushes callee-saved registers |
| 243 | +// before the call. On Xtensa windowed ABI the same effect is achieved |
| 244 | +// by forcing hardware window-overflow for every occupied pane: each |
| 245 | +// overflow saves the four registers in that pane to the stack frame |
| 246 | +// pointed to by the pane's a1 (sp). After all panes are flushed, a |
| 247 | +// scan from the current sp to stackTop covers every live value. |
| 248 | +// |
| 249 | +// Without this spill the conservative GC misses heap pointers held only |
| 250 | +// in physical registers, frees live objects, and later crashes jumping |
| 251 | +// through a freed/garbage function pointer (e.g. a goroutine trampoline). |
| 252 | +// ----------------------------------------------------------------------- |
53 | 253 | .section .text.tinygo_scanCurrentStack |
| 254 | + |
54 | 255 | .global tinygo_scanCurrentStack |
55 | 256 | tinygo_scanCurrentStack: |
56 | | - // TODO: save callee saved registers on the stack |
57 | | - j tinygo_scanstack |
| 257 | + entry a1, 48 |
| 258 | + |
| 259 | + // Disable interrupts while flushing register windows. |
| 260 | + rsr a4, PS |
| 261 | + s32i a4, a1, 0 // save PS for later restore |
| 262 | + rsil a4, 3 // XCHAL_EXCM_LEVEL |
| 263 | + |
| 264 | + // Flush all register windows using recursive call4. |
| 265 | + // For NAREG=64 (16 panes), 15 recursive levels cover all panes |
| 266 | + // except the current one (which is kept active). |
| 267 | + movi a6, 15 |
| 268 | + call4 .Lscan_spill |
| 269 | + |
| 270 | + // Restore interrupts. |
| 271 | + l32i a4, a1, 0 |
| 272 | + wsr.ps a4 |
| 273 | + rsync |
| 274 | + |
| 275 | + // Pass current sp to tinygo_scanstack. |
| 276 | + // call4 maps caller's a5→callee's a1 (stack ptr for callee's entry) |
| 277 | + // and caller's a6→callee's a2 (first argument = sp). |
| 278 | + mov a5, a1 // callee's a1 = valid stack pointer |
| 279 | + mov a6, a1 // callee's a2 = sp argument |
| 280 | + call4 tinygo_scanstack |
| 281 | + |
| 282 | + retw |
| 283 | + |
| 284 | + .balign 4 |
| 285 | +.Lscan_spill: |
| 286 | + entry a1, 16 |
| 287 | + beqz a2, .Lscan_spill_done |
| 288 | + addi a2, a2, -1 |
| 289 | + mov a6, a2 |
| 290 | + call4 .Lscan_spill |
| 291 | +.Lscan_spill_done: |
| 292 | + retw |
0 commit comments