Search
lxdream.org :: lxdream/src/sh4/timer.c
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
view annotate diff log raw
     1 /**
     2  * $Id: timer.c,v 1.3 2005-12-29 12:52:29 nkeynes Exp $
     3  * 
     4  * SH4 Timer/Clock peripheral modules (CPG, TMU, RTC), combined together to
     5  * keep things simple (they intertwine a bit).
     6  *
     7  * Copyright (c) 2005 Nathan Keynes.
     8  *
     9  * This program is free software; you can redistribute it and/or modify
    10  * it under the terms of the GNU General Public License as published by
    11  * the Free Software Foundation; either version 2 of the License, or
    12  * (at your option) any later version.
    13  *
    14  * This program is distributed in the hope that it will be useful,
    15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    17  * GNU General Public License for more details.
    18  */
    20 #include "dream.h"
    21 #include "mem.h"
    22 #include "clock.h"
    23 #include "sh4core.h"
    24 #include "sh4mmio.h"
    25 #include "intc.h"
    27 /********************************* CPG *************************************/
    28 /* This is the base clock from which all other clocks are derived */
    29 uint32_t sh4_input_freq = SH4_BASE_RATE;
    31 uint32_t sh4_cpu_freq = SH4_BASE_RATE;
    32 uint32_t sh4_bus_freq = SH4_BASE_RATE;
    33 uint32_t sh4_peripheral_freq = SH4_BASE_RATE / 2;
    35 uint32_t sh4_cpu_period = 1000 / SH4_BASE_RATE; /* in nanoseconds */
    36 uint32_t sh4_bus_period = 1000 / SH4_BASE_RATE;
    37 uint32_t sh4_peripheral_period = 2000 / SH4_BASE_RATE;
    39 int32_t mmio_region_CPG_read( uint32_t reg )
    40 {
    41     return MMIO_READ( CPG, reg );
    42 }
    44 /* CPU + bus dividers (note officially only the first 6 values are valid) */
    45 int ifc_divider[8] = { 1, 2, 3, 4, 5, 8, 8, 8 };
    46 /* Peripheral clock dividers (only first 5 are officially valid) */
    47 int pfc_divider[8] = { 2, 3, 4, 6, 8, 8, 8, 8 };
    49 void mmio_region_CPG_write( uint32_t reg, uint32_t val )
    50 {
    51     uint32_t div;
    52     switch( reg ) {
    53     case FRQCR: /* Frequency control */
    54 	div = ifc_divider[(val >> 6) & 0x07];
    55 	sh4_cpu_freq = sh4_input_freq / div;
    56 	sh4_cpu_period = 1000 * div / sh4_input_freq;
    57 	div = ifc_divider[(val >> 3) & 0x07];
    58 	sh4_bus_freq = sh4_input_freq / div;
    59 	sh4_bus_period = 1000 * div / sh4_input_freq;
    60 	div = pfc_divider[val & 0x07];
    61 	sh4_peripheral_freq = sh4_input_freq / div;
    62 	sh4_peripheral_period = 1000 * div / sh4_input_freq;
    64 	/* Update everything that depends on the peripheral frequency */
    65 	SCIF_update_line_speed();
    66 	break;
    67     case WTCSR: /* Watchdog timer */
    68 	break;
    69     }
    71     MMIO_WRITE( CPG, reg, val );
    72 }
    74 /********************************** RTC *************************************/
    76 uint32_t rtc_output_period;
    78 int32_t mmio_region_RTC_read( uint32_t reg )
    79 {
    80     return MMIO_READ( RTC, reg );
    81 }
    83 void mmio_region_RTC_write( uint32_t reg, uint32_t val )
    84 {
    85     MMIO_WRITE( RTC, reg, val );
    86 }
    88 /********************************** TMU *************************************/
    90 #define TCR_ICPF 0x0200
    91 #define TCR_UNF  0x0100
    92 #define TCR_UNIE 0x0020
    94 struct TMU_timer {
    95     uint32_t timer_period;
    96     uint32_t timer_remainder; /* left-over cycles from last count */
    97     uint32_t timer_run; /* cycles already run from this slice */
    98 };
   100 struct TMU_timer TMU_timers[3];
   102 int32_t mmio_region_TMU_read( uint32_t reg )
   103 {
   104     return MMIO_READ( TMU, reg );
   105 }
   107 void TMU_set_timer_period( int timer,  int tcr )
   108 {
   109     uint32_t period = 1;
   110     switch( tcr & 0x07 ) {
   111     case 0:
   112 	period = sh4_peripheral_period << 2 ;
   113 	break;
   114     case 1: 
   115 	period = sh4_peripheral_period << 4;
   116 	break;
   117     case 2:
   118 	period = sh4_peripheral_period << 6;
   119 	break;
   120     case 3: 
   121 	period = sh4_peripheral_period << 8;
   122 	break;
   123     case 4:
   124 	period = sh4_peripheral_period << 10;
   125 	break;
   126     case 5:
   127 	/* Illegal value. */
   128 	ERROR( "TMU %d period set to illegal value (5)", timer );
   129 	period = sh4_peripheral_period << 12; /* for something to do */
   130 	break;
   131     case 6:
   132 	period = rtc_output_period;
   133 	break;
   134     case 7:
   135 	/* External clock... Hrm? */
   136 	period = sh4_peripheral_period; /* I dunno... */
   137 	break;
   138     }
   139     TMU_timers[timer].timer_period = period;
   140 }
   142 void TMU_start( int timer )
   143 {
   144     TMU_timers[timer].timer_run = 0;
   145     TMU_timers[timer].timer_remainder = 0;
   146 }
   148 void TMU_stop( int timer )
   149 {
   151 }
   153 /**
   154  * Count the specified timer for a given number of nanoseconds.
   155  */
   156 uint32_t TMU_count( int timer, uint32_t nanosecs ) 
   157 {
   158     nanosecs = nanosecs + TMU_timers[timer].timer_remainder -
   159 	TMU_timers[timer].timer_run;
   160     TMU_timers[timer].timer_remainder = 
   161 	nanosecs % TMU_timers[timer].timer_period;
   162     uint32_t count = nanosecs / TMU_timers[timer].timer_period;
   163     uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
   164     uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
   165     if( count > value ) {
   166 	uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
   167 	tcr |= TCR_UNF;
   168 	count -= value;
   169         value = reset - (count % reset);
   170 	MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
   171 	if( tcr & TCR_UNIE ) 
   172 	    intc_raise_interrupt( INT_TMU_TUNI0 + timer );
   173     } else {
   174 	value -= count;
   175     }
   176     MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
   177     return value;
   178 }
   180 void mmio_region_TMU_write( uint32_t reg, uint32_t val )
   181 {
   182     uint32_t oldval;
   183     int i;
   184     switch( reg ) {
   185     case TSTR:
   186 	oldval = MMIO_READ( TMU, TSTR );
   187 	for( i=0; i<3; i++ ) {
   188 	    uint32_t tmp = 1<<i;
   189 	    if( (oldval & tmp) == 1 && (val&tmp) == 0  )
   190 		TMU_stop(i);
   191 	    else if( (oldval&tmp) == 0 && (val&tmp) == 1 )
   192 		TMU_start(i);
   193 	}
   194 	break;
   195     case TCR0:
   196 	TMU_set_timer_period( 0, val );
   197 	break;
   198     case TCR1:
   199 	TMU_set_timer_period( 1, val );
   200 	break;
   201     case TCR2:
   202 	TMU_set_timer_period( 2, val );
   203 	break;
   204     }
   205     MMIO_WRITE( TMU, reg, val );
   206 }
   208 void TMU_run_slice( uint32_t nanosecs )
   209 {
   210     int tcr = MMIO_READ( TMU, TSTR );
   211     if( tcr & 0x01 ) {
   212 	TMU_count( 0, nanosecs );
   213 	TMU_timers[0].timer_run = 0;
   214     }
   215     if( tcr & 0x02 ) {
   216 	TMU_count( 1, nanosecs );
   217 	TMU_timers[1].timer_run = 0;
   218     }
   219     if( tcr & 0x04 ) {
   220 	TMU_count( 2, nanosecs );
   221 	TMU_timers[2].timer_run = 0;
   222     }
   223 }
   225 void TMU_update_clocks()
   226 {
   227     TMU_set_timer_period( 0, MMIO_READ( TMU, TCR0 ) );
   228     TMU_set_timer_period( 1, MMIO_READ( TMU, TCR1 ) );
   229     TMU_set_timer_period( 2, MMIO_READ( TMU, TCR2 ) );
   230 }
   232 void TMU_reset( )
   233 {
   234     TMU_timers[0].timer_remainder = 0;
   235     TMU_timers[0].timer_run = 0;
   236     TMU_timers[1].timer_remainder = 0;
   237     TMU_timers[1].timer_run = 0;
   238     TMU_timers[2].timer_remainder = 0;
   239     TMU_timers[2].timer_run = 0;
   240     TMU_update_clocks();
   241 }
   243 void TMU_save_state( FILE *f ) {
   244     fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
   245 }
   247 int TMU_load_state( FILE *f ) 
   248 {
   249     fread( &TMU_timers, sizeof(TMU_timers), 1, f );
   250     return 0;
   251 }
.