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