revision 53:f2981805b929
summary |
tree |
shortlog |
changelog |
graph |
changeset |
raw | bz2 | zip | gz changeset | 53:f2981805b929 |
parent | 52:429b7fc6b843 |
child | 54:d8b73031289c |
author | nkeynes |
date | Thu Dec 29 12:52:29 2005 +0000 (15 years ago) |
Rewrite timer to be more accurate, also support cycle precision in the future
![]() | src/sh4/sh4core.c | view | annotate | diff | log | |
![]() | src/sh4/sh4core.h | view | annotate | diff | log | |
![]() | src/sh4/timer.c | view | annotate | diff | log |
1.1 --- a/src/sh4/sh4core.c Wed Dec 28 22:50:08 2005 +00001.2 +++ b/src/sh4/sh4core.c Thu Dec 29 12:52:29 2005 +00001.3 @@ -1,5 +1,5 @@1.4 /**1.5 - * $Id: sh4core.c,v 1.16 2005-12-26 11:47:15 nkeynes Exp $1.6 + * $Id: sh4core.c,v 1.17 2005-12-29 12:52:29 nkeynes Exp $1.7 *1.8 * SH4 emulation core, and parent module for all the SH4 peripheral1.9 * modules.1.10 @@ -37,14 +37,6 @@1.11 #define EXC_FPDISABLE 0x8001.12 #define EXV_FPDISABLE 0x1001.14 -uint32_t sh4_freq = SH4_BASE_RATE;1.15 -uint32_t sh4_bus_freq = SH4_BASE_RATE;1.16 -uint32_t sh4_peripheral_freq = SH4_BASE_RATE / 2;1.17 -1.18 -uint32_t sh4_cpu_period = 1000 / SH4_BASE_RATE; /* in nanoseconds */1.19 -uint32_t sh4_bus_period = 1000 / SH4_BASE_RATE;1.20 -uint32_t sh4_peripheral_period = 2000 / SH4_BASE_RATE;1.21 -1.22 /********************** SH4 Module Definition ****************************/1.24 void sh4_init( void );1.25 @@ -139,8 +131,7 @@1.26 sh4r.sh4_state = SH4_STATE_RUNNING;;1.27 }1.29 - while( sh4r.icount < target && sh4r.sh4_state == SH4_STATE_RUNNING ) {1.30 - sh4r.icount++;1.31 + for( sh4r.slice_cycle = 0; sh4r.slice_cycle < nanosecs; sh4r.slice_cycle += sh4_cpu_period ) {1.32 if( !sh4_execute_instruction() )1.33 break;1.34 #ifdef ENABLE_DEBUG_MODE1.35 @@ -162,14 +153,14 @@1.36 * we're doing a hard abort - cut the timeslice back to what we1.37 * actually executed1.38 */1.39 - if( target != sh4r.icount && sh4r.sh4_state == SH4_STATE_RUNNING ) {1.40 - /* Halted - compute time actually executed */1.41 - nanosecs = (sh4r.icount - start) * sh4_cpu_period;1.42 + if( sh4r.slice_cycle != nanosecs && sh4r.sh4_state == SH4_STATE_RUNNING ) {1.43 + nanosecs = sh4r.slice_cycle;1.44 }1.45 if( sh4r.sh4_state != SH4_STATE_STANDBY ) {1.46 TMU_run_slice( nanosecs );1.47 SCIF_run_slice( nanosecs );1.48 }1.49 + sh4r.icount += sh4r.slice_cycle / sh4_cpu_period;1.50 return nanosecs;1.51 }1.53 @@ -181,12 +172,14 @@1.54 void sh4_save_state( FILE *f )1.55 {1.56 fwrite( &sh4r, sizeof(sh4r), 1, f );1.57 + TMU_save_state( f );1.58 SCIF_save_state( f );1.59 }1.61 int sh4_load_state( FILE * f )1.62 {1.63 fread( &sh4r, sizeof(sh4r), 1, f );1.64 + TMU_load_state( f );1.65 return SCIF_load_state( f );1.66 }
2.1 --- a/src/sh4/sh4core.h Wed Dec 28 22:50:08 2005 +00002.2 +++ b/src/sh4/sh4core.h Thu Dec 29 12:52:29 2005 +00002.3 @@ -1,5 +1,5 @@2.4 /**2.5 - * $Id: sh4core.h,v 1.8 2005-12-26 11:47:15 nkeynes Exp $2.6 + * $Id: sh4core.h,v 1.9 2005-12-29 12:52:29 nkeynes Exp $2.7 *2.8 * This file defines the public functions exported by the SH4 core, except2.9 * for disassembly functions defined in sh4dasm.h2.10 @@ -70,6 +70,7 @@2.11 uint32_t int_pending; /* flag set by the INTC = pending priority level */2.12 int in_delay_slot; /* flag to indicate the current instruction is in2.13 * a delay slot (certain rules apply) */2.14 + uint32_t slice_cycle; /* Current cycle within the timeslice */2.15 int sh4_state; /* Current power-on state (one of the SH4_STATE_* values ) */2.16 };2.18 @@ -106,6 +107,10 @@2.19 /* Peripheral functions */2.20 void DMAC_run_slice( uint32_t );2.21 void TMU_run_slice( uint32_t );2.22 +void TMU_update_clocks( void );2.23 +void TMU_reset( void );2.24 +void TMU_save_state( FILE * );2.25 +int TMU_load_state( FILE * );2.26 void SCIF_reset( void );2.27 void SCIF_run_slice( uint32_t );2.28 void SCIF_save_state( FILE *f );
3.1 --- a/src/sh4/timer.c Wed Dec 28 22:50:08 2005 +00003.2 +++ b/src/sh4/timer.c Thu Dec 29 12:52:29 2005 +00003.3 @@ -1,5 +1,5 @@3.4 /**3.5 - * $Id: timer.c,v 1.2 2005-12-25 05:57:00 nkeynes Exp $3.6 + * $Id: timer.c,v 1.3 2005-12-29 12:52:29 nkeynes Exp $3.7 *3.8 * SH4 Timer/Clock peripheral modules (CPG, TMU, RTC), combined together to3.9 * keep things simple (they intertwine a bit).3.10 @@ -22,21 +22,59 @@3.11 #include "clock.h"3.12 #include "sh4core.h"3.13 #include "sh4mmio.h"3.14 +#include "intc.h"3.16 /********************************* CPG *************************************/3.17 +/* This is the base clock from which all other clocks are derived */3.18 +uint32_t sh4_input_freq = SH4_BASE_RATE;3.19 +3.20 +uint32_t sh4_cpu_freq = SH4_BASE_RATE;3.21 +uint32_t sh4_bus_freq = SH4_BASE_RATE;3.22 +uint32_t sh4_peripheral_freq = SH4_BASE_RATE / 2;3.23 +3.24 +uint32_t sh4_cpu_period = 1000 / SH4_BASE_RATE; /* in nanoseconds */3.25 +uint32_t sh4_bus_period = 1000 / SH4_BASE_RATE;3.26 +uint32_t sh4_peripheral_period = 2000 / SH4_BASE_RATE;3.28 int32_t mmio_region_CPG_read( uint32_t reg )3.29 {3.30 return MMIO_READ( CPG, reg );3.31 }3.33 +/* CPU + bus dividers (note officially only the first 6 values are valid) */3.34 +int ifc_divider[8] = { 1, 2, 3, 4, 5, 8, 8, 8 };3.35 +/* Peripheral clock dividers (only first 5 are officially valid) */3.36 +int pfc_divider[8] = { 2, 3, 4, 6, 8, 8, 8, 8 };3.37 +3.38 void mmio_region_CPG_write( uint32_t reg, uint32_t val )3.39 {3.40 + uint32_t div;3.41 + switch( reg ) {3.42 + case FRQCR: /* Frequency control */3.43 + div = ifc_divider[(val >> 6) & 0x07];3.44 + sh4_cpu_freq = sh4_input_freq / div;3.45 + sh4_cpu_period = 1000 * div / sh4_input_freq;3.46 + div = ifc_divider[(val >> 3) & 0x07];3.47 + sh4_bus_freq = sh4_input_freq / div;3.48 + sh4_bus_period = 1000 * div / sh4_input_freq;3.49 + div = pfc_divider[val & 0x07];3.50 + sh4_peripheral_freq = sh4_input_freq / div;3.51 + sh4_peripheral_period = 1000 * div / sh4_input_freq;3.52 +3.53 + /* Update everything that depends on the peripheral frequency */3.54 + SCIF_update_line_speed();3.55 + break;3.56 + case WTCSR: /* Watchdog timer */3.57 + break;3.58 + }3.59 +3.60 MMIO_WRITE( CPG, reg, val );3.61 }3.63 /********************************** RTC *************************************/3.65 +uint32_t rtc_output_period;3.66 +3.67 int32_t mmio_region_RTC_read( uint32_t reg )3.68 {3.69 return MMIO_READ( RTC, reg );3.70 @@ -49,38 +87,120 @@3.72 /********************************** TMU *************************************/3.74 -int timer_divider[3] = {16,16,16};3.75 +#define TCR_ICPF 0x02003.76 +#define TCR_UNF 0x01003.77 +#define TCR_UNIE 0x00203.78 +3.79 +struct TMU_timer {3.80 + uint32_t timer_period;3.81 + uint32_t timer_remainder; /* left-over cycles from last count */3.82 + uint32_t timer_run; /* cycles already run from this slice */3.83 +};3.84 +3.85 +struct TMU_timer TMU_timers[3];3.87 int32_t mmio_region_TMU_read( uint32_t reg )3.88 {3.89 return MMIO_READ( TMU, reg );3.90 }3.92 +void TMU_set_timer_period( int timer, int tcr )3.93 +{3.94 + uint32_t period = 1;3.95 + switch( tcr & 0x07 ) {3.96 + case 0:3.97 + period = sh4_peripheral_period << 2 ;3.98 + break;3.99 + case 1:3.100 + period = sh4_peripheral_period << 4;3.101 + break;3.102 + case 2:3.103 + period = sh4_peripheral_period << 6;3.104 + break;3.105 + case 3:3.106 + period = sh4_peripheral_period << 8;3.107 + break;3.108 + case 4:3.109 + period = sh4_peripheral_period << 10;3.110 + break;3.111 + case 5:3.112 + /* Illegal value. */3.113 + ERROR( "TMU %d period set to illegal value (5)", timer );3.114 + period = sh4_peripheral_period << 12; /* for something to do */3.115 + break;3.116 + case 6:3.117 + period = rtc_output_period;3.118 + break;3.119 + case 7:3.120 + /* External clock... Hrm? */3.121 + period = sh4_peripheral_period; /* I dunno... */3.122 + break;3.123 + }3.124 + TMU_timers[timer].timer_period = period;3.125 +}3.127 -int get_timer_div( int val )3.128 +void TMU_start( int timer )3.129 {3.130 - switch( val & 0x07 ) {3.131 - case 0: return 16; /* assume peripheral clock is IC/4 */3.132 - case 1: return 64;3.133 - case 2: return 256;3.134 - case 3: return 1024;3.135 - case 4: return 4096;3.136 + TMU_timers[timer].timer_run = 0;3.137 + TMU_timers[timer].timer_remainder = 0;3.138 +}3.139 +3.140 +void TMU_stop( int timer )3.141 +{3.142 +3.143 +}3.144 +3.145 +/**3.146 + * Count the specified timer for a given number of nanoseconds.3.147 + */3.148 +uint32_t TMU_count( int timer, uint32_t nanosecs )3.149 +{3.150 + nanosecs = nanosecs + TMU_timers[timer].timer_remainder -3.151 + TMU_timers[timer].timer_run;3.152 + TMU_timers[timer].timer_remainder =3.153 + nanosecs % TMU_timers[timer].timer_period;3.154 + uint32_t count = nanosecs / TMU_timers[timer].timer_period;3.155 + uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );3.156 + uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );3.157 + if( count > value ) {3.158 + uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );3.159 + tcr |= TCR_UNF;3.160 + count -= value;3.161 + value = reset - (count % reset);3.162 + MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );3.163 + if( tcr & TCR_UNIE )3.164 + intc_raise_interrupt( INT_TMU_TUNI0 + timer );3.165 + } else {3.166 + value -= count;3.167 }3.168 - return 1;3.169 + MMIO_WRITE( TMU, TCNT0 + 12*timer, value );3.170 + return value;3.171 }3.173 void mmio_region_TMU_write( uint32_t reg, uint32_t val )3.174 {3.175 + uint32_t oldval;3.176 + int i;3.177 switch( reg ) {3.178 - case TCR0:3.179 - timer_divider[0] = get_timer_div(val);3.180 - break;3.181 - case TCR1:3.182 - timer_divider[1] = get_timer_div(val);3.183 - break;3.184 - case TCR2:3.185 - timer_divider[2] = get_timer_div(val);3.186 - break;3.187 + case TSTR:3.188 + oldval = MMIO_READ( TMU, TSTR );3.189 + for( i=0; i<3; i++ ) {3.190 + uint32_t tmp = 1<<i;3.191 + if( (oldval & tmp) == 1 && (val&tmp) == 0 )3.192 + TMU_stop(i);3.193 + else if( (oldval&tmp) == 0 && (val&tmp) == 1 )3.194 + TMU_start(i);3.195 + }3.196 + break;3.197 + case TCR0:3.198 + TMU_set_timer_period( 0, val );3.199 + break;3.200 + case TCR1:3.201 + TMU_set_timer_period( 1, val );3.202 + break;3.203 + case TCR2:3.204 + TMU_set_timer_period( 2, val );3.205 + break;3.206 }3.207 MMIO_WRITE( TMU, reg, val );3.208 }3.209 @@ -88,41 +208,44 @@3.210 void TMU_run_slice( uint32_t nanosecs )3.211 {3.212 int tcr = MMIO_READ( TMU, TSTR );3.213 - int cycles = nanosecs / sh4_peripheral_period;3.214 if( tcr & 0x01 ) {3.215 - int count = cycles / timer_divider[0];3.216 - int *val = MMIO_REG( TMU, TCNT0 );3.217 - if( *val < count ) {3.218 - MMIO_READ( TMU, TCR0 ) |= 0x100;3.219 - /* interrupt goes here */3.220 - count -= *val;3.221 - *val = MMIO_READ( TMU, TCOR0 ) - count;3.222 - } else {3.223 - *val -= count;3.224 - }3.225 + TMU_count( 0, nanosecs );3.226 + TMU_timers[0].timer_run = 0;3.227 }3.228 if( tcr & 0x02 ) {3.229 - int count = cycles / timer_divider[1];3.230 - int *val = MMIO_REG( TMU, TCNT1 );3.231 - if( *val < count ) {3.232 - MMIO_READ( TMU, TCR1 ) |= 0x100;3.233 - /* interrupt goes here */3.234 - count -= *val;3.235 - *val = MMIO_READ( TMU, TCOR1 ) - count;3.236 - } else {3.237 - *val -= count;3.238 - }3.239 + TMU_count( 1, nanosecs );3.240 + TMU_timers[1].timer_run = 0;3.241 }3.242 if( tcr & 0x04 ) {3.243 - int count = cycles / timer_divider[2];3.244 - int *val = MMIO_REG( TMU, TCNT2 );3.245 - if( *val < count ) {3.246 - MMIO_READ( TMU, TCR2 ) |= 0x100;3.247 - /* interrupt goes here */3.248 - count -= *val;3.249 - *val = MMIO_READ( TMU, TCOR2 ) - count;3.250 - } else {3.251 - *val -= count;3.252 - }3.253 + TMU_count( 2, nanosecs );3.254 + TMU_timers[2].timer_run = 0;3.255 }3.256 }3.257 +3.258 +void TMU_update_clocks()3.259 +{3.260 + TMU_set_timer_period( 0, MMIO_READ( TMU, TCR0 ) );3.261 + TMU_set_timer_period( 1, MMIO_READ( TMU, TCR1 ) );3.262 + TMU_set_timer_period( 2, MMIO_READ( TMU, TCR2 ) );3.263 +}3.264 +3.265 +void TMU_reset( )3.266 +{3.267 + TMU_timers[0].timer_remainder = 0;3.268 + TMU_timers[0].timer_run = 0;3.269 + TMU_timers[1].timer_remainder = 0;3.270 + TMU_timers[1].timer_run = 0;3.271 + TMU_timers[2].timer_remainder = 0;3.272 + TMU_timers[2].timer_run = 0;3.273 + TMU_update_clocks();3.274 +}3.275 +3.276 +void TMU_save_state( FILE *f ) {3.277 + fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );3.278 +}3.279 +3.280 +int TMU_load_state( FILE *f )3.281 +{3.282 + fread( &TMU_timers, sizeof(TMU_timers), 1, f );3.283 + return 0;3.284 +}
.