GLdc/GL/perfctr.c
Hayden Kowalchuk 3a4f09bef2 feat: implement proper chanegs from profiling
- math
- inlining
2020-02-18 11:48:37 -05:00

248 lines
8.3 KiB
C

// ---- perfctr.c - SH7091 Performance Counter Module Code ----
//
// This file is part of the DreamHAL project, a hardware abstraction library
// primarily intended for use on the SH7091 found in hardware such as the SEGA
// Dreamcast game console.
//
// The performance counter module is hereby released into the public domain in
// the hope that it may prove useful. Now go profile some code and hit 60 fps! :)
//
// --Moopthehedgehog
// See perfctr.h for more of my notes and documentation on these counters.
#include "perfctr.h"
#include "cygprofile.h"
#if CYG_FUNC_TRACE_ENABLED
static unsigned char pmcr_enabled = 0;
//
// Initialize performance counters. It's just a clear -> enable.
// It's good practice to clear a counter before starting it for the first time.
//
// Also: Disabling and re-enabling the counters doesn't reset them; the clearing
// needs to happen while a counter is disabled to reset it.
//
// You can disable and re-enable with a different mode without explicitly
// clearing and have it keep going, continuing from where it left off.
//
__attribute__((no_instrument_function)) void PMCR_Init(int which, unsigned short mode, unsigned char count_type) // Will do nothing if perfcounter is already running!
{
// Don't do anything if being asked to enable an already-enabled counter
if( (which == 1) && ((!pmcr_enabled) || (pmcr_enabled == 2)) )
{
// counter 1
PMCR_Enable(1, mode, count_type, PMCR_RESET_COUNTER);
}
else if( (which == 2) && ((!pmcr_enabled) || (pmcr_enabled == 1)) )
{
// counter 2
PMCR_Enable(2, mode, count_type, PMCR_RESET_COUNTER);
}
else if( (which == 3) && (!pmcr_enabled) )
{
// Both
PMCR_Enable(3, mode, count_type, PMCR_RESET_COUNTER);
}
}
// Enable "undocumented" performance counters (well, they were undocumented at one point. They're documented now!)
__attribute__((no_instrument_function)) void PMCR_Enable(int which, unsigned short mode, unsigned char count_type, unsigned char reset_count) // Will do nothing if perfcounter is already running!
{
// Don't do anything if count_type or reset_count are invalid
if((count_type | reset_count) > 1)
{
return;
}
// Build config from parameters
unsigned short pmcr_ctrl = PMCR_RUN_COUNTER | (reset_count << PMCR_RESET_COUNTER_SHIFT) | (count_type << PMCR_CLOCK_TYPE_SHIFT) | mode;
// Don't do anything if being asked to enable an already-enabled counter
if( (which == 1) && ((!pmcr_enabled) || (pmcr_enabled == 2)) )
{
// counter 1
*((volatile unsigned short*)PMCR1_CTRL_REG) = pmcr_ctrl;
pmcr_enabled += 1;
}
else if( (which == 2) && ((!pmcr_enabled) || (pmcr_enabled == 1)) )
{
// counter 2
*((volatile unsigned short*)PMCR2_CTRL_REG) = pmcr_ctrl;
pmcr_enabled += 2;
}
else if( (which == 3) && (!pmcr_enabled) )
{
// Both
*((volatile unsigned short*)PMCR1_CTRL_REG) = pmcr_ctrl;
*((volatile unsigned short*)PMCR2_CTRL_REG) = pmcr_ctrl;
pmcr_enabled = 3;
}
}
// For reference:
// #define PMCTR1H_REG 0xFF100004
// #define PMCTR1L_REG 0xFF100008
// #define PMCTR2H_REG 0xFF10000C
// #define PMCTR2L_REG 0xFF100010
static const unsigned int pmcr1_regh = PMCTR1H_REG;
static const unsigned int pmcr1_regl = PMCTR1L_REG;
static const unsigned int pmcr2_regh = PMCTR2H_REG;
static const unsigned int pmcr2_regl = PMCTR2L_REG;
// Sorry, can only read one counter at a time!
// out_array should be an array consisting of 2x unsigned ints.
__attribute__((no_instrument_function)) void PMCR_Read(int which, volatile unsigned int *out_array)
{
// if pmcr is not enabled, this function will just return 0
// little endian (big endian would need to flip [0] and [1])
// Note: These reads really do need to be done in assembly: unfortunately it
// appears that using C causes GCC to insert a branch right smack in between
// the high and low reads of perf counter 2 (with a nop, so it's literally
// delaying the reads by several cycles!), which is totally insane. Doing it
// the assembly way ensures that nothing ridiculous like that happens. It's
// also portable between versions of GCC that do put the nonsensical branch in.
//
// One thing that would be nice is if SH4 had the movi20s instruction to make
// absolute addresses in 3 cycles, but only the SH2A has that... :(
if( (which == 1) && (pmcr_enabled & 0x1) )
{
// counter 1
// out_array[1] = *((volatile unsigned int*)PMCTR1H_REG) & 0xffff;
// out_array[0] = *((volatile unsigned int*)PMCTR1L_REG);
asm volatile("mov.l %[reg1h],r1\n\t" // load counter address (high)
"mov.l %[reg1l],r2\n\t" // load counter address (low)
"mov.l @r1,r1\n\t" // read counter (high)
"mov.l @r2,r2\n\t" // read counter (low)
"extu.w r1,r1\n\t" // zero-extend high, aka high & 0xffff
"mov.l r1,%[outh]\n\t" // get data to memory
"mov.l r2,%[outl]\n\t" // get data to memory
: [outh] "=m" (out_array[1]), [outl] "=m" (out_array[0])
: [reg1h] "m" (pmcr1_regh), [reg1l] "m" (pmcr1_regl) // SH4 can't mov an immediate longword into a register...
: "r1", "r2"
);
}
else if( (which == 2) && (pmcr_enabled & 0x2) )
{
// counter 2
// out_array[1] = *((volatile unsigned int*)PMCTR2H_REG) & 0xffff;
// out_array[0] = *((volatile unsigned int*)PMCTR2L_REG);
asm volatile("mov.l %[reg2h],r1\n\t" // load counter address (high)
"mov.l %[reg2l],r2\n\t" // load counter address (low)
"mov.l @r1,r1\n\t" // read counter (high)
"mov.l @r2,r2\n\t" // read counter (low)
"extu.w r1,r1\n\t" // zero-extend high, aka high & 0xffff
"mov.l r1,%[outh]\n\t" // get data to memory
"mov.l r2,%[outl]\n\t" // get data to memory
: [outh] "=m" (out_array[1]), [outl] "=m" (out_array[0])
: [reg2h] "m" (pmcr2_regh), [reg2l] "m" (pmcr2_regl) // SH4 can't mov an immediate longword into a register...
: "r1", "r2"
);
}
else if(!pmcr_enabled)
{
out_array[1] = 0;
out_array[0] = 0;
}
else // Invalid
{
out_array[1] = 0xffff;
out_array[0] = 0xffffffff;
}
}
// Reset counter to 0 and start it again
// NOTE: It does not appear to be possible to clear a counter while it is running.
__attribute__((no_instrument_function)) void PMCR_Restart(int which, unsigned short mode, unsigned char count_type)
{
if( (which == 1) && (pmcr_enabled & 0x1) )
{
// counter 1
PMCR_Stop(1);
PMCR_Enable(1, mode, count_type, PMCR_RESET_COUNTER);
}
else if( (which == 2) && (pmcr_enabled & 0x2) )
{
// counter 2
PMCR_Stop(2);
PMCR_Enable(2, mode, count_type, PMCR_RESET_COUNTER);
}
else if( (which == 3) && (pmcr_enabled == 3) )
{
// Both
PMCR_Stop(3);
PMCR_Enable(3, mode, count_type, PMCR_RESET_COUNTER);
}
}
// Clearing only works when the counter is disabled. Otherwise, stopping the
// counter via setting the 0x2000 bit holds the data in the data registers,
// whereas disabling without setting that bit reads back as all 0 (but doesn't
// clear the counters for next start). This function just stops a running
// counter and does nothing if the counter is already stopped or disabled, as
// clearing is handled by PMCR_Enable().
__attribute__((no_instrument_function)) void PMCR_Stop(int which)
{
if( (which == 1) && (pmcr_enabled & 0x1) )
{
// counter 1
*((volatile unsigned short*)PMCR1_CTRL_REG) = PMCR_STOP_COUNTER;
pmcr_enabled &= 0x2;
}
else if( (which == 2) && (pmcr_enabled & 0x2) )
{
// counter 2
*((volatile unsigned short*)PMCR2_CTRL_REG) = PMCR_STOP_COUNTER;
pmcr_enabled &= 0x1;
}
else if( (which == 3) && (pmcr_enabled == 3) )
{
// Both
*((volatile unsigned short*)PMCR1_CTRL_REG) = PMCR_STOP_COUNTER;
*((volatile unsigned short*)PMCR2_CTRL_REG) = PMCR_STOP_COUNTER;
pmcr_enabled = 0;
}
}
// Note that disabling does NOT clear the counter.
// It may appear that way because reading a disabled counter returns 0, but re-
// enabling without first clearing will simply continue where it left off.
__attribute__((no_instrument_function)) void PMCR_Disable(int which)
{
if(which == 1)
{
// counter 1
*((volatile unsigned short*)PMCR1_CTRL_REG) = PMCR_DISABLE_COUNTER;
pmcr_enabled &= 0x2;
}
else if(which == 2)
{
// counter 2
*((volatile unsigned short*)PMCR2_CTRL_REG) = PMCR_DISABLE_COUNTER;
pmcr_enabled &= 0x1;
}
else if(which == 3)
{
// Both
*((volatile unsigned short*)PMCR1_CTRL_REG) = PMCR_DISABLE_COUNTER;
*((volatile unsigned short*)PMCR2_CTRL_REG) = PMCR_DISABLE_COUNTER;
pmcr_enabled = 0;
}
}
#endif