1/* 2 * vr4300.S -- CPU specific support routines 3 * 4 * Copyright (c) 1995,1996 Cygnus Support 5 * 6 * The authors hereby grant permission to use, copy, modify, distribute, 7 * and license this software and its documentation for any purpose, provided 8 * that existing copyright notices are retained in all copies and that this 9 * notice is included verbatim in any distributions. No written agreement, 10 * license, or royalty fee is required for any of the authorized uses. 11 * Modifications to this software may be copyrighted by their authors 12 * and need not follow the licensing terms described here, provided that 13 * the new terms are clearly indicated on the first page of each file where 14 * they apply. 15 */ 16 17#ifndef __mips64 18 .set mips3 19#endif 20#ifdef __mips16 21/* This file contains 32 bit assembly code. */ 22 .set nomips16 23#endif 24 25#include "regs.S" 26 27 .text 28 .align 2 29 30 # Taken from "R4300 Preliminary RISC Processor Specification 31 # Revision 2.0 January 1995" page 39: "The Count 32 # register... increments at a constant rate... at one-half the 33 # PClock speed." 34 # We can use this fact to provide small polled delays. 35 .globl __cpu_timer_poll 36 .ent __cpu_timer_poll 37__cpu_timer_poll: 38 .set noreorder 39 # in: a0 = (unsigned int) number of PClock ticks to wait for 40 # out: void 41 42 # The Vr4300 counter updates at half PClock, so divide by 2 to 43 # get counter delta: 44 bnezl a0, 1f # continue if delta non-zero 45 srl a0, a0, 1 # divide ticks by 2 {DELAY SLOT} 46 # perform a quick return to the caller: 47 j ra 48 nop # {DELAY SLOT} 491: 50 mfc0 v0, $9 # C0_COUNT: get current counter value 51 nop 52 nop 53 # We cannot just do the simple test, of adding our delta onto 54 # the current value (ignoring overflow) and then checking for 55 # equality. The counter is incrementing every two PClocks, 56 # which means the counter value can change between 57 # instructions, making it hard to sample at the exact value 58 # desired. 59 60 # However, we do know that our entry delta value is less than 61 # half the number space (since we divide by 2 on entry). This 62 # means we can use a difference in signs to indicate timer 63 # overflow. 64 addu a0, v0, a0 # unsigned add (ignore overflow) 65 # We know have our end value (which will have been 66 # sign-extended to fill the 64bit register value). 672: 68 # get current counter value: 69 mfc0 v0, $9 # C0_COUNT 70 nop 71 nop 72 # This is an unsigned 32bit subtraction: 73 subu v0, a0, v0 # delta = (end - now) {DELAY SLOT} 74 bgtzl v0, 2b # looping back is most likely 75 nop 76 # We have now been delayed (in the foreground) for AT LEAST 77 # the required number of counter ticks. 78 j ra # return to caller 79 nop # {DELAY SLOT} 80 .set reorder 81 .end __cpu_timer_poll 82 83 # Flush the processor caches to memory: 84 85 .globl __cpu_flush 86 .ent __cpu_flush 87__cpu_flush: 88 .set noreorder 89 # NOTE: The Vr4300 *CANNOT* have any secondary cache (bit 17 90 # of the CONFIG registered is hard-wired to 1). We just 91 # provide code to flush the Data and Instruction caches. 92 93 # Even though the Vr4300 has hard-wired cache and cache line 94 # sizes, we still interpret the relevant Config register 95 # bits. This allows this code to be used for other conforming 96 # MIPS architectures if desired. 97 98 # Get the config register 99 mfc0 a0, C0_CONFIG 100 nop 101 nop 102 li a1, 1 # a useful constant 103 # 104 srl a2, a0, 9 # bits 11..9 for instruction cache size 105 andi a2, a2, 0x7 # 3bits of information 106 add a2, a2, 12 # get full power-of-2 value 107 sllv a2, a1, a2 # instruction cache size 108 # 109 srl a3, a0, 6 # bits 8..6 for data cache size 110 andi a3, a3, 0x7 # 3bits of information 111 add a3, a3, 12 # get full power-of-2 value 112 sllv a3, a1, a3 # data cache size 113 # 114 li a1, (1 << 5) # check IB (instruction cache line size) 115 and a1, a0, a1 # mask against the CONFIG register value 116 beqz a1, 1f # branch on result of delay slot operation 117 nop 118 li a1, 32 # non-zero, then 32bytes 119 j 2f # continue 120 nop 1211: 122 li a1, 16 # 16bytes 1232: 124 # 125 li t0, (1 << 4) # check DB (data cache line size) 126 and a0, a0, t0 # mask against the CONFIG register value 127 beqz a0, 3f # branch on result of delay slot operation 128 nop 129 li a0, 32 # non-zero, then 32bytes 130 j 4f # continue 131 nop 1323: 133 li a0, 16 # 16bytes 1344: 135 # 136 # a0 = data cache line size 137 # a1 = instruction cache line size 138 # a2 = instruction cache size 139 # a3 = data cache size 140 # 141 lui t0, ((K0BASE >> 16) & 0xFFFF) 142 ori t0, t0, (K0BASE & 0xFFFF) 143 addu t1, t0, a2 # end cache address 144 subu t2, a1, 1 # line size mask 145 not t2 # invert the mask 146 and t3, t0, t2 # get start address 147 addu t1, -1 148 and t1, t2 # get end address 1495: 150 cache INDEX_INVALIDATE_I,0(t3) 151 bne t3, t1, 5b 152 addu t3, a1 153 # 154 addu t1, t0, a3 # end cache address 155 subu t2, a0, 1 # line size mask 156 not t2 # invert the mask 157 and t3, t0, t2 # get start address 158 addu t1, -1 159 and t1, t2 # get end address 1606: 161 cache INDEX_WRITEBACK_INVALIDATE_D,0(t3) 162 bne t3, t1, 6b 163 addu t3, a0 164 # 165 j ra # return to the caller 166 nop 167 .set reorder 168 .end __cpu_flush 169 170 # NOTE: This variable should *NOT* be addressed relative to 171 # the $gp register since this code is executed before $gp is 172 # initialised... hence we leave it in the text area. This will 173 # cause problems if this routine is ever ROMmed: 174 175 .globl __buserr_cnt 176__buserr_cnt: 177 .word 0 178 .align 3 179__k1_save: 180 .word 0 181 .word 0 182 .align 2 183 184 .ent __buserr 185 .globl __buserr 186__buserr: 187 .set noat 188 .set noreorder 189 # k0 and k1 available for use: 190 mfc0 k0,C0_CAUSE 191 nop 192 nop 193 andi k0,k0,0x7c 194 sub k0,k0,7 << 2 195 beq k0,$0,__buserr_do 196 nop 197 # call the previous handler 198 la k0,__previous 199 jr k0 200 nop 201 # 202__buserr_do: 203 # TODO: check that the cause is indeed a bus error 204 # - if not then just jump to the previous handler 205 la k0,__k1_save 206 sd k1,0(k0) 207 # 208 la k1,__buserr_cnt 209 lw k0,0(k1) # increment counter 210 addu k0,1 211 sw k0,0(k1) 212 # 213 la k0,__k1_save 214 ld k1,0(k0) 215 # 216 mfc0 k0,C0_EPC 217 nop 218 nop 219 addu k0,k0,4 # skip offending instruction 220 mtc0 k0,C0_EPC # update EPC 221 nop 222 nop 223 eret 224# j k0 225# rfe 226 .set reorder 227 .set at 228 .end __buserr 229 230__exception_code: 231 .set noreorder 232 lui k0,%hi(__buserr) 233 daddiu k0,k0,%lo(__buserr) 234 jr k0 235 nop 236 .set reorder 237__exception_code_end: 238 239 .data 240__previous: 241 .space (__exception_code_end - __exception_code) 242 # This subtracting two addresses is working 243 # but is not garenteed to continue working. 244 # The assemble reserves the right to put these 245 # two labels into different frags, and then 246 # cant take their difference. 247 248 .text 249 250 .ent __default_buserr_handler 251 .globl __default_buserr_handler 252__default_buserr_handler: 253 .set noreorder 254 # attach our simple bus error handler: 255 # in: void 256 # out: void 257 mfc0 a0,C0_SR 258 nop 259 li a1,SR_BEV 260 and a1,a1,a0 261 beq a1,$0,baseaddr 262 lui a0,0x8000 # delay slot 263 lui a0,0xbfc0 264 daddiu a0,a0,0x0200 265baseaddr: 266 daddiu a0,a0,0x0180 267 # a0 = base vector table address 268 la a1,__exception_code_end 269 la a2,__exception_code 270 subu a1,a1,a2 271 la a3,__previous 272 # there must be a better way of doing this???? 273copyloop: 274 lw v0,0(a0) 275 sw v0,0(a3) 276 lw v0,0(a2) 277 sw v0,0(a0) 278 daddiu a0,a0,4 279 daddiu a2,a2,4 280 daddiu a3,a3,4 281 subu a1,a1,4 282 bne a1,$0,copyloop 283 nop 284 la a0,__buserr_cnt 285 sw $0,0(a0) 286 j ra 287 nop 288 .set reorder 289 .end __default_buserr_handler 290 291 .ent __restore_buserr_handler 292 .globl __restore_buserr_handler 293__restore_buserr_handler: 294 .set noreorder 295 # restore original (monitor) bus error handler 296 # in: void 297 # out: void 298 mfc0 a0,C0_SR 299 nop 300 li a1,SR_BEV 301 and a1,a1,a0 302 beq a1,$0,res_baseaddr 303 lui a0,0x8000 # delay slot 304 lui a0,0xbfc0 305 daddiu a0,a0,0x0200 306res_baseaddr: 307 daddiu a0,a0,0x0180 308 # a0 = base vector table address 309 la a1,__exception_code_end 310 la a3,__exception_code 311 subu a1,a1,a3 312 la a3,__previous 313 # there must be a better way of doing this???? 314res_copyloop: 315 lw v0,0(a3) 316 sw v0,0(a0) 317 daddiu a0,a0,4 318 daddiu a3,a3,4 319 subu a1,a1,4 320 bne a1,$0,res_copyloop 321 nop 322 j ra 323 nop 324 .set reorder 325 .end __restore_buserr_handler 326 327 .ent __buserr_count 328 .globl __buserr_count 329__buserr_count: 330 .set noreorder 331 # restore original (monitor) bus error handler 332 # in: void 333 # out: unsigned int __buserr_cnt 334 la v0,__buserr_cnt 335 lw v0,0(v0) 336 j ra 337 nop 338 .set reorder 339 .end __buserr_count 340 341/* EOF vr4300.S */ 342