pbrook | 87ecb68 | 2007-11-17 17:14:51 +0000 | [diff] [blame] | 1 | #include "hw.h" |
| 2 | #include "mips.h" |
| 3 | #include "qemu-timer.h" |
ths | e16fe40 | 2006-12-06 21:38:37 +0000 | [diff] [blame] | 4 | |
aurel32 | ea86e4e | 2008-04-11 04:55:31 +0000 | [diff] [blame] | 5 | #define TIMER_FREQ 100 * 1000 * 1000 |
| 6 | |
ths | e16fe40 | 2006-12-06 21:38:37 +0000 | [diff] [blame] | 7 | /* XXX: do not use a global */ |
| 8 | uint32_t cpu_mips_get_random (CPUState *env) |
| 9 | { |
aurel32 | 59d9413 | 2009-01-08 18:48:12 +0000 | [diff] [blame] | 10 | static uint32_t lfsr = 1; |
| 11 | static uint32_t prev_idx = 0; |
ths | e16fe40 | 2006-12-06 21:38:37 +0000 | [diff] [blame] | 12 | uint32_t idx; |
aurel32 | 59d9413 | 2009-01-08 18:48:12 +0000 | [diff] [blame] | 13 | /* Don't return same value twice, so get another value */ |
| 14 | do { |
| 15 | lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xd0000001u); |
| 16 | idx = lfsr % (env->tlb->nb_tlb - env->CP0_Wired) + env->CP0_Wired; |
| 17 | } while (idx == prev_idx); |
| 18 | prev_idx = idx; |
ths | e16fe40 | 2006-12-06 21:38:37 +0000 | [diff] [blame] | 19 | return idx; |
| 20 | } |
| 21 | |
| 22 | /* MIPS R4K timer */ |
| 23 | uint32_t cpu_mips_get_count (CPUState *env) |
| 24 | { |
ths | 4253218 | 2007-09-25 16:53:15 +0000 | [diff] [blame] | 25 | if (env->CP0_Cause & (1 << CP0Ca_DC)) |
| 26 | return env->CP0_Count; |
| 27 | else |
| 28 | return env->CP0_Count + |
| 29 | (uint32_t)muldiv64(qemu_get_clock(vm_clock), |
aurel32 | ea86e4e | 2008-04-11 04:55:31 +0000 | [diff] [blame] | 30 | TIMER_FREQ, ticks_per_sec); |
| 31 | } |
| 32 | |
| 33 | static void cpu_mips_timer_update(CPUState *env) |
| 34 | { |
| 35 | uint64_t now, next; |
| 36 | uint32_t wait; |
| 37 | |
| 38 | now = qemu_get_clock(vm_clock); |
| 39 | wait = env->CP0_Compare - env->CP0_Count - |
| 40 | (uint32_t)muldiv64(now, TIMER_FREQ, ticks_per_sec); |
| 41 | next = now + muldiv64(wait, ticks_per_sec, TIMER_FREQ); |
| 42 | qemu_mod_timer(env->timer, next); |
ths | e16fe40 | 2006-12-06 21:38:37 +0000 | [diff] [blame] | 43 | } |
| 44 | |
ths | 3529b53 | 2007-04-05 23:17:40 +0000 | [diff] [blame] | 45 | void cpu_mips_store_count (CPUState *env, uint32_t count) |
ths | e16fe40 | 2006-12-06 21:38:37 +0000 | [diff] [blame] | 46 | { |
ths | 3529b53 | 2007-04-05 23:17:40 +0000 | [diff] [blame] | 47 | if (env->CP0_Cause & (1 << CP0Ca_DC)) |
aurel32 | ea86e4e | 2008-04-11 04:55:31 +0000 | [diff] [blame] | 48 | env->CP0_Count = count; |
| 49 | else { |
| 50 | /* Store new count register */ |
| 51 | env->CP0_Count = |
| 52 | count - (uint32_t)muldiv64(qemu_get_clock(vm_clock), |
| 53 | TIMER_FREQ, ticks_per_sec); |
| 54 | /* Update timer timer */ |
| 55 | cpu_mips_timer_update(env); |
| 56 | } |
ths | e16fe40 | 2006-12-06 21:38:37 +0000 | [diff] [blame] | 57 | } |
| 58 | |
| 59 | void cpu_mips_store_compare (CPUState *env, uint32_t value) |
| 60 | { |
ths | 3529b53 | 2007-04-05 23:17:40 +0000 | [diff] [blame] | 61 | env->CP0_Compare = value; |
aurel32 | ea86e4e | 2008-04-11 04:55:31 +0000 | [diff] [blame] | 62 | if (!(env->CP0_Cause & (1 << CP0Ca_DC))) |
| 63 | cpu_mips_timer_update(env); |
| 64 | if (env->insn_flags & ISA_MIPS32R2) |
ths | 39d51eb | 2007-03-18 12:43:40 +0000 | [diff] [blame] | 65 | env->CP0_Cause &= ~(1 << CP0Ca_TI); |
ths | 4253218 | 2007-09-25 16:53:15 +0000 | [diff] [blame] | 66 | qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]); |
| 67 | } |
| 68 | |
| 69 | void cpu_mips_start_count(CPUState *env) |
| 70 | { |
| 71 | cpu_mips_store_count(env, env->CP0_Count); |
| 72 | } |
| 73 | |
| 74 | void cpu_mips_stop_count(CPUState *env) |
| 75 | { |
| 76 | /* Store the current value */ |
| 77 | env->CP0_Count += (uint32_t)muldiv64(qemu_get_clock(vm_clock), |
aurel32 | ea86e4e | 2008-04-11 04:55:31 +0000 | [diff] [blame] | 78 | TIMER_FREQ, ticks_per_sec); |
ths | e16fe40 | 2006-12-06 21:38:37 +0000 | [diff] [blame] | 79 | } |
| 80 | |
| 81 | static void mips_timer_cb (void *opaque) |
| 82 | { |
| 83 | CPUState *env; |
| 84 | |
| 85 | env = opaque; |
| 86 | #if 0 |
aliguori | 93fcfe3 | 2009-01-15 22:34:14 +0000 | [diff] [blame] | 87 | qemu_log("%s\n", __func__); |
ths | e16fe40 | 2006-12-06 21:38:37 +0000 | [diff] [blame] | 88 | #endif |
ths | 4253218 | 2007-09-25 16:53:15 +0000 | [diff] [blame] | 89 | |
| 90 | if (env->CP0_Cause & (1 << CP0Ca_DC)) |
| 91 | return; |
| 92 | |
pbrook | 2e70f6e | 2008-06-29 01:03:05 +0000 | [diff] [blame] | 93 | /* ??? This callback should occur when the counter is exactly equal to |
| 94 | the comparator value. Offset the count by one to avoid immediately |
| 95 | retriggering the callback before any virtual time has passed. */ |
| 96 | env->CP0_Count++; |
aurel32 | ea86e4e | 2008-04-11 04:55:31 +0000 | [diff] [blame] | 97 | cpu_mips_timer_update(env); |
pbrook | 2e70f6e | 2008-06-29 01:03:05 +0000 | [diff] [blame] | 98 | env->CP0_Count--; |
aurel32 | ea86e4e | 2008-04-11 04:55:31 +0000 | [diff] [blame] | 99 | if (env->insn_flags & ISA_MIPS32R2) |
ths | 39d51eb | 2007-03-18 12:43:40 +0000 | [diff] [blame] | 100 | env->CP0_Cause |= 1 << CP0Ca_TI; |
ths | 4253218 | 2007-09-25 16:53:15 +0000 | [diff] [blame] | 101 | qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]); |
ths | e16fe40 | 2006-12-06 21:38:37 +0000 | [diff] [blame] | 102 | } |
| 103 | |
| 104 | void cpu_mips_clock_init (CPUState *env) |
| 105 | { |
| 106 | env->timer = qemu_new_timer(vm_clock, &mips_timer_cb, env); |
| 107 | env->CP0_Compare = 0; |
aurel32 | ea86e4e | 2008-04-11 04:55:31 +0000 | [diff] [blame] | 108 | cpu_mips_store_count(env, 1); |
ths | e16fe40 | 2006-12-06 21:38:37 +0000 | [diff] [blame] | 109 | } |