Search
lxdream.org :: lxdream/src/sh4/timer.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/timer.c
changeset 859:b941c703ccd6
prev736:a02d1475ccfd
next929:fd8cb0c82f5f
author nkeynes
date Thu Dec 11 23:26:03 2008 +0000 (15 years ago)
permissions -rw-r--r--
last change Disable the generational translation cache - I've got no evidence that it
actually helps performance, and it simplifies things to get rid of it (in
particular, translated code doesn't have to worry about being moved now).
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 int32_t mmio_region_CPG_read( uint32_t reg )
    48 {
    49     return MMIO_READ( CPG, reg );
    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 void mmio_region_CPG_write( uint32_t reg, uint32_t val )
    58 {
    59     uint32_t div;
    60     uint32_t primary_clock = sh4_input_freq;
    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 int32_t mmio_region_RTC_read( uint32_t reg )
   102 {
   103     return MMIO_READ( RTC, reg );
   104 }
   106 void mmio_region_RTC_write( uint32_t reg, uint32_t val )
   107 {
   108     MMIO_WRITE( RTC, reg, 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 int32_t mmio_region_TMU_read( uint32_t reg )
   144 {
   145     switch( reg ) {
   146     case TCNT0:
   147         TMU_count( 0, sh4r.slice_cycle );
   148         break;
   149     case TCNT1:
   150         TMU_count( 1, sh4r.slice_cycle );
   151         break;
   152     case TCNT2:
   153         TMU_count( 2, sh4r.slice_cycle );
   154         break;
   155     }
   156     return MMIO_READ( TMU, reg );
   157 }
   159 void TMU_set_timer_control( int timer,  int tcr )
   160 {
   161     uint32_t period = 1;
   162     uint32_t oldtcr = MMIO_READ( TMU, TCR0 + (12*timer) );
   164     if( (oldtcr & TCR_UNF) == 0 ) {
   165         tcr = tcr & (~TCR_UNF);
   166     } else {
   167         if( ((oldtcr & TCR_UNIE) == 0) && 
   168                 (tcr & TCR_IRQ_ACTIVE) == TCR_IRQ_ACTIVE ) {
   169             intc_raise_interrupt( INT_TMU_TUNI0 + timer );
   170         } else if( (oldtcr & TCR_UNIE) != 0 && 
   171                 (tcr & TCR_IRQ_ACTIVE) != TCR_IRQ_ACTIVE ) {
   172             intc_clear_interrupt( INT_TMU_TUNI0 + timer );
   173         }
   174     }
   176     switch( tcr & 0x07 ) {
   177     case 0:
   178         period = sh4_peripheral_period << 2 ;
   179         break;
   180     case 1: 
   181         period = sh4_peripheral_period << 4;
   182         break;
   183     case 2:
   184         period = sh4_peripheral_period << 6;
   185         break;
   186     case 3: 
   187         period = sh4_peripheral_period << 8;
   188         break;
   189     case 4:
   190         period = sh4_peripheral_period << 10;
   191         break;
   192     case 5:
   193         /* Illegal value. */
   194         ERROR( "TMU %d period set to illegal value (5)", timer );
   195         period = sh4_peripheral_period << 12; /* for something to do */
   196         break;
   197     case 6:
   198         period = rtc_output_period;
   199         break;
   200     case 7:
   201         /* External clock... Hrm? */
   202         period = sh4_peripheral_period; /* I dunno... */
   203         break;
   204     }
   205     TMU_timers[timer].timer_period = period;
   207     MMIO_WRITE( TMU, TCR0 + (12*timer), tcr );
   208 }
   210 void TMU_schedule_timer( int timer )
   211 {
   212     uint64_t duration = (uint64_t)((uint32_t)(MMIO_READ( TMU, TCNT0 + 12*timer )+1)) * 
   213     (uint64_t)TMU_timers[timer].timer_period - TMU_timers[timer].timer_remainder;
   214     event_schedule_long( EVENT_TMU0+timer, (uint32_t)(duration / 1000000000), 
   215                          (uint32_t)(duration % 1000000000) );
   216 }
   218 void TMU_start( int timer )
   219 {
   220     TMU_timers[timer].timer_run = sh4r.slice_cycle;
   221     TMU_timers[timer].timer_remainder = 0;
   222     TMU_schedule_timer( timer );
   223 }
   225 /**
   226  * Stop the given timer. Run it up to the current time and leave it there.
   227  */
   228 void TMU_stop( int timer )
   229 {
   230     TMU_count( timer, sh4r.slice_cycle );
   231     event_cancel( EVENT_TMU0+timer );
   232 }
   234 /**
   235  * Count the specified timer for a given number of nanoseconds.
   236  */
   237 uint32_t TMU_count( int timer, uint32_t nanosecs ) 
   238 {
   239     uint32_t run_ns = nanosecs + TMU_timers[timer].timer_remainder -
   240     TMU_timers[timer].timer_run;
   241     TMU_timers[timer].timer_remainder = 
   242         run_ns % TMU_timers[timer].timer_period;
   243     TMU_timers[timer].timer_run = nanosecs;
   244     uint32_t count = run_ns / TMU_timers[timer].timer_period;
   245     uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
   246     uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
   247     if( count > value ) {
   248         uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
   249         tcr |= TCR_UNF;
   250         count -= value;
   251         value = reset - (count % reset) + 1;
   252         MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
   253         if( tcr & TCR_UNIE ) 
   254             intc_raise_interrupt( INT_TMU_TUNI0 + timer );
   255         MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
   256         TMU_schedule_timer(timer);
   257     } else {
   258         value -= count;
   259         MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
   260     }
   261     return value;
   262 }
   264 void mmio_region_TMU_write( uint32_t reg, uint32_t val )
   265 {
   266     uint32_t oldval;
   267     int i;
   268     switch( reg ) {
   269     case TSTR:
   270         oldval = MMIO_READ( TMU, TSTR );
   271         for( i=0; i<3; i++ ) {
   272             uint32_t tmp = 1<<i;
   273             if( (oldval & tmp) != 0 && (val&tmp) == 0  )
   274                 TMU_stop(i);
   275             else if( (oldval&tmp) == 0 && (val&tmp) != 0 )
   276                 TMU_start(i);
   277         }
   278         break;
   279     case TCR0:
   280         TMU_set_timer_control( 0, val );
   281         return;
   282     case TCR1:
   283         TMU_set_timer_control( 1, val );
   284         return;
   285     case TCR2:
   286         TMU_set_timer_control( 2, val );
   287         return;
   288     case TCNT0:
   289         MMIO_WRITE( TMU, reg, val );
   290         if( TMU_IS_RUNNING(0) ) { // reschedule
   291             TMU_timers[0].timer_run = sh4r.slice_cycle;
   292             TMU_schedule_timer( 0 );
   293         }
   294         return;
   295     case TCNT1:
   296         MMIO_WRITE( TMU, reg, val );
   297         if( TMU_IS_RUNNING(1) ) { // reschedule
   298             TMU_timers[1].timer_run = sh4r.slice_cycle;
   299             TMU_schedule_timer( 1 );
   300         }
   301         return;
   302     case TCNT2:
   303         MMIO_WRITE( TMU, reg, val );
   304         if( TMU_IS_RUNNING(2) ) { // reschedule
   305             TMU_timers[2].timer_run = sh4r.slice_cycle;
   306             TMU_schedule_timer( 2 );
   307         }
   308         return;
   309     }
   310     MMIO_WRITE( TMU, reg, val );
   311 }
   313 void TMU_count_all( uint32_t nanosecs )
   314 {
   315     int tcr = MMIO_READ( TMU, TSTR );
   316     if( tcr & 0x01 ) {
   317         TMU_count( 0, nanosecs );
   318     }
   319     if( tcr & 0x02 ) {
   320         TMU_count( 1, nanosecs );
   321     }
   322     if( tcr & 0x04 ) {
   323         TMU_count( 2, nanosecs );
   324     }
   325 }
   327 void TMU_run_slice( uint32_t nanosecs )
   328 {
   329     TMU_count_all( nanosecs );
   330     TMU_timers[0].timer_run = 0;
   331     TMU_timers[1].timer_run = 0;
   332     TMU_timers[2].timer_run = 0;
   333 }
   335 void TMU_update_clocks()
   336 {
   337     TMU_set_timer_control( 0, MMIO_READ( TMU, TCR0 ) );
   338     TMU_set_timer_control( 1, MMIO_READ( TMU, TCR1 ) );
   339     TMU_set_timer_control( 2, MMIO_READ( TMU, TCR2 ) );
   340 }
   342 void TMU_reset( )
   343 {
   344     TMU_timers[0].timer_remainder = 0;
   345     TMU_timers[0].timer_run = 0;
   346     TMU_timers[1].timer_remainder = 0;
   347     TMU_timers[1].timer_run = 0;
   348     TMU_timers[2].timer_remainder = 0;
   349     TMU_timers[2].timer_run = 0;
   350     TMU_update_clocks();
   351 }
   353 void TMU_save_state( FILE *f ) {
   354     fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
   355 }
   357 int TMU_load_state( FILE *f ) 
   358 {
   359     fread( &TMU_timers, sizeof(TMU_timers), 1, f );
   360     return 0;
   361 }
.