# HG changeset patch # User nkeynes # Date 1220356396 0 # Node ID 808d64b05073569496eddc43a09f3a84c92f9886 # Parent c6a778c228a60d308c2953e15745fc7026ffa617 Initial implementation of the performance counters, only working one for now is raw clock cycles (0x23) --- a/src/sh4/mmu.c Tue Sep 02 03:34:23 2008 +0000 +++ b/src/sh4/mmu.c Tue Sep 02 11:53:16 2008 +0000 @@ -192,10 +192,12 @@ val &= 0x0000001C; break; case PMCR1: + PMM_write_control(0, val); + val &= 0x0000C13F; + break; case PMCR2: - if( val != 0 ) { - WARN( "Performance counters not implemented" ); - } + PMM_write_control(1, val); + val &= 0x0000C13F; break; default: break; @@ -964,19 +966,3 @@ return TRUE; } -/********************************* PMM *************************************/ - -/** - * Side note - this is here (rather than in sh4mmio.c) as the control registers - * are part of the MMU block, and it seems simplest to keep it all together. - */ - -int32_t mmio_region_PMM_read( uint32_t reg ) -{ - return MMIO_READ( PMM, reg ); -} - -void mmio_region_PMM_write( uint32_t reg, uint32_t val ) -{ - /* Read-only */ -} --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sh4/pmm.c Tue Sep 02 11:53:16 2008 +0000 @@ -0,0 +1,156 @@ +/** + * $Id: pmm.c 833 2008-08-18 12:18:10Z nkeynes $ + * + * PMM (performance counter) module + * + * Copyright (c) 2005 Nathan Keynes. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "sh4/sh4mmio.h" +#include "sh4/sh4core.h" +#include "clock.h" + +/* + * Performance counter list from Paul Mundt's OProfile patch + * Currently only 0x23 is actually supported, since it doesn't require any + * actual instrumentation + * + * 0x01 Operand read access + * 0x02 Operand write access + * 0x03 UTLB miss + * 0x04 Operand cache read miss + * 0x05 Operand cache write miss + * 0x06 Instruction fetch (w/ cache) + * 0x07 Instruction TLB miss + * 0x08 Instruction cache miss + * 0x09 All operand accesses + * 0x0a All instruction accesses + * 0x0b OC RAM operand access + * 0x0d On-chip I/O space access + * 0x0e Operand access (r/w) + * 0x0f Operand cache miss (r/w) + * 0x10 Branch instruction + * 0x11 Branch taken + * 0x12 BSR/BSRF/JSR + * 0x13 Instruction execution + * 0x14 Instruction execution in parallel + * 0x15 FPU Instruction execution + * 0x16 Interrupt + * 0x17 NMI + * 0x18 trapa instruction execution + * 0x19 UBCA match + * 0x1a UBCB match + * 0x21 Instruction cache fill + * 0x22 Operand cache fill + * 0x23 Elapsed time + * 0x24 Pipeline freeze by I-cache miss + * 0x25 Pipeline freeze by D-cache miss + * 0x27 Pipeline freeze by branch instruction + * 0x28 Pipeline freeze by CPU register + * 0x29 Pipeline freeze by FPU + */ +struct PMM_counter_struct { + uint64_t count; + uint32_t mode; /* if running only, otherwise 0 */ + uint32_t runfor; +}; + +static struct PMM_counter_struct PMM_counter[2] = {{0,0},{0,0}}; + +void PMM_reset(void) +{ + PMM_counter[0].count = 0; + PMM_counter[0].mode = 0; + PMM_counter[0].runfor = 0; + PMM_counter[1].count = 0; + PMM_counter[1].mode = 0; + PMM_counter[1].runfor = 0; +} + +void PMM_save_state( FILE *f ) { + fwrite( &PMM_counter, sizeof(PMM_counter), 1, f ); +} + +int PMM_load_state( FILE *f ) +{ + fread( &PMM_counter, sizeof(PMM_counter), 1, f ); + return 0; +} + +void PMM_count( int ctr, uint32_t runfor ) +{ + uint32_t delta = runfor - PMM_counter[ctr].runfor; + + switch( PMM_counter[ctr].mode ) { + case 0x23: + PMM_counter[ctr].count += (delta / (1000/SH4_BASE_RATE)); + break; + default: + break; + } + + PMM_counter[ctr].runfor = runfor; +} + +uint32_t PMM_run_slice( uint32_t nanosecs ) +{ + PMM_count( 0, nanosecs ); + PMM_count( 1, nanosecs ); + PMM_counter[0].runfor = 0; + PMM_counter[1].runfor = 0; + return nanosecs; +} + +void PMM_write_control( int ctr, uint32_t val ) +{ + int is_running = ((val & PMCR_RUNNING) == PMCR_RUNNING); + + PMM_count(ctr, sh4r.slice_cycle); + if( PMM_counter[ctr].mode == 0 && (val & PMCR_PMCLR) != 0 ) { + PMM_counter[ctr].count = 0; + } + if( is_running ) { + int mode = val & 0x3F; + if( mode != PMM_counter[ctr].mode ) { + /* Instrumentation setup goes here */ + PMM_counter[ctr].mode = mode; + } + } else if( PMM_counter[ctr].mode != 0 ) { + /* Instrumentation removal goes here */ + PMM_counter[ctr].mode = 0; + } +} + +int32_t mmio_region_PMM_read( uint32_t reg ) +{ + switch( reg & 0x1F ) { + case 0: return 0; /* not a register */ + case PMCTR1H: + PMM_count(0, sh4r.slice_cycle); + return ((uint32_t)(PMM_counter[0].count >> 32)) & 0x0000FFFF; + case PMCTR1L: + PMM_count(0, sh4r.slice_cycle); + return (uint32_t)PMM_counter[0].count; + case PMCTR2H: + PMM_count(1, sh4r.slice_cycle); + return ((uint32_t)(PMM_counter[1].count >> 32)) & 0x0000FFFF; + default: + PMM_count(1, sh4r.slice_cycle); + return (uint32_t)PMM_counter[1].count; + } +} + +void mmio_region_PMM_write( uint32_t reg, uint32_t val ) +{ + /* Read-only */ +} --- a/src/sh4/sh4.c Tue Sep 02 03:34:23 2008 +0000 +++ b/src/sh4/sh4.c Tue Sep 02 11:53:16 2008 +0000 @@ -118,6 +118,7 @@ CPG_reset(); INTC_reset(); MMU_reset(); + PMM_reset(); TMU_reset(); SCIF_reset(); @@ -156,6 +157,7 @@ if( sh4r.sh4_state != SH4_STATE_STANDBY ) { TMU_run_slice( sh4r.slice_cycle ); SCIF_run_slice( sh4r.slice_cycle ); + PMM_run_slice( sh4r.slice_cycle ); dreamcast_stop(); return sh4r.slice_cycle; } @@ -193,6 +195,7 @@ if( sh4r.sh4_state != SH4_STATE_STANDBY ) { TMU_run_slice( nanosecs ); SCIF_run_slice( nanosecs ); + PMM_run_slice( sh4r.slice_cycle ); } return nanosecs; } @@ -233,6 +236,7 @@ fwrite( &sh4r, sizeof(sh4r), 1, f ); MMU_save_state( f ); + PMM_save_state( f ); INTC_save_state( f ); TMU_save_state( f ); SCIF_save_state( f ); @@ -245,6 +249,7 @@ } fread( &sh4r, sizeof(sh4r), 1, f ); MMU_load_state( f ); + PMM_load_state( f ); INTC_load_state( f ); TMU_load_state( f ); return SCIF_load_state( f ); @@ -458,6 +463,7 @@ /* Bring all running peripheral modules up to date, and then halt them. */ TMU_run_slice( sh4r.slice_cycle ); SCIF_run_slice( sh4r.slice_cycle ); + PMM_run_slice( sh4r.slice_cycle ); } else { if( MMIO_READ( CPG, STBCR2 ) & 0x80 ) { sh4r.sh4_state = SH4_STATE_DEEP_SLEEP; --- a/src/sh4/sh4core.h Tue Sep 02 03:34:23 2008 +0000 +++ b/src/sh4/sh4core.h Tue Sep 02 11:53:16 2008 +0000 @@ -161,6 +161,11 @@ void TMU_save_state( FILE * ); int TMU_load_state( FILE * ); void TMU_update_clocks( void ); +void PMM_reset( void ); +void PMM_write_control( int, uint32_t ); +void PMM_save_state( FILE * ); +int PMM_load_state( FILE * ); +uint32_t PMM_run_slice( uint32_t ); uint32_t sh4_translate_run_slice(uint32_t); uint32_t sh4_emulate_run_slice(uint32_t); --- a/src/sh4/sh4mmio.h Tue Sep 02 03:34:23 2008 +0000 +++ b/src/sh4/sh4mmio.h Tue Sep 02 11:53:16 2008 +0000 @@ -229,6 +229,7 @@ #define PMCR_PMCLR 0x2000 #define PMCR_PMST 0x4000 #define PMCR_PMEN 0x8000 +#define PMCR_RUNNING (PMCR_PMST|PMCR_PMEN) /* MMU functions */ void mmu_init(void);