Search
lxdream.org :: lxdream/src/sh4/timer.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/timer.c
changeset 929:fd8cb0c82f5f
prev859:b941c703ccd6
next975:007bf7eb944f
author nkeynes
date Sat Dec 27 02:59:35 2008 +0000 (15 years ago)
branchlxdream-mem
permissions -rw-r--r--
last change Replace fpscr_mask/fpscr flags in xlat_cache_block with a single xlat_sh4_mode,
which tracks the field of the same name in sh4r - actually a little faster this way.
Now depends on SR.MD, FPSCR.PR and FPSCR.SZ (although it doesn't benefit from the SR
flag yet).

Also fixed the failure to check the flags in the common case (code address returned
by previous block) which took away the performance benefits, but oh well.
view annotate diff log raw
     1 /**
     2  * $Id$
     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 <assert.h>
    21 #include "lxdream.h"
    22 #include "mem.h"
    23 #include "clock.h"
    24 #include "eventq.h"
    25 #include "sh4/sh4core.h"
    26 #include "sh4/sh4mmio.h"
    27 #include "sh4/intc.h"
    29 /********************************* CPG *************************************/
    30 /* This is the base clock from which all other clocks are derived. 
    31  * Note: The real clock runs at 33Mhz, which is multiplied by the PLL to
    32  * run the instruction clock at 200Mhz. For sake of simplicity/precision,
    33  * we instead use 200Mhz as the base rate and divide everything down instead.
    34  **/
    35 uint32_t sh4_input_freq = SH4_BASE_RATE;
    37 uint32_t sh4_cpu_multiplier = 2000; /* = 0.5 * frequency */
    39 uint32_t sh4_cpu_freq = SH4_BASE_RATE;
    40 uint32_t sh4_bus_freq = SH4_BASE_RATE / 2;
    41 uint32_t sh4_peripheral_freq = SH4_BASE_RATE / 4;
    43 uint32_t sh4_cpu_period = 1000 / SH4_BASE_RATE; /* in nanoseconds */
    44 uint32_t sh4_bus_period = 2* 1000 / SH4_BASE_RATE;
    45 uint32_t sh4_peripheral_period = 4 * 2000 / SH4_BASE_RATE;
    47 MMIO_REGION_READ_FN( CPG, reg )
    48 {
    49     return MMIO_READ( CPG, reg&0xFFF );
    50 }
    52 /* CPU + bus dividers (note officially only the first 6 values are valid) */
    53 int ifc_divider[8] = { 1, 2, 3, 4, 5, 8, 8, 8 };
    54 /* Peripheral clock dividers (only first 5 are officially valid) */
    55 int pfc_divider[8] = { 2, 3, 4, 6, 8, 8, 8, 8 };
    57 MMIO_REGION_WRITE_FN( CPG, reg, val )
    58 {
    59     uint32_t div;
    60     uint32_t primary_clock = sh4_input_freq;
    61     reg &= 0xFFF;
    62     switch( reg ) {
    63     case FRQCR: /* Frequency control */
    64         if( (val & FRQCR_PLL1EN) == 0 )
    65             primary_clock /= 6;
    66         div = ifc_divider[(val >> 6) & 0x07];
    67         sh4_cpu_freq = primary_clock / div;
    68         sh4_cpu_period = sh4_cpu_multiplier * div / sh4_input_freq;
    69         div = ifc_divider[(val >> 3) & 0x07];
    70         sh4_bus_freq = primary_clock / div;
    71         sh4_bus_period = 1000 * div / sh4_input_freq;
    72         div = pfc_divider[val & 0x07];
    73         sh4_peripheral_freq = primary_clock / div;
    74         sh4_peripheral_period = 1000 * div / sh4_input_freq;
    76         /* Update everything that depends on the peripheral frequency */
    77         SCIF_update_line_speed();
    78         break;
    79     case WTCSR: /* Watchdog timer */
    80         break;
    81     }
    83     MMIO_WRITE( CPG, reg, val );
    84 }
    86 /**
    87  * We don't really know what the default reset value is as it's determined
    88  * by the mode select pins. This is the standard value that the BIOS sets,
    89  * however, so it works for now.
    90  */
    91 void CPG_reset( )
    92 {
    93     mmio_region_CPG_write( FRQCR, 0x0E0A );
    94 }
    97 /********************************** RTC *************************************/
    99 uint32_t rtc_output_period;
   101 MMIO_REGION_READ_FN( RTC, reg )
   102 {
   103     return MMIO_READ( RTC, reg &0xFFF );
   104 }
   106 MMIO_REGION_WRITE_FN( RTC, reg, val )
   107 {
   108     MMIO_WRITE( RTC, reg &0xFFF, val );
   109 }
   111 /********************************** TMU *************************************/
   113 #define TMU_IS_RUNNING(timer)  (MMIO_READ(TMU,TSTR) & (1<<timer))
   115 uint32_t TMU_count( int timer, uint32_t nanosecs );
   117 void TMU_event_callback( int eventid )
   118 {
   119     TMU_count( eventid - EVENT_TMU0, sh4r.slice_cycle );
   120 }
   122 void TMU_init(void)
   123 {
   124     register_event_callback( EVENT_TMU0, TMU_event_callback );
   125     register_event_callback( EVENT_TMU1, TMU_event_callback );
   126     register_event_callback( EVENT_TMU2, TMU_event_callback );
   127 }    
   129 #define TCR_ICPF 0x0200
   130 #define TCR_UNF  0x0100
   131 #define TCR_UNIE 0x0020
   133 #define TCR_IRQ_ACTIVE (TCR_UNF|TCR_UNIE)
   135 struct TMU_timer {
   136     uint32_t timer_period;
   137     uint32_t timer_remainder; /* left-over cycles from last count */
   138     uint32_t timer_run; /* cycles already run from this slice */
   139 };
   141 static struct TMU_timer TMU_timers[3];
   143 void TMU_set_timer_control( int timer,  int tcr )
   144 {
   145     uint32_t period = 1;
   146     uint32_t oldtcr = MMIO_READ( TMU, TCR0 + (12*timer) );
   148     if( (oldtcr & TCR_UNF) == 0 ) {
   149         tcr = tcr & (~TCR_UNF);
   150     } else {
   151         if( ((oldtcr & TCR_UNIE) == 0) && 
   152                 (tcr & TCR_IRQ_ACTIVE) == TCR_IRQ_ACTIVE ) {
   153             intc_raise_interrupt( INT_TMU_TUNI0 + timer );
   154         } else if( (oldtcr & TCR_UNIE) != 0 && 
   155                 (tcr & TCR_IRQ_ACTIVE) != TCR_IRQ_ACTIVE ) {
   156             intc_clear_interrupt( INT_TMU_TUNI0 + timer );
   157         }
   158     }
   160     switch( tcr & 0x07 ) {
   161     case 0:
   162         period = sh4_peripheral_period << 2 ;
   163         break;
   164     case 1: 
   165         period = sh4_peripheral_period << 4;
   166         break;
   167     case 2:
   168         period = sh4_peripheral_period << 6;
   169         break;
   170     case 3: 
   171         period = sh4_peripheral_period << 8;
   172         break;
   173     case 4:
   174         period = sh4_peripheral_period << 10;
   175         break;
   176     case 5:
   177         /* Illegal value. */
   178         ERROR( "TMU %d period set to illegal value (5)", timer );
   179         period = sh4_peripheral_period << 12; /* for something to do */
   180         break;
   181     case 6:
   182         period = rtc_output_period;
   183         break;
   184     case 7:
   185         /* External clock... Hrm? */
   186         period = sh4_peripheral_period; /* I dunno... */
   187         break;
   188     }
   189     TMU_timers[timer].timer_period = period;
   191     MMIO_WRITE( TMU, TCR0 + (12*timer), tcr );
   192 }
   194 void TMU_schedule_timer( int timer )
   195 {
   196     uint64_t duration = (uint64_t)((uint32_t)(MMIO_READ( TMU, TCNT0 + 12*timer )+1)) * 
   197     (uint64_t)TMU_timers[timer].timer_period - TMU_timers[timer].timer_remainder;
   198     event_schedule_long( EVENT_TMU0+timer, (uint32_t)(duration / 1000000000), 
   199                          (uint32_t)(duration % 1000000000) );
   200 }
   202 void TMU_start( int timer )
   203 {
   204     TMU_timers[timer].timer_run = sh4r.slice_cycle;
   205     TMU_timers[timer].timer_remainder = 0;
   206     TMU_schedule_timer( timer );
   207 }
   209 /**
   210  * Stop the given timer. Run it up to the current time and leave it there.
   211  */
   212 void TMU_stop( int timer )
   213 {
   214     TMU_count( timer, sh4r.slice_cycle );
   215     event_cancel( EVENT_TMU0+timer );
   216 }
   218 /**
   219  * Count the specified timer for a given number of nanoseconds.
   220  */
   221 uint32_t TMU_count( int timer, uint32_t nanosecs ) 
   222 {
   223     uint32_t run_ns = nanosecs + TMU_timers[timer].timer_remainder -
   224     TMU_timers[timer].timer_run;
   225     TMU_timers[timer].timer_remainder = 
   226         run_ns % TMU_timers[timer].timer_period;
   227     TMU_timers[timer].timer_run = nanosecs;
   228     uint32_t count = run_ns / TMU_timers[timer].timer_period;
   229     uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
   230     uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
   231     if( count > value ) {
   232         uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
   233         tcr |= TCR_UNF;
   234         count -= value;
   235         value = reset - (count % reset) + 1;
   236         MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
   237         if( tcr & TCR_UNIE ) 
   238             intc_raise_interrupt( INT_TMU_TUNI0 + timer );
   239         MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
   240         TMU_schedule_timer(timer);
   241     } else {
   242         value -= count;
   243         MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
   244     }
   245     return value;
   246 }
   248 MMIO_REGION_READ_FN( TMU, reg )
   249 {
   250     reg &= 0xFFF;
   251     switch( reg ) {
   252     case TCNT0:
   253         TMU_count( 0, sh4r.slice_cycle );
   254         break;
   255     case TCNT1:
   256         TMU_count( 1, sh4r.slice_cycle );
   257         break;
   258     case TCNT2:
   259         TMU_count( 2, sh4r.slice_cycle );
   260         break;
   261     }
   262     return MMIO_READ( TMU, reg );
   263 }
   265 MMIO_REGION_WRITE_FN( TMU, reg, val )
   266 {
   267     uint32_t oldval;
   268     int i;
   269     reg &= 0xFFF;
   270     switch( reg ) {
   271     case TSTR:
   272         oldval = MMIO_READ( TMU, TSTR );
   273         for( i=0; i<3; i++ ) {
   274             uint32_t tmp = 1<<i;
   275             if( (oldval & tmp) != 0 && (val&tmp) == 0  )
   276                 TMU_stop(i);
   277             else if( (oldval&tmp) == 0 && (val&tmp) != 0 )
   278                 TMU_start(i);
   279         }
   280         break;
   281     case TCR0:
   282         TMU_set_timer_control( 0, val );
   283         return;
   284     case TCR1:
   285         TMU_set_timer_control( 1, val );
   286         return;
   287     case TCR2:
   288         TMU_set_timer_control( 2, val );
   289         return;
   290     case TCNT0:
   291         MMIO_WRITE( TMU, reg, val );
   292         if( TMU_IS_RUNNING(0) ) { // reschedule
   293             TMU_timers[0].timer_run = sh4r.slice_cycle;
   294             TMU_schedule_timer( 0 );
   295         }
   296         return;
   297     case TCNT1:
   298         MMIO_WRITE( TMU, reg, val );
   299         if( TMU_IS_RUNNING(1) ) { // reschedule
   300             TMU_timers[1].timer_run = sh4r.slice_cycle;
   301             TMU_schedule_timer( 1 );
   302         }
   303         return;
   304     case TCNT2:
   305         MMIO_WRITE( TMU, reg, val );
   306         if( TMU_IS_RUNNING(2) ) { // reschedule
   307             TMU_timers[2].timer_run = sh4r.slice_cycle;
   308             TMU_schedule_timer( 2 );
   309         }
   310         return;
   311     }
   312     MMIO_WRITE( TMU, reg, val );
   313 }
   315 void TMU_count_all( uint32_t nanosecs )
   316 {
   317     int tcr = MMIO_READ( TMU, TSTR );
   318     if( tcr & 0x01 ) {
   319         TMU_count( 0, nanosecs );
   320     }
   321     if( tcr & 0x02 ) {
   322         TMU_count( 1, nanosecs );
   323     }
   324     if( tcr & 0x04 ) {
   325         TMU_count( 2, nanosecs );
   326     }
   327 }
   329 void TMU_run_slice( uint32_t nanosecs )
   330 {
   331     TMU_count_all( nanosecs );
   332     TMU_timers[0].timer_run = 0;
   333     TMU_timers[1].timer_run = 0;
   334     TMU_timers[2].timer_run = 0;
   335 }
   337 void TMU_update_clocks()
   338 {
   339     TMU_set_timer_control( 0, MMIO_READ( TMU, TCR0 ) );
   340     TMU_set_timer_control( 1, MMIO_READ( TMU, TCR1 ) );
   341     TMU_set_timer_control( 2, MMIO_READ( TMU, TCR2 ) );
   342 }
   344 void TMU_reset( )
   345 {
   346     TMU_timers[0].timer_remainder = 0;
   347     TMU_timers[0].timer_run = 0;
   348     TMU_timers[1].timer_remainder = 0;
   349     TMU_timers[1].timer_run = 0;
   350     TMU_timers[2].timer_remainder = 0;
   351     TMU_timers[2].timer_run = 0;
   352     TMU_update_clocks();
   353 }
   355 void TMU_save_state( FILE *f ) {
   356     fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
   357 }
   359 int TMU_load_state( FILE *f ) 
   360 {
   361     fread( &TMU_timers, sizeof(TMU_timers), 1, f );
   362     return 0;
   363 }
.