Search
lxdream.org :: lxdream/src/sh4/timer.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/timer.c
changeset 53:f2981805b929
prev30:89b30313d757
next115:699aa8916803
author nkeynes
date Thu Dec 29 12:52:29 2005 +0000 (14 years ago)
permissions -rw-r--r--
last change Rewrite timer to be more accurate, also support cycle precision in the future
file annotate diff log raw
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.3 @@ -1,5 +1,5 @@
1.4 /**
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.7 *
1.8 * SH4 Timer/Clock peripheral modules (CPG, TMU, RTC), combined together to
1.9 * keep things simple (they intertwine a bit).
1.10 @@ -22,21 +22,59 @@
1.11 #include "clock.h"
1.12 #include "sh4core.h"
1.13 #include "sh4mmio.h"
1.14 +#include "intc.h"
1.15
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.19 +
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.23 +
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.27
1.28 int32_t mmio_region_CPG_read( uint32_t reg )
1.29 {
1.30 return MMIO_READ( CPG, reg );
1.31 }
1.32
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.37 +
1.38 void mmio_region_CPG_write( uint32_t reg, uint32_t val )
1.39 {
1.40 + uint32_t div;
1.41 + switch( reg ) {
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.52 +
1.53 + /* Update everything that depends on the peripheral frequency */
1.54 + SCIF_update_line_speed();
1.55 + break;
1.56 + case WTCSR: /* Watchdog timer */
1.57 + break;
1.58 + }
1.59 +
1.60 MMIO_WRITE( CPG, reg, val );
1.61 }
1.62
1.63 /********************************** RTC *************************************/
1.64
1.65 +uint32_t rtc_output_period;
1.66 +
1.67 int32_t mmio_region_RTC_read( uint32_t reg )
1.68 {
1.69 return MMIO_READ( RTC, reg );
1.70 @@ -49,38 +87,120 @@
1.71
1.72 /********************************** TMU *************************************/
1.73
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.78 +
1.79 +struct TMU_timer {
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.83 +};
1.84 +
1.85 +struct TMU_timer TMU_timers[3];
1.86
1.87 int32_t mmio_region_TMU_read( uint32_t reg )
1.88 {
1.89 return MMIO_READ( TMU, reg );
1.90 }
1.91
1.92 +void TMU_set_timer_period( int timer, int tcr )
1.93 +{
1.94 + uint32_t period = 1;
1.95 + switch( tcr & 0x07 ) {
1.96 + case 0:
1.97 + period = sh4_peripheral_period << 2 ;
1.98 + break;
1.99 + case 1:
1.100 + period = sh4_peripheral_period << 4;
1.101 + break;
1.102 + case 2:
1.103 + period = sh4_peripheral_period << 6;
1.104 + break;
1.105 + case 3:
1.106 + period = sh4_peripheral_period << 8;
1.107 + break;
1.108 + case 4:
1.109 + period = sh4_peripheral_period << 10;
1.110 + break;
1.111 + case 5:
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.115 + break;
1.116 + case 6:
1.117 + period = rtc_output_period;
1.118 + break;
1.119 + case 7:
1.120 + /* External clock... Hrm? */
1.121 + period = sh4_peripheral_period; /* I dunno... */
1.122 + break;
1.123 + }
1.124 + TMU_timers[timer].timer_period = period;
1.125 +}
1.126
1.127 -int get_timer_div( int val )
1.128 +void TMU_start( int timer )
1.129 {
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.138 +}
1.139 +
1.140 +void TMU_stop( int timer )
1.141 +{
1.142 +
1.143 +}
1.144 +
1.145 +/**
1.146 + * Count the specified timer for a given number of nanoseconds.
1.147 + */
1.148 +uint32_t TMU_count( int timer, uint32_t nanosecs )
1.149 +{
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.159 + tcr |= TCR_UNF;
1.160 + count -= value;
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.165 + } else {
1.166 + value -= count;
1.167 }
1.168 - return 1;
1.169 + MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
1.170 + return value;
1.171 }
1.172
1.173 void mmio_region_TMU_write( uint32_t reg, uint32_t val )
1.174 {
1.175 + uint32_t oldval;
1.176 + int i;
1.177 switch( reg ) {
1.178 - case TCR0:
1.179 - timer_divider[0] = get_timer_div(val);
1.180 - break;
1.181 - case TCR1:
1.182 - timer_divider[1] = get_timer_div(val);
1.183 - break;
1.184 - case TCR2:
1.185 - timer_divider[2] = get_timer_div(val);
1.186 - break;
1.187 + case TSTR:
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.192 + TMU_stop(i);
1.193 + else if( (oldval&tmp) == 0 && (val&tmp) == 1 )
1.194 + TMU_start(i);
1.195 + }
1.196 + break;
1.197 + case TCR0:
1.198 + TMU_set_timer_period( 0, val );
1.199 + break;
1.200 + case TCR1:
1.201 + TMU_set_timer_period( 1, val );
1.202 + break;
1.203 + case TCR2:
1.204 + TMU_set_timer_period( 2, val );
1.205 + break;
1.206 }
1.207 MMIO_WRITE( TMU, reg, val );
1.208 }
1.209 @@ -88,41 +208,44 @@
1.210 void TMU_run_slice( uint32_t nanosecs )
1.211 {
1.212 int tcr = MMIO_READ( TMU, TSTR );
1.213 - int cycles = nanosecs / sh4_peripheral_period;
1.214 if( tcr & 0x01 ) {
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.220 - count -= *val;
1.221 - *val = MMIO_READ( TMU, TCOR0 ) - count;
1.222 - } else {
1.223 - *val -= count;
1.224 - }
1.225 + TMU_count( 0, nanosecs );
1.226 + TMU_timers[0].timer_run = 0;
1.227 }
1.228 if( tcr & 0x02 ) {
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.234 - count -= *val;
1.235 - *val = MMIO_READ( TMU, TCOR1 ) - count;
1.236 - } else {
1.237 - *val -= count;
1.238 - }
1.239 + TMU_count( 1, nanosecs );
1.240 + TMU_timers[1].timer_run = 0;
1.241 }
1.242 if( tcr & 0x04 ) {
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.248 - count -= *val;
1.249 - *val = MMIO_READ( TMU, TCOR2 ) - count;
1.250 - } else {
1.251 - *val -= count;
1.252 - }
1.253 + TMU_count( 2, nanosecs );
1.254 + TMU_timers[2].timer_run = 0;
1.255 }
1.256 }
1.257 +
1.258 +void TMU_update_clocks()
1.259 +{
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.263 +}
1.264 +
1.265 +void TMU_reset( )
1.266 +{
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.274 +}
1.275 +
1.276 +void TMU_save_state( FILE *f ) {
1.277 + fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
1.278 +}
1.279 +
1.280 +int TMU_load_state( FILE *f )
1.281 +{
1.282 + fread( &TMU_timers, sizeof(TMU_timers), 1, f );
1.283 + return 0;
1.284 +}
.