1.1 --- a/src/sh4/timer.c Sun Dec 25 05:57:00 2005 +0000
1.2 +++ b/src/sh4/timer.c Thu Dec 29 12:52:29 2005 +0000
1.5 - * $Id: timer.c,v 1.2 2005-12-25 05:57:00 nkeynes Exp $
1.6 + * $Id: timer.c,v 1.3 2005-12-29 12:52:29 nkeynes Exp $
1.8 * SH4 Timer/Clock peripheral modules (CPG, TMU, RTC), combined together to
1.9 * keep things simple (they intertwine a bit).
1.12 #include "sh4core.h"
1.13 #include "sh4mmio.h"
1.16 /********************************* CPG *************************************/
1.17 +/* This is the base clock from which all other clocks are derived */
1.18 +uint32_t sh4_input_freq = SH4_BASE_RATE;
1.20 +uint32_t sh4_cpu_freq = SH4_BASE_RATE;
1.21 +uint32_t sh4_bus_freq = SH4_BASE_RATE;
1.22 +uint32_t sh4_peripheral_freq = SH4_BASE_RATE / 2;
1.24 +uint32_t sh4_cpu_period = 1000 / SH4_BASE_RATE; /* in nanoseconds */
1.25 +uint32_t sh4_bus_period = 1000 / SH4_BASE_RATE;
1.26 +uint32_t sh4_peripheral_period = 2000 / SH4_BASE_RATE;
1.28 int32_t mmio_region_CPG_read( uint32_t reg )
1.30 return MMIO_READ( CPG, reg );
1.33 +/* CPU + bus dividers (note officially only the first 6 values are valid) */
1.34 +int ifc_divider[8] = { 1, 2, 3, 4, 5, 8, 8, 8 };
1.35 +/* Peripheral clock dividers (only first 5 are officially valid) */
1.36 +int pfc_divider[8] = { 2, 3, 4, 6, 8, 8, 8, 8 };
1.38 void mmio_region_CPG_write( uint32_t reg, uint32_t val )
1.42 + case FRQCR: /* Frequency control */
1.43 + div = ifc_divider[(val >> 6) & 0x07];
1.44 + sh4_cpu_freq = sh4_input_freq / div;
1.45 + sh4_cpu_period = 1000 * div / sh4_input_freq;
1.46 + div = ifc_divider[(val >> 3) & 0x07];
1.47 + sh4_bus_freq = sh4_input_freq / div;
1.48 + sh4_bus_period = 1000 * div / sh4_input_freq;
1.49 + div = pfc_divider[val & 0x07];
1.50 + sh4_peripheral_freq = sh4_input_freq / div;
1.51 + sh4_peripheral_period = 1000 * div / sh4_input_freq;
1.53 + /* Update everything that depends on the peripheral frequency */
1.54 + SCIF_update_line_speed();
1.56 + case WTCSR: /* Watchdog timer */
1.60 MMIO_WRITE( CPG, reg, val );
1.63 /********************************** RTC *************************************/
1.65 +uint32_t rtc_output_period;
1.67 int32_t mmio_region_RTC_read( uint32_t reg )
1.69 return MMIO_READ( RTC, reg );
1.70 @@ -49,38 +87,120 @@
1.72 /********************************** TMU *************************************/
1.74 -int timer_divider[3] = {16,16,16};
1.75 +#define TCR_ICPF 0x0200
1.76 +#define TCR_UNF 0x0100
1.77 +#define TCR_UNIE 0x0020
1.80 + uint32_t timer_period;
1.81 + uint32_t timer_remainder; /* left-over cycles from last count */
1.82 + uint32_t timer_run; /* cycles already run from this slice */
1.85 +struct TMU_timer TMU_timers[3];
1.87 int32_t mmio_region_TMU_read( uint32_t reg )
1.89 return MMIO_READ( TMU, reg );
1.92 +void TMU_set_timer_period( int timer, int tcr )
1.94 + uint32_t period = 1;
1.95 + switch( tcr & 0x07 ) {
1.97 + period = sh4_peripheral_period << 2 ;
1.100 + period = sh4_peripheral_period << 4;
1.103 + period = sh4_peripheral_period << 6;
1.106 + period = sh4_peripheral_period << 8;
1.109 + period = sh4_peripheral_period << 10;
1.112 + /* Illegal value. */
1.113 + ERROR( "TMU %d period set to illegal value (5)", timer );
1.114 + period = sh4_peripheral_period << 12; /* for something to do */
1.117 + period = rtc_output_period;
1.120 + /* External clock... Hrm? */
1.121 + period = sh4_peripheral_period; /* I dunno... */
1.124 + TMU_timers[timer].timer_period = period;
1.127 -int get_timer_div( int val )
1.128 +void TMU_start( int timer )
1.130 - switch( val & 0x07 ) {
1.131 - case 0: return 16; /* assume peripheral clock is IC/4 */
1.132 - case 1: return 64;
1.133 - case 2: return 256;
1.134 - case 3: return 1024;
1.135 - case 4: return 4096;
1.136 + TMU_timers[timer].timer_run = 0;
1.137 + TMU_timers[timer].timer_remainder = 0;
1.140 +void TMU_stop( int timer )
1.146 + * Count the specified timer for a given number of nanoseconds.
1.148 +uint32_t TMU_count( int timer, uint32_t nanosecs )
1.150 + nanosecs = nanosecs + TMU_timers[timer].timer_remainder -
1.151 + TMU_timers[timer].timer_run;
1.152 + TMU_timers[timer].timer_remainder =
1.153 + nanosecs % TMU_timers[timer].timer_period;
1.154 + uint32_t count = nanosecs / TMU_timers[timer].timer_period;
1.155 + uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
1.156 + uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
1.157 + if( count > value ) {
1.158 + uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
1.161 + value = reset - (count % reset);
1.162 + MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
1.163 + if( tcr & TCR_UNIE )
1.164 + intc_raise_interrupt( INT_TMU_TUNI0 + timer );
1.169 + MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
1.173 void mmio_region_TMU_write( uint32_t reg, uint32_t val )
1.179 - timer_divider[0] = get_timer_div(val);
1.182 - timer_divider[1] = get_timer_div(val);
1.185 - timer_divider[2] = get_timer_div(val);
1.188 + oldval = MMIO_READ( TMU, TSTR );
1.189 + for( i=0; i<3; i++ ) {
1.190 + uint32_t tmp = 1<<i;
1.191 + if( (oldval & tmp) == 1 && (val&tmp) == 0 )
1.193 + else if( (oldval&tmp) == 0 && (val&tmp) == 1 )
1.198 + TMU_set_timer_period( 0, val );
1.201 + TMU_set_timer_period( 1, val );
1.204 + TMU_set_timer_period( 2, val );
1.207 MMIO_WRITE( TMU, reg, val );
1.209 @@ -88,41 +208,44 @@
1.210 void TMU_run_slice( uint32_t nanosecs )
1.212 int tcr = MMIO_READ( TMU, TSTR );
1.213 - int cycles = nanosecs / sh4_peripheral_period;
1.215 - int count = cycles / timer_divider[0];
1.216 - int *val = MMIO_REG( TMU, TCNT0 );
1.217 - if( *val < count ) {
1.218 - MMIO_READ( TMU, TCR0 ) |= 0x100;
1.219 - /* interrupt goes here */
1.221 - *val = MMIO_READ( TMU, TCOR0 ) - count;
1.225 + TMU_count( 0, nanosecs );
1.226 + TMU_timers[0].timer_run = 0;
1.229 - int count = cycles / timer_divider[1];
1.230 - int *val = MMIO_REG( TMU, TCNT1 );
1.231 - if( *val < count ) {
1.232 - MMIO_READ( TMU, TCR1 ) |= 0x100;
1.233 - /* interrupt goes here */
1.235 - *val = MMIO_READ( TMU, TCOR1 ) - count;
1.239 + TMU_count( 1, nanosecs );
1.240 + TMU_timers[1].timer_run = 0;
1.243 - int count = cycles / timer_divider[2];
1.244 - int *val = MMIO_REG( TMU, TCNT2 );
1.245 - if( *val < count ) {
1.246 - MMIO_READ( TMU, TCR2 ) |= 0x100;
1.247 - /* interrupt goes here */
1.249 - *val = MMIO_READ( TMU, TCOR2 ) - count;
1.253 + TMU_count( 2, nanosecs );
1.254 + TMU_timers[2].timer_run = 0;
1.258 +void TMU_update_clocks()
1.260 + TMU_set_timer_period( 0, MMIO_READ( TMU, TCR0 ) );
1.261 + TMU_set_timer_period( 1, MMIO_READ( TMU, TCR1 ) );
1.262 + TMU_set_timer_period( 2, MMIO_READ( TMU, TCR2 ) );
1.267 + TMU_timers[0].timer_remainder = 0;
1.268 + TMU_timers[0].timer_run = 0;
1.269 + TMU_timers[1].timer_remainder = 0;
1.270 + TMU_timers[1].timer_run = 0;
1.271 + TMU_timers[2].timer_remainder = 0;
1.272 + TMU_timers[2].timer_run = 0;
1.273 + TMU_update_clocks();
1.276 +void TMU_save_state( FILE *f ) {
1.277 + fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
1.280 +int TMU_load_state( FILE *f )
1.282 + fread( &TMU_timers, sizeof(TMU_timers), 1, f );