nkeynes@841 | 1 | /**
|
nkeynes@841 | 2 | * $Id: pmm.c 833 2008-08-18 12:18:10Z nkeynes $
|
nkeynes@841 | 3 | *
|
nkeynes@841 | 4 | * PMM (performance counter) module
|
nkeynes@841 | 5 | *
|
nkeynes@841 | 6 | * Copyright (c) 2005 Nathan Keynes.
|
nkeynes@841 | 7 | *
|
nkeynes@841 | 8 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@841 | 9 | * it under the terms of the GNU General Public License as published by
|
nkeynes@841 | 10 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@841 | 11 | * (at your option) any later version.
|
nkeynes@841 | 12 | *
|
nkeynes@841 | 13 | * This program is distributed in the hope that it will be useful,
|
nkeynes@841 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@841 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@841 | 16 | * GNU General Public License for more details.
|
nkeynes@841 | 17 | */
|
nkeynes@841 | 18 |
|
nkeynes@841 | 19 | #include "sh4/sh4mmio.h"
|
nkeynes@841 | 20 | #include "sh4/sh4core.h"
|
nkeynes@841 | 21 | #include "clock.h"
|
nkeynes@841 | 22 |
|
nkeynes@841 | 23 | /*
|
nkeynes@841 | 24 | * Performance counter list from Paul Mundt's OProfile patch
|
nkeynes@841 | 25 | * Currently only 0x23 is actually supported, since it doesn't require any
|
nkeynes@841 | 26 | * actual instrumentation
|
nkeynes@841 | 27 | *
|
nkeynes@841 | 28 | * 0x01 Operand read access
|
nkeynes@841 | 29 | * 0x02 Operand write access
|
nkeynes@841 | 30 | * 0x03 UTLB miss
|
nkeynes@841 | 31 | * 0x04 Operand cache read miss
|
nkeynes@841 | 32 | * 0x05 Operand cache write miss
|
nkeynes@841 | 33 | * 0x06 Instruction fetch (w/ cache)
|
nkeynes@841 | 34 | * 0x07 Instruction TLB miss
|
nkeynes@841 | 35 | * 0x08 Instruction cache miss
|
nkeynes@841 | 36 | * 0x09 All operand accesses
|
nkeynes@841 | 37 | * 0x0a All instruction accesses
|
nkeynes@841 | 38 | * 0x0b OC RAM operand access
|
nkeynes@841 | 39 | * 0x0d On-chip I/O space access
|
nkeynes@841 | 40 | * 0x0e Operand access (r/w)
|
nkeynes@841 | 41 | * 0x0f Operand cache miss (r/w)
|
nkeynes@841 | 42 | * 0x10 Branch instruction
|
nkeynes@841 | 43 | * 0x11 Branch taken
|
nkeynes@841 | 44 | * 0x12 BSR/BSRF/JSR
|
nkeynes@841 | 45 | * 0x13 Instruction execution
|
nkeynes@841 | 46 | * 0x14 Instruction execution in parallel
|
nkeynes@841 | 47 | * 0x15 FPU Instruction execution
|
nkeynes@841 | 48 | * 0x16 Interrupt
|
nkeynes@841 | 49 | * 0x17 NMI
|
nkeynes@841 | 50 | * 0x18 trapa instruction execution
|
nkeynes@841 | 51 | * 0x19 UBCA match
|
nkeynes@841 | 52 | * 0x1a UBCB match
|
nkeynes@841 | 53 | * 0x21 Instruction cache fill
|
nkeynes@841 | 54 | * 0x22 Operand cache fill
|
nkeynes@841 | 55 | * 0x23 Elapsed time
|
nkeynes@841 | 56 | * 0x24 Pipeline freeze by I-cache miss
|
nkeynes@841 | 57 | * 0x25 Pipeline freeze by D-cache miss
|
nkeynes@841 | 58 | * 0x27 Pipeline freeze by branch instruction
|
nkeynes@841 | 59 | * 0x28 Pipeline freeze by CPU register
|
nkeynes@841 | 60 | * 0x29 Pipeline freeze by FPU
|
nkeynes@841 | 61 | */
|
nkeynes@841 | 62 | struct PMM_counter_struct {
|
nkeynes@841 | 63 | uint64_t count;
|
nkeynes@841 | 64 | uint32_t mode; /* if running only, otherwise 0 */
|
nkeynes@841 | 65 | uint32_t runfor;
|
nkeynes@841 | 66 | };
|
nkeynes@841 | 67 |
|
nkeynes@841 | 68 | static struct PMM_counter_struct PMM_counter[2] = {{0,0},{0,0}};
|
nkeynes@841 | 69 |
|
nkeynes@841 | 70 | void PMM_reset(void)
|
nkeynes@841 | 71 | {
|
nkeynes@841 | 72 | PMM_counter[0].count = 0;
|
nkeynes@841 | 73 | PMM_counter[0].mode = 0;
|
nkeynes@841 | 74 | PMM_counter[0].runfor = 0;
|
nkeynes@841 | 75 | PMM_counter[1].count = 0;
|
nkeynes@841 | 76 | PMM_counter[1].mode = 0;
|
nkeynes@841 | 77 | PMM_counter[1].runfor = 0;
|
nkeynes@841 | 78 | }
|
nkeynes@841 | 79 |
|
nkeynes@841 | 80 | void PMM_save_state( FILE *f ) {
|
nkeynes@841 | 81 | fwrite( &PMM_counter, sizeof(PMM_counter), 1, f );
|
nkeynes@841 | 82 | }
|
nkeynes@841 | 83 |
|
nkeynes@841 | 84 | int PMM_load_state( FILE *f )
|
nkeynes@841 | 85 | {
|
nkeynes@841 | 86 | fread( &PMM_counter, sizeof(PMM_counter), 1, f );
|
nkeynes@841 | 87 | return 0;
|
nkeynes@841 | 88 | }
|
nkeynes@841 | 89 |
|
nkeynes@841 | 90 | void PMM_count( int ctr, uint32_t runfor )
|
nkeynes@841 | 91 | {
|
nkeynes@841 | 92 | uint32_t delta = runfor - PMM_counter[ctr].runfor;
|
nkeynes@841 | 93 |
|
nkeynes@841 | 94 | switch( PMM_counter[ctr].mode ) {
|
nkeynes@841 | 95 | case 0x23:
|
nkeynes@841 | 96 | PMM_counter[ctr].count += (delta / (1000/SH4_BASE_RATE));
|
nkeynes@841 | 97 | break;
|
nkeynes@841 | 98 | default:
|
nkeynes@841 | 99 | break;
|
nkeynes@841 | 100 | }
|
nkeynes@841 | 101 |
|
nkeynes@841 | 102 | PMM_counter[ctr].runfor = runfor;
|
nkeynes@841 | 103 | }
|
nkeynes@841 | 104 |
|
nkeynes@841 | 105 | uint32_t PMM_run_slice( uint32_t nanosecs )
|
nkeynes@841 | 106 | {
|
nkeynes@841 | 107 | PMM_count( 0, nanosecs );
|
nkeynes@841 | 108 | PMM_count( 1, nanosecs );
|
nkeynes@841 | 109 | PMM_counter[0].runfor = 0;
|
nkeynes@841 | 110 | PMM_counter[1].runfor = 0;
|
nkeynes@841 | 111 | return nanosecs;
|
nkeynes@841 | 112 | }
|
nkeynes@841 | 113 |
|
nkeynes@841 | 114 | void PMM_write_control( int ctr, uint32_t val )
|
nkeynes@841 | 115 | {
|
nkeynes@841 | 116 | int is_running = ((val & PMCR_RUNNING) == PMCR_RUNNING);
|
nkeynes@841 | 117 |
|
nkeynes@841 | 118 | PMM_count(ctr, sh4r.slice_cycle);
|
nkeynes@841 | 119 | if( PMM_counter[ctr].mode == 0 && (val & PMCR_PMCLR) != 0 ) {
|
nkeynes@841 | 120 | PMM_counter[ctr].count = 0;
|
nkeynes@841 | 121 | }
|
nkeynes@841 | 122 | if( is_running ) {
|
nkeynes@841 | 123 | int mode = val & 0x3F;
|
nkeynes@841 | 124 | if( mode != PMM_counter[ctr].mode ) {
|
nkeynes@841 | 125 | /* Instrumentation setup goes here */
|
nkeynes@841 | 126 | PMM_counter[ctr].mode = mode;
|
nkeynes@841 | 127 | }
|
nkeynes@841 | 128 | } else if( PMM_counter[ctr].mode != 0 ) {
|
nkeynes@841 | 129 | /* Instrumentation removal goes here */
|
nkeynes@841 | 130 | PMM_counter[ctr].mode = 0;
|
nkeynes@841 | 131 | }
|
nkeynes@841 | 132 | }
|
nkeynes@841 | 133 |
|
nkeynes@841 | 134 | int32_t mmio_region_PMM_read( uint32_t reg )
|
nkeynes@841 | 135 | {
|
nkeynes@841 | 136 | switch( reg & 0x1F ) {
|
nkeynes@841 | 137 | case 0: return 0; /* not a register */
|
nkeynes@841 | 138 | case PMCTR1H:
|
nkeynes@841 | 139 | PMM_count(0, sh4r.slice_cycle);
|
nkeynes@841 | 140 | return ((uint32_t)(PMM_counter[0].count >> 32)) & 0x0000FFFF;
|
nkeynes@841 | 141 | case PMCTR1L:
|
nkeynes@841 | 142 | PMM_count(0, sh4r.slice_cycle);
|
nkeynes@841 | 143 | return (uint32_t)PMM_counter[0].count;
|
nkeynes@841 | 144 | case PMCTR2H:
|
nkeynes@841 | 145 | PMM_count(1, sh4r.slice_cycle);
|
nkeynes@841 | 146 | return ((uint32_t)(PMM_counter[1].count >> 32)) & 0x0000FFFF;
|
nkeynes@841 | 147 | default:
|
nkeynes@841 | 148 | PMM_count(1, sh4r.slice_cycle);
|
nkeynes@841 | 149 | return (uint32_t)PMM_counter[1].count;
|
nkeynes@841 | 150 | }
|
nkeynes@841 | 151 | }
|
nkeynes@841 | 152 |
|
nkeynes@841 | 153 | void mmio_region_PMM_write( uint32_t reg, uint32_t val )
|
nkeynes@841 | 154 | {
|
nkeynes@841 | 155 | /* Read-only */
|
nkeynes@841 | 156 | }
|