Line data Source code
1 : #ifndef HEADER_fd_src_disco_fd_clock_tile_h 2 : #define HEADER_fd_src_disco_fd_clock_tile_h 3 : 4 : /* fd_clock_tile.h provides fd_clock convenience APIs for tiles */ 5 : 6 : #include "../tango/tempo/fd_tempo.h" 7 : #include "../util/clock/fd_clock.h" 8 : 9 : /* fd_clock_tile_t is a fast nanosecond clock source for Firedancer 10 : tiles (single-threaded usage). */ 11 : 12 : struct fd_clock_tile { 13 : fd_clock_shmem_t shmem[1]; 14 : fd_clock_t clock[1]; 15 : fd_clock_epoch_t epoch[1]; 16 : double inv_m; 17 : }; 18 : 19 : typedef struct fd_clock_tile fd_clock_tile_t; 20 : 21 : FD_PROTOTYPES_BEGIN 22 : 23 : /* fd_clock_tile_init creates a new fd_clock_tile_t. Assumes that the 24 : calling thread has pre-calibrated fd_tempo tick_per_ns value. */ 25 : 26 : static inline void 27 0 : fd_clock_tile_init( fd_clock_tile_t * clock ) { 28 : /* fdctl calibrates tick_per_ns on startup, no need to calibrate here */ 29 0 : double init_w = (double)fd_tempo_tick_per_ns( NULL ); 30 : 31 0 : long init_x0, init_y0; 32 0 : int clock_err = fd_clock_joint_read( _fd_tickcount, NULL, fd_log_wallclock_host, NULL, &init_x0, &init_y0, NULL ); 33 0 : if( FD_UNLIKELY( clock_err ) ) FD_LOG_ERR(( "fd_clock_joint_read failed (%i-%s)", clock_err, fd_clock_strerror( clock_err ) )); 34 : 35 0 : long recal_avg = 10e6L; /* 10ms */ 36 0 : void * shclock = fd_clock_new( clock->shmem, recal_avg, 0L, 0., 0., init_x0, init_y0, init_w ); 37 0 : if( FD_UNLIKELY( !shclock ) ) FD_LOG_ERR(( "fd_clock_new failed" )); 38 : 39 0 : fd_clock_t * clock_ptr = fd_clock_join( clock->clock, shclock, _fd_tickcount, NULL ); 40 0 : if( FD_UNLIKELY( !clock_ptr ) ) FD_LOG_ERR(( "fd_clock_join failed" )); 41 : 42 0 : fd_clock_epoch_init( clock->epoch, clock->shmem ); 43 0 : clock->inv_m = 1.0/clock->epoch->m; 44 0 : } 45 : 46 : /* fd_clock_tile_recal_next returns the wallclock deadline after which 47 : the next recal should be done. */ 48 : 49 : static inline long 50 0 : fd_clock_tile_recal_next( fd_clock_tile_t const * clock ) { 51 0 : return fd_clock_recal_next( clock->clock ); 52 0 : } 53 : 54 : /* fd_clock_tile_recal is called periodically to synchronize tickcount 55 : to log_wallclock. Returns the wallclock deadline after which the 56 : next recal should be done. */ 57 : 58 : static inline long 59 0 : fd_clock_tile_recal( fd_clock_tile_t * clock ) { 60 0 : long x; long y; 61 0 : int err = fd_clock_joint_read( _fd_tickcount, NULL, fd_log_wallclock_host, NULL, &x, &y, NULL ); 62 0 : if( FD_UNLIKELY( err ) ) { 63 : /* on recal fail, retry in 1ms */ 64 0 : FD_LOG_WARNING(( "fd_clock_joint_read failed (%i-%s)", err, fd_clock_strerror( err ) )); 65 0 : return ( clock->shmem->recal_next = fd_clock_epoch_y( clock->epoch, fd_tickcount() ) + 1000000L ); 66 0 : } 67 0 : long recal_next = fd_clock_recal( clock->clock, x, y ); 68 0 : fd_clock_epoch_refresh( clock->epoch, clock->shmem ); 69 0 : clock->inv_m = 1.0/clock->epoch->m; 70 0 : return recal_next; 71 0 : } 72 : 73 : /* fd_clock_tile_set re-anchors the clock to a caller-supplied 74 : wallclock. For harnesses driving a virtual timeline. */ 75 : 76 : static inline void 77 : fd_clock_tile_set( fd_clock_tile_t * clock, 78 0 : long now ) { 79 0 : fd_clock_step( clock->clock, fd_tickcount(), now, clock->epoch->w ); 80 0 : fd_clock_epoch_refresh( clock->epoch, clock->shmem ); 81 0 : clock->inv_m = 1.0/clock->epoch->m; 82 0 : } 83 : 84 : /* fd_clock_tile_now returns an approximation of fd_log_wallclock. */ 85 : 86 : static inline long 87 0 : fd_clock_tile_now( fd_clock_tile_t const * clock ) { 88 0 : return fd_clock_epoch_y( clock->epoch, fd_tickcount() ); 89 0 : } 90 : 91 : /* fd_clock_tile_recal_due returns 1 if a recalibration is due, else 0. */ 92 : 93 : static inline int 94 0 : fd_clock_tile_recal_due( fd_clock_tile_t * clock ) { 95 0 : return fd_clock_tile_now( clock ) >= fd_clock_tile_recal_next( clock ); 96 0 : } 97 : 98 : /* fd_clock_tile_tickcount_to_wallclock converts an fd_tickcount() 99 : sample to a fd_log_wallclock() estimate. */ 100 : 101 : static inline long 102 : fd_clock_tile_tickcount_to_wallclock( fd_clock_tile_t const * clock, 103 0 : long tickcount ) { 104 0 : return fd_clock_epoch_y( clock->epoch, tickcount ); 105 0 : } 106 : 107 : /* fd_clock_tile_wallclock_to_tickcount converts a fd_log_wallclock() 108 : value to the fd_tickcount() at which the clock would read it. */ 109 : 110 : static inline long 111 : fd_clock_tile_wallclock_to_tickcount( fd_clock_tile_t const * clock, 112 0 : long wallclock ) { 113 0 : fd_clock_epoch_t const * epoch = clock->epoch; 114 0 : return fd_clock_epoch_x0( epoch ) + (long)( (double)( wallclock-fd_clock_epoch_y0_eff( epoch ) )*clock->inv_m ); 115 0 : } 116 : 117 : /* fd_clock_tile_tickcomp_to_wallclock converts a compressed 118 : fd_tickcount() sample to a fd_log_wallclock() estimate. */ 119 : 120 : static inline long 121 : fd_clock_tile_tickcomp_to_wallclock( fd_clock_tile_t const * clock, 122 0 : ulong ts_comp ) { 123 0 : long ts = fd_frag_meta_ts_decomp( ts_comp, clock->epoch->x0 ); 124 0 : return fd_clock_tile_tickcount_to_wallclock( clock, ts ); 125 0 : } 126 : 127 : FD_PROTOTYPES_END 128 : 129 : #endif /* HEADER_fd_src_disco_fd_clock_tile_h */