/* ** GENERAL NOTE: This timer module has two functions to use it. First is a ** designed as a single-use interval timer. This is convenient for a frame ** rate display. It is accessed through the function TMXframe_rate(). The ** second mode of functionality is a stack of timers. In this mode, a time ** is kept for each timer on the stack, but only the timer at the top of the ** stack is accumulating time. A push operation stops the current timer and ** creates a new one, pushing it on the stack. Initially, there is a timer ** on the stack, but it is not accessible since there it was not pushed on ** the stack. A function could be added to gather this data, of course. A ** pop operation returns the time accumulated by the timer on the top of the ** stack and removes it. This implicitly restarts the timer that becomes ** the new top of the stack. A push operation stop the current timer (and ** adds in the time), and creates a new timer as the new top of the stack, ** starting it. ** ** The current time is read directly from /dev/mmem, to which an open file ** descriptor is maintained. The file is memory-mapped so that the address ** retrieved initially is always correct. Details on this procedure are in ** the manual page for syssgi(2) under SGI_QUERY_CYCLECTR. The units are ** obtained from the syssgi() call, in picoseconds per cycle. This value is ** then used to convert all returned times into seconds. Time values read ** read the clock are in cycles. This method of getting the current time is ** significantly faster than gettimeofday(). ** ** ONE VERY IMPORTANT NOTE: In order not to take up a great deal of time, ** there is no policing of the order of calls. If you attempt to pop too ** many timers off the stack, the stack pointers will point into non-related ** data. Similarly, calling TMXframe_rate(), TMXpush(), or TMXpop() without ** calling TMXinit() will likely cause a segmentation violation or bus error, ** crashing your program. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "time.h" /* Mark's fix to Andrei's hack to make it compile under 6.2; */ #ifndef _PAGESZ #define _PAGESZ ( getpagesize( ) ) #endif /* These three variables are general information used for all timers */ static volatile unsigned long *iotimer_addr; /* hardware address of timer */ static int fd; /* file descriptor of mmem */ /* These variables are only for the general frame rate display */ static double sec_per_cyc; /* conversion factor for time */ static unsigned long last_frame_low = 0; /* time stamp last read */ /* These variables are for the stack of timers */ static unsigned long *acc_time; /* Total time attributed to this timer */ static unsigned long start_time; /* Time the current timer was started */ static unsigned long *cur_time; /* pointer to active timer */ /* This variable is for the time stamp function */ static unsigned long time_zero; /* Time the timer was initialized */ /* ** General initialization ** The argument is the number of timers that should be created on the stack. */ void TMXinit( int n ) { unsigned long *time_a; unsigned int phys_addr, raddr, cycleval; inventory_t *iv; /* The following code is virtually copied from the syssgi(2) man page */ /* Get the hardware address and cycle time (in picoseconds) */ phys_addr = syssgi( SGI_QUERY_CYCLECNTR, &cycleval ); if( phys_addr == ENODEV ) { fprintf( stderr, "TMXinit: Sorry, hardware counter not supported\n" ); return; } /* Mask the address to be page-aligned */ raddr = ( phys_addr & ~POFFMASK ); /* Open the pseudo-kernel memory /dev/mmem */ fd = open( "/dev/mmem", O_RDONLY ); /* Map the page-aligned address */ time_a = (unsigned long *) mmap( 0, POFFMASK, PROT_READ, MAP_PRIVATE, fd, (int) raddr ); /* Add back the offset to get the true address */ time_a = (unsigned long *) ( (unsigned long) time_a + poff( phys_addr ) ); /* Assign it to the variable visible throughout the module */ iotimer_addr = (unsigned long *) time_a; /* Now, try to figure out what length of timer we have, and adjust the ** address accordingly - i.e. add one for a 64-bit timer to use only the ** low-order 32 bits. The constants for the inv_state field are in ** invent.h and get reported by "uname -m" (Unix command). */ setinvent( ); for( iv = getinvent( ); iv->inv_class != INV_PROCESSOR || iv->inv_type != INV_CPUBOARD; iv = getinvent( ) ) /*EMPTY*/; if( iv == NULL ) { printf( "TMXinit(): unable to determine CPU type, assuming 64-bit\n" ); } else { switch( iv->inv_state ) { case INV_IP22BOARD: /* UNC: indigo[12], lysine, csg, lionfish, noyce */ break; /* 32-bit timer */ case INV_IP19BOARD: /* UNC: coons, histidine */ iotimer_addr++; /* 64-bit timer, using only low-order 32 bits */ break; case INV_IP21BOARD: /* UNC: milli - don't know what length */ default: printf( "TMXinit(): unknown CPU type (%d), assuming 64-bit timer\n", iv->inv_state ); iotimer_addr++; break; } /* end switch */ } endinvent( ); /* Now the functionality that we desire */ /* Timer stack initialization */ acc_time = (unsigned long *) malloc( n * sizeof( unsigned long ) ); if( acc_time == NULL ) { fprintf( stderr, "ERROR: Unable to initialize timer.\n" ); fprintf( stderr, " The program should crash now.\n"); } bzero( (void *) acc_time, n * sizeof( unsigned long ) ); cur_time = acc_time; /* Frame rate timer initialization */ /* Compute the number of cycles per second. This will be divided by the ** number of cycles to return Hertz. */ sec_per_cyc = (double) cycleval / 1e+12; /* Prime the pump for the frame timer, timer stack, and the time stamp */ time_zero = start_time = last_frame_low = *( iotimer_addr ); return; } /* Clean up */ void TMXdestroy( void ) { close( fd ); if( acc_time != NULL ) { free( acc_time ); acc_time = NULL; } return; } /* ** Stop the current timer. ** Create a new one. ** Push the new one on the stack and start it. */ void TMXpush( void ) { unsigned long timestamp; /* Stop the previous timer and record the time */ timestamp = *( iotimer_addr ); *cur_time += timestamp - start_time; /* Initialize the new timer */ cur_time++; *cur_time = 0; /* Start (time stamp) the new timer */ start_time = timestamp; return; } double TMXpop( void ) { unsigned long timestamp; double elapsed; /* Stop the timer and compute elapsed time in seconds */ timestamp = *( iotimer_addr ); *cur_time += timestamp - start_time; elapsed = *cur_time * sec_per_cyc; /* Point to the previous timer and restart it (new time stamp) */ cur_time--; start_time = timestamp; return elapsed; } double TMXframeRate( double *seconds ) { unsigned long low; double fr = 0.0; low = *( iotimer_addr ); *seconds = (double) ( low - last_frame_low ) * sec_per_cyc; fr = 1.0 / *seconds; last_frame_low = low; return fr; } void TMXsetMarker( void ) { time_zero = *( iotimer_addr ); return; } double TMXtimeStamp( void ) { double t; t = (double) ( *( iotimer_addr ) - time_zero ) * sec_per_cyc; return t; }

AltStyle によって変換されたページ (->オリジナル) /