filename | src/sh4/timer.c |
changeset | 1127:4b8194e3974c |
prev | 1124:aacaae9812ea |
author | nkeynes |
date | Sun Mar 04 21:28:48 2012 +1000 (12 years ago) |
permissions | -rw-r--r-- |
last change | Fix have_shaders in glrender.c Set negative fog for lut when using shaders |
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 * 1000 / SH4_BASE_RATE;
47 MMIO_REGION_READ_FN( CPG, reg )
48 {
49 return MMIO_READ( CPG, reg&0xFFF );
50 }
51 MMIO_REGION_READ_DEFSUBFNS(CPG)
53 /* CPU + bus dividers (note officially only the first 6 values are valid) */
54 int ifc_divider[8] = { 1, 2, 3, 4, 5, 8, 8, 8 };
55 /* Peripheral clock dividers (only first 5 are officially valid) */
56 int pfc_divider[8] = { 2, 3, 4, 6, 8, 8, 8, 8 };
58 MMIO_REGION_WRITE_FN( CPG, reg, val )
59 {
60 uint32_t div;
61 uint32_t primary_clock = sh4_input_freq;
62 reg &= 0xFFF;
63 switch( reg ) {
64 case FRQCR: /* Frequency control */
65 if( (val & FRQCR_PLL1EN) == 0 )
66 primary_clock /= 6;
67 div = ifc_divider[(val >> 6) & 0x07];
68 sh4_cpu_freq = primary_clock / div;
69 sh4_cpu_period = sh4_cpu_multiplier * div / sh4_input_freq;
70 div = ifc_divider[(val >> 3) & 0x07];
71 sh4_bus_freq = primary_clock / div;
72 sh4_bus_period = 1000 * div / sh4_input_freq;
73 div = pfc_divider[val & 0x07];
74 sh4_peripheral_freq = primary_clock / div;
75 sh4_peripheral_period = 1000 * div / sh4_input_freq;
77 /* Update everything that depends on the peripheral frequency */
78 SCIF_update_line_speed();
79 break;
80 case WTCSR: /* Watchdog timer */
81 break;
82 }
84 MMIO_WRITE( CPG, reg, val );
85 }
87 /**
88 * We don't really know what the default reset value is as it's determined
89 * by the mode select pins. This is the standard value that the BIOS sets,
90 * however, so it works for now.
91 */
92 void CPG_reset( )
93 {
94 mmio_region_CPG_write( FRQCR, 0x0E0A );
95 }
98 /********************************** RTC *************************************/
100 uint32_t rtc_output_period;
102 MMIO_REGION_READ_FN( RTC, reg )
103 {
104 return MMIO_READ( RTC, reg &0xFFF );
105 }
106 MMIO_REGION_READ_DEFSUBFNS(RTC)
108 MMIO_REGION_WRITE_FN( RTC, reg, val )
109 {
110 MMIO_WRITE( RTC, reg &0xFFF, val );
111 }
113 /********************************** TMU *************************************/
115 #define TCR_ICPF 0x0200
116 #define TCR_UNF 0x0100
117 #define TCR_UNIE 0x0020
119 #define TCR_IRQ_ACTIVE (TCR_UNF|TCR_UNIE)
121 #define TMU_IS_RUNNING(timer) (MMIO_READ(TMU,TSTR) & (1<<timer))
123 struct TMU_timer {
124 uint32_t timer_period;
125 uint32_t timer_remainder; /* left-over cycles from last count */
126 uint32_t timer_run; /* cycles already run from this slice */
127 };
129 static struct TMU_timer TMU_timers[3];
131 uint32_t TMU_count( int timer, uint32_t nanosecs );
132 void TMU_schedule_timer( int timer );
134 void TMU_event_callback( int eventid )
135 {
136 TMU_count( eventid - EVENT_TMU0, sh4r.slice_cycle );
137 assert( MMIO_READ( TMU, TCR0 + (eventid - EVENT_TMU0)*12 ) & 0x100 );
138 }
140 void TMU_init(void)
141 {
142 register_event_callback( EVENT_TMU0, TMU_event_callback );
143 register_event_callback( EVENT_TMU1, TMU_event_callback );
144 register_event_callback( EVENT_TMU2, TMU_event_callback );
145 }
147 void TMU_dump(unsigned timer)
148 {
149 fprintf(stderr, "Timer %d: %s %08x/%08x %dns run: %08X - %08X\n",
150 timer, TMU_IS_RUNNING(timer) ? "running" : "stopped",
151 MMIO_READ(TMU, TCNT0 + (timer*12)), MMIO_READ(TMU, TCOR0 + (timer*12)),
152 TMU_timers[timer].timer_period,
153 TMU_timers[timer].timer_run,
154 TMU_timers[timer].timer_remainder );
155 }
158 void TMU_set_timer_control( int timer, int tcr )
159 {
160 uint32_t period = 1;
161 uint32_t oldtcr = MMIO_READ( TMU, TCR0 + (12*timer) );
163 if( (oldtcr & TCR_UNF) == 0 ) {
164 tcr = tcr & (~TCR_UNF);
165 } else {
166 if( ((oldtcr & TCR_UNIE) == 0) &&
167 (tcr & TCR_IRQ_ACTIVE) == TCR_IRQ_ACTIVE ) {
168 intc_raise_interrupt( INT_TMU_TUNI0 + timer );
169 } else if( (oldtcr & TCR_UNIE) != 0 &&
170 (tcr & TCR_IRQ_ACTIVE) != TCR_IRQ_ACTIVE ) {
171 intc_clear_interrupt( INT_TMU_TUNI0 + timer );
172 }
173 }
175 switch( tcr & 0x07 ) {
176 case 0:
177 period = sh4_peripheral_period << 2 ;
178 break;
179 case 1:
180 period = sh4_peripheral_period << 4;
181 break;
182 case 2:
183 period = sh4_peripheral_period << 6;
184 break;
185 case 3:
186 period = sh4_peripheral_period << 8;
187 break;
188 case 4:
189 period = sh4_peripheral_period << 10;
190 break;
191 case 5:
192 /* Illegal value. */
193 ERROR( "TMU %d period set to illegal value (5)", timer );
194 period = sh4_peripheral_period << 12; /* for something to do */
195 break;
196 case 6:
197 period = rtc_output_period;
198 break;
199 case 7:
200 /* External clock... Hrm? */
201 period = sh4_peripheral_period; /* I dunno... */
202 break;
203 }
205 if( period != TMU_timers[timer].timer_period ) {
206 if( TMU_IS_RUNNING(timer) ) {
207 /* If we're changing clock speed while counting, sync up and reschedule */
208 TMU_count(timer, sh4r.slice_cycle);
209 TMU_timers[timer].timer_period = period;
210 TMU_schedule_timer(timer);
211 } else {
212 TMU_timers[timer].timer_period = period;
213 }
214 }
216 MMIO_WRITE( TMU, TCR0 + (12*timer), tcr );
217 }
219 void TMU_schedule_timer( int timer )
220 {
221 uint64_t duration = ((uint64_t)((uint32_t)(MMIO_READ( TMU, TCNT0 + 12*timer )))+1) *
222 (uint64_t)TMU_timers[timer].timer_period - TMU_timers[timer].timer_remainder;
223 event_schedule_long( EVENT_TMU0+timer, (uint32_t)(duration / 1000000000),
224 (uint32_t)(duration % 1000000000) );
225 // if( timer == 2 ) {
226 // WARN( "Schedule timer %d: %lldns", timer, duration );
227 // TMU_dump(timer);
228 // }
229 }
231 void TMU_start( int timer )
232 {
233 TMU_timers[timer].timer_run = sh4r.slice_cycle;
234 TMU_timers[timer].timer_remainder = 0;
235 TMU_schedule_timer( timer );
236 }
238 /**
239 * Stop the given timer. Run it up to the current time and leave it there.
240 */
241 void TMU_stop( int timer )
242 {
243 TMU_count( timer, sh4r.slice_cycle );
244 event_cancel( EVENT_TMU0+timer );
245 }
247 /**
248 * Count the specified timer for a given number of nanoseconds.
249 */
250 uint32_t TMU_count( int timer, uint32_t nanosecs )
251 {
252 uint32_t run_ns = nanosecs + TMU_timers[timer].timer_remainder -
253 TMU_timers[timer].timer_run;
254 TMU_timers[timer].timer_remainder =
255 run_ns % TMU_timers[timer].timer_period;
256 TMU_timers[timer].timer_run = nanosecs;
257 uint32_t count = run_ns / TMU_timers[timer].timer_period;
258 uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer );
259 uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer );
260 // if( timer == 2 )
261 // WARN( "Counting timer %d: %d ns, %d ticks", timer, run_ns, count );
262 if( count > value ) {
263 uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer );
264 tcr |= TCR_UNF;
265 count -= value;
266 value = reset - (count % reset) + 1;
267 MMIO_WRITE( TMU, TCR0 + 12*timer, tcr );
268 if( tcr & TCR_UNIE )
269 intc_raise_interrupt( INT_TMU_TUNI0 + timer );
270 MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
271 // if( timer == 2 )
272 // WARN( "Underflowed timer %d", timer );
273 TMU_schedule_timer(timer);
274 } else {
275 value -= count;
276 MMIO_WRITE( TMU, TCNT0 + 12*timer, value );
277 }
278 return value;
279 }
281 MMIO_REGION_READ_FN( TMU, reg )
282 {
283 reg &= 0xFFF;
284 switch( reg ) {
285 case TCNT0:
286 if( TMU_IS_RUNNING(0) )
287 TMU_count( 0, sh4r.slice_cycle );
288 break;
289 case TCNT1:
290 if( TMU_IS_RUNNING(1) )
291 TMU_count( 1, sh4r.slice_cycle );
292 break;
293 case TCNT2:
294 if( TMU_IS_RUNNING(2) )
295 TMU_count( 2, sh4r.slice_cycle );
296 break;
297 }
298 return MMIO_READ( TMU, reg );
299 }
300 MMIO_REGION_READ_DEFSUBFNS(TMU)
303 MMIO_REGION_WRITE_FN( TMU, reg, val )
304 {
305 uint32_t oldval;
306 int i;
307 reg &= 0xFFF;
308 switch( reg ) {
309 case TSTR:
310 oldval = MMIO_READ( TMU, TSTR );
311 for( i=0; i<3; i++ ) {
312 uint32_t tmp = 1<<i;
313 if( (oldval & tmp) != 0 && (val&tmp) == 0 )
314 TMU_stop(i);
315 else if( (oldval&tmp) == 0 && (val&tmp) != 0 )
316 TMU_start(i);
317 }
318 break;
319 case TCR0:
320 TMU_set_timer_control( 0, val );
321 return;
322 case TCR1:
323 TMU_set_timer_control( 1, val );
324 return;
325 case TCR2:
326 TMU_set_timer_control( 2, val );
327 return;
328 case TCNT0:
329 MMIO_WRITE( TMU, reg, val );
330 if( TMU_IS_RUNNING(0) ) { // reschedule
331 TMU_timers[0].timer_run = sh4r.slice_cycle;
332 TMU_schedule_timer( 0 );
333 }
334 return;
335 case TCNT1:
336 MMIO_WRITE( TMU, reg, val );
337 if( TMU_IS_RUNNING(1) ) { // reschedule
338 TMU_timers[1].timer_run = sh4r.slice_cycle;
339 TMU_schedule_timer( 1 );
340 }
341 return;
342 case TCNT2:
343 MMIO_WRITE( TMU, reg, val );
344 if( TMU_IS_RUNNING(2) ) { // reschedule
345 TMU_timers[2].timer_run = sh4r.slice_cycle;
346 TMU_schedule_timer( 2 );
347 }
348 return;
349 }
350 MMIO_WRITE( TMU, reg, val );
351 }
353 void TMU_count_all( uint32_t nanosecs )
354 {
355 int tcr = MMIO_READ( TMU, TSTR );
356 if( tcr & 0x01 ) {
357 TMU_count( 0, nanosecs );
358 }
359 if( tcr & 0x02 ) {
360 TMU_count( 1, nanosecs );
361 }
362 if( tcr & 0x04 ) {
363 TMU_count( 2, nanosecs );
364 }
365 }
367 void TMU_run_slice( uint32_t nanosecs )
368 {
369 TMU_count_all( nanosecs );
370 TMU_timers[0].timer_run = 0;
371 TMU_timers[1].timer_run = 0;
372 TMU_timers[2].timer_run = 0;
373 }
375 void TMU_update_clocks()
376 {
377 TMU_set_timer_control( 0, MMIO_READ( TMU, TCR0 ) );
378 TMU_set_timer_control( 1, MMIO_READ( TMU, TCR1 ) );
379 TMU_set_timer_control( 2, MMIO_READ( TMU, TCR2 ) );
380 }
382 void TMU_reset( )
383 {
384 TMU_timers[0].timer_remainder = 0;
385 TMU_timers[0].timer_run = 0;
386 TMU_timers[1].timer_remainder = 0;
387 TMU_timers[1].timer_run = 0;
388 TMU_timers[2].timer_remainder = 0;
389 TMU_timers[2].timer_run = 0;
390 TMU_update_clocks();
391 }
393 void TMU_save_state( FILE *f ) {
394 fwrite( &TMU_timers, sizeof(TMU_timers), 1, f );
395 }
397 int TMU_load_state( FILE *f )
398 {
399 fread( &TMU_timers, sizeof(TMU_timers), 1, f );
400 return 0;
401 }
.