uint32_t CycleTimerHelper::getCycleTimerTicks(uint64_t now) { uint32_t retval; struct compute_vars *my_vars; // get pointer and copy the contents // no locking should be needed since we have more than one // of these vars available, and our use will always be finished before // m_current_shadow_idx changes since this thread's priority should // be higher than the one of the writer thread. Even if not, we only have to ensure // that the used dataset is consistent. We can use an older dataset if it's consistent // since it will also provide a fairly decent extrapolation. my_vars = m_shadow_vars + m_current_shadow_idx; int64_t time_diff = now - my_vars->usecs; double y_step_in_ticks = ((double)time_diff) * my_vars->rate; int64_t y_step_in_ticks_int = (int64_t)y_step_in_ticks; uint64_t offset_in_ticks_int = my_vars->ticks; if (y_step_in_ticks_int > 0) { retval = addTicks(offset_in_ticks_int, y_step_in_ticks_int); /* debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "y_step_in_ticks_int > 0: %d, time_diff: %f, rate: %f, retval: %u\n", y_step_in_ticks_int, time_diff, my_vars.rate, retval);*/ } else { retval = substractTicks(offset_in_ticks_int, -y_step_in_ticks_int); // this can happen if the update thread was woken up earlier than it should have been /* debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "y_step_in_ticks_int <= 0: %d, time_diff: %f, rate: %f, retval: %u\n", y_step_in_ticks_int, time_diff, my_vars.rate, retval);*/ } return retval; }
/** * @brief Process the last input stored * * This function executes the last input stored by the function receiveInput * * @See receiveInput() */ void Object::executeLastInput() { char direction; unsigned char personDirection, state, ticks; state = getState(); ticks = stats->ss.ticks; direction = directionFromInput(lastInput); personDirection = getDirection(state); addTicks(); if(wasHitted(state)) { if(stats->ss.ticks >= stats->ss.hittedTicks) { changeState(state & 0x07); zeroTicks(); } breakMovement(); } else if(isMeleeAttacking(state)) { if(stats->ss.ticks >= stats->ss.meleeTicks) { changeState(state & 0x07); stats->ss.ticks = 0; } breakMovement(); } else if(isBreaking(state)) { if(stats->ps->dx == 0 && stats->ps->dy == 0) { changeState(state & 0x07); } breakMovement(); } else if(isMoving(state)) { if(direction == -3) { changeState(0x80 | personDirection); } else { movement(personDirection); } } else { breakMovement(); } lastInput = 0; }
/* * call with lock held */ bool CycleTimerHelper::initDLL() { uint32_t cycle_timer; uint64_t local_time; double bw_rel = m_dll_coeff_b / (DLL_SQRT2 * DLL_2PI); double bw_abs = bw_rel / (m_usecs_per_update / 1e6); if (bw_rel > 0.5) { double bw_max = 0.5 / (m_usecs_per_update / 1e6); debugWarning("Specified DLL bandwidth too high (%f > %f), reducing to max." " Increase the DLL update rate to increase the max DLL bandwidth\n", bw_abs, bw_max); bw_rel = 0.49; bw_abs = bw_rel / (m_usecs_per_update / 1e6); m_dll_coeff_b = bw_rel * (DLL_SQRT2 * DLL_2PI); m_dll_coeff_c = bw_rel * bw_rel * DLL_2PI * DLL_2PI; } if(!readCycleTimerWithRetry(&cycle_timer, &local_time, 10)) { debugError("Could not read cycle timer register\n"); return false; } #if DEBUG_EXTREME_ENABLE uint64_t cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer); #endif debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, " read : CTR: %11u, local: %17" PRIu64 "\n", cycle_timer, local_time); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " ctr : 0x%08X %11" PRIu64 " (%03us %04ucy %04uticks)\n", (uint32_t)cycle_timer, (uint64_t)cycle_timer_ticks, (unsigned int)TICKS_TO_SECS( (uint64_t)cycle_timer_ticks ), (unsigned int)TICKS_TO_CYCLES( (uint64_t)cycle_timer_ticks ), (unsigned int)TICKS_TO_OFFSET( (uint64_t)cycle_timer_ticks ) ); m_sleep_until = local_time + m_usecs_per_update; m_dll_e2 = m_ticks_per_update; m_current_time_usecs = local_time; m_next_time_usecs = m_current_time_usecs + m_usecs_per_update; m_current_time_ticks = CYCLE_TIMER_TO_TICKS( cycle_timer ); m_next_time_ticks = addTicks( (uint64_t)m_current_time_ticks, (uint64_t)m_dll_e2); debugOutput(DEBUG_LEVEL_VERBOSE, " (%p) First run\n", this); debugOutput(DEBUG_LEVEL_VERBOSE, " DLL bandwidth: %f Hz (rel: %f)\n", bw_abs, bw_rel); debugOutput(DEBUG_LEVEL_VERBOSE, " usecs/update: %u, ticks/update: %u, m_dll_e2: %f\n", m_usecs_per_update, m_ticks_per_update, m_dll_e2); debugOutput(DEBUG_LEVEL_VERBOSE, " usecs current: %f, next: %f\n", m_current_time_usecs, m_next_time_usecs); debugOutput(DEBUG_LEVEL_VERBOSE, " ticks current: %f, next: %f\n", m_current_time_ticks, m_next_time_ticks); return true; }
bool CycleTimerHelper::Execute() { debugOutput( DEBUG_LEVEL_ULTRA_VERBOSE, "Execute %p...\n", this); #ifdef DEBUG uint64_t now = m_Parent.getCurrentTimeAsUsecs(); int diff = now - m_last_loop_entry; if(diff < 100) { debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "(%p) short loop detected (%d usec), cnt: %d\n", this, diff, m_successive_short_loops); m_successive_short_loops++; if(m_successive_short_loops > 100) { debugError("Shutting down runaway thread\n"); return false; } } else { // reset the counter m_successive_short_loops = 0; } m_last_loop_entry = now; #endif if (!m_first_run) { // wait for the next update period //#if DEBUG_EXTREME_ENABLE #ifdef DEBUG ffado_microsecs_t now = Util::SystemTimeSource::getCurrentTimeAsUsecs(); int sleep_time = m_sleep_until - now; debugOutput( DEBUG_LEVEL_ULTRA_VERBOSE, "(%p) Sleep until %"PRId64"/%f (now: %"PRId64", diff=%d) ...\n", this, m_sleep_until, m_next_time_usecs, now, sleep_time); #endif Util::SystemTimeSource::SleepUsecAbsolute(m_sleep_until); debugOutput( DEBUG_LEVEL_ULTRA_VERBOSE, " (%p) back...\n", this); } else { // Since getCycleTimerTicks() is called below, // m_shadow_vars[m_current_shadow_idx] must contain valid data. On // the first run through, however, it won't because the contents of // m_shadow_vars[] are only set later on in this function. Thus // set up some vaguely realistic values to prevent unnecessary // delays when reading the cycle timer for the first time. struct compute_vars new_vars; new_vars.ticks = (uint64_t)(m_current_time_ticks); new_vars.usecs = (uint64_t)m_current_time_usecs; new_vars.rate = getRate(); m_shadow_vars[0] = new_vars; } uint32_t cycle_timer; uint64_t local_time; int64_t usecs_late; int ntries=10; uint64_t cycle_timer_ticks; int64_t err_ticks; bool not_good; // if the difference between the predicted value at readout time and the // actual value seems to be too large, retry reading the cycle timer // some host controllers return bogus values on some reads // (looks like a non-atomic update of the register) do { debugOutput( DEBUG_LEVEL_ULTRA_VERBOSE, "(%p) reading cycle timer register...\n", this); if(!readCycleTimerWithRetry(&cycle_timer, &local_time, 10)) { debugError("Could not read cycle timer register\n"); return false; } usecs_late = local_time - m_sleep_until; cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer); // calculate the CTR_TICKS we expect to read at "local_time" // then calculate the difference with what we actually read, // taking wraparound into account. If these deviate too much // from eachother then read the register again (bogus read). int64_t expected_ticks = getCycleTimerTicks(local_time); err_ticks = diffTicks(cycle_timer_ticks, expected_ticks); // check for unrealistic CTR reads (NEC controller does that sometimes) not_good = (-err_ticks > 1*TICKS_PER_CYCLE || err_ticks > 1*TICKS_PER_CYCLE); if(not_good) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) have to retry CTR read, diff unrealistic: diff: %"PRId64", max: +/- %u (try: %d) %"PRId64"\n", this, err_ticks, 1*TICKS_PER_CYCLE, ntries, expected_ticks); // sleep half a cycle to make sure the hardware moved on Util::SystemTimeSource::SleepUsecRelative(USECS_PER_CYCLE / 2); } } while(not_good && --ntries && !m_first_run && !m_unhandled_busreset); // grab the lock after sleeping, otherwise we can't be interrupted by // the busreset thread (lower prio) // also grab it after reading the CTR register such that the jitter between // wakeup and read is as small as possible Util::MutexLockHelper lock(*m_update_lock); // the difference between the measured and the expected time int64_t diff_ticks = diffTicks(cycle_timer_ticks, (int64_t)m_next_time_ticks); // // simulate a random scheduling delay between (0-10ms) // ffado_microsecs_t tmp = Util::SystemTimeSource::SleepUsecRandom(10000); // debugOutput( DEBUG_LEVEL_VERBOSE, " (%p) random sleep of %u usecs...\n", this, tmp); if(m_unhandled_busreset) { debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) Skipping DLL update due to unhandled busreset\n", this); m_sleep_until += m_usecs_per_update; // keep the thread running return true; } debugOutputExtreme( DEBUG_LEVEL_ULTRA_VERBOSE, " read : CTR: %11u, local: %17"PRIu64"\n", cycle_timer, local_time); debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, " ctr : 0x%08X %11"PRIu64" (%03us %04ucy %04uticks)\n", (uint32_t)cycle_timer, (uint64_t)cycle_timer_ticks, (unsigned int)TICKS_TO_SECS( (uint64_t)cycle_timer_ticks ), (unsigned int)TICKS_TO_CYCLES( (uint64_t)cycle_timer_ticks ), (unsigned int)TICKS_TO_OFFSET( (uint64_t)cycle_timer_ticks ) ); if (m_first_run) { if(!initDLL()) { debugError("(%p) Could not init DLL\n", this); return false; } m_first_run = false; } else if (diff_ticks > m_ticks_per_update * 20) { debugOutput(DEBUG_LEVEL_VERBOSE, "re-init dll due to too large tick diff: %"PRId64" >> %f\n", diff_ticks, (float)(m_ticks_per_update * 20)); if(!initDLL()) { debugError("(%p) Could not init DLL\n", this); return false; } } else { // calculate next sleep time m_sleep_until += m_usecs_per_update; // correct for the latency between the wakeup and the actual CTR // read. The only time we can trust is the time returned by the // CTR read kernel call, since that (should be) atomically read // together with the ctr register itself. // if we are usecs_late usecs late // the cycle timer has ticked approx ticks_late ticks too much // if we are woken up early (which shouldn't happen according to POSIX) // the cycle timer has ticked approx -ticks_late too little int64_t ticks_late = (usecs_late * TICKS_PER_SECOND) / 1000000LL; // the corrected difference between predicted and actual ctr // i.e. DLL error signal int64_t diff_ticks_corr; if (ticks_late >= 0) { diff_ticks_corr = diff_ticks - ticks_late; debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, "diff_ticks_corr=%"PRId64", diff_ticks = %"PRId64", ticks_late = %"PRId64"\n", diff_ticks_corr, diff_ticks, ticks_late); } else { debugError("Early wakeup, should not happen!\n"); // recover diff_ticks_corr = diff_ticks + ticks_late; } #ifdef DEBUG // makes no sense if not running realtime if(m_realtime && usecs_late > 1000) { debugOutput(DEBUG_LEVEL_VERBOSE, "Rather late wakeup: %"PRId64" usecs\n", usecs_late); } #endif // update the x-axis values m_current_time_ticks = m_next_time_ticks; // decide what coefficients to use // it should be ok to not do this in tick space // since diff_ticks_corr should not be near wrapping // (otherwise we are out of range. we need a few calls // w/o wrapping for this to work. That should not be // an issue as long as the update interval is smaller // than the wrapping interval.) // and coeff_b < 1, hence tmp is not near wrapping double diff_ticks_corr_d = (double)diff_ticks_corr; double step_ticks = (m_dll_coeff_b * diff_ticks_corr_d); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "diff_ticks_corr=%f, step_ticks=%f\n", diff_ticks_corr_d, step_ticks); // the same goes for m_dll_e2, which should be approx equal // to the ticks/usec rate (= 24.576) hence also not near // wrapping step_ticks += m_dll_e2; debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "add %f ticks to step_ticks => step_ticks=%f\n", m_dll_e2, step_ticks); // it can't be that we have to update to a value in the past if(step_ticks < 0) { debugError("negative step: %f! (correcting to nominal)\n", step_ticks); // recover to an estimated value step_ticks = (double)m_ticks_per_update; } if(step_ticks > TICKS_PER_SECOND) { debugWarning("rather large step: %f ticks (> 1sec)\n", step_ticks); } // now add the step ticks with wrapping. m_next_time_ticks = (double)(addTicks((uint64_t)m_current_time_ticks, (uint64_t)step_ticks)); // update the DLL state m_dll_e2 += m_dll_coeff_c * diff_ticks_corr_d; // update the y-axis values m_current_time_usecs = m_next_time_usecs; m_next_time_usecs += m_usecs_per_update; debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " usecs: current: %f next: %f usecs_late=%"PRId64" ticks_late=%"PRId64"\n", m_current_time_usecs, m_next_time_usecs, usecs_late, ticks_late); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " ticks: current: %f next: %f diff=%"PRId64"\n", m_current_time_ticks, m_next_time_ticks, diff_ticks); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " ticks: current: %011"PRIu64" (%03us %04ucy %04uticks)\n", (uint64_t)m_current_time_ticks, (unsigned int)TICKS_TO_SECS( (uint64_t)m_current_time_ticks ), (unsigned int)TICKS_TO_CYCLES( (uint64_t)m_current_time_ticks ), (unsigned int)TICKS_TO_OFFSET( (uint64_t)m_current_time_ticks ) ); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " ticks: next : %011"PRIu64" (%03us %04ucy %04uticks)\n", (uint64_t)m_next_time_ticks, (unsigned int)TICKS_TO_SECS( (uint64_t)m_next_time_ticks ), (unsigned int)TICKS_TO_CYCLES( (uint64_t)m_next_time_ticks ), (unsigned int)TICKS_TO_OFFSET( (uint64_t)m_next_time_ticks ) ); debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " state: local: %11"PRIu64", dll_e2: %f, rate: %f\n", local_time, m_dll_e2, getRate()); } // prepare the new compute vars struct compute_vars new_vars; new_vars.ticks = (uint64_t)(m_current_time_ticks); new_vars.usecs = (uint64_t)m_current_time_usecs; new_vars.rate = getRate(); // get the next index unsigned int next_idx = (m_current_shadow_idx + 1) % CTRHELPER_NB_SHADOW_VARS; // update the next index position m_shadow_vars[next_idx] = new_vars; // then we can update the current index m_current_shadow_idx = next_idx; #ifdef DEBUG // do some verification // we re-read a valid ctr timestamp // then we use the attached system time to calculate // the DLL generated timestamp and we check what the // difference is if(!readCycleTimerWithRetry(&cycle_timer, &local_time, 10)) { debugError("Could not read cycle timer register (verify)\n"); return true; // true since this is a check only } cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer); // only check when successful int64_t time_diff = local_time - new_vars.usecs; double y_step_in_ticks = ((double)time_diff) * new_vars.rate; int64_t y_step_in_ticks_int = (int64_t)y_step_in_ticks; uint64_t offset_in_ticks_int = new_vars.ticks; uint32_t dll_time; if (y_step_in_ticks_int > 0) { dll_time = addTicks(offset_in_ticks_int, y_step_in_ticks_int); } else { dll_time = substractTicks(offset_in_ticks_int, -y_step_in_ticks_int); } int32_t ctr_diff = cycle_timer_ticks-dll_time; debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "(%p) CTR DIFF: HW %010"PRIu64" - DLL %010u = %010d (%s)\n", this, cycle_timer_ticks, dll_time, ctr_diff, (ctr_diff>0?"lag":"lead")); #endif return true; }
enum StreamProcessor::eChildReturnValue RmeTransmitStreamProcessor::generateSilentPacketHeader ( unsigned char *data, unsigned int *length, unsigned char *tag, unsigned char *sy, uint32_t pkt_ctr ) { unsigned int cycle = CYCLE_TIMER_GET_CYCLES(pkt_ctr); debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "XMIT SILENT: CY=%04u, TSP=%011llu (%04u)\n", cycle, m_last_timestamp, ( unsigned int ) TICKS_TO_CYCLES ( m_last_timestamp ) ); // A "silent" packet is identical to a regular data packet except all // audio data is set to zero. The requirements of the silent packet // for RME devices is still being confirmed. // The number of events per packet expected by the RME is solely // dependent on the current sample rate. An 'event' is one sample from // all channels plus possibly other midi and control data. signed n_events = getNominalFramesPerPacket(); // Do housekeeping expected for all packets, even for packets containing // no audio data. *sy = 0x00; /* Assume the packet will be empty unless proven otherwise */ *length = 0; uint64_t presentation_time; unsigned int presentation_cycle; int cycles_until_presentation; uint64_t transmit_at_time; unsigned int transmit_at_cycle; int cycles_until_transmit; /* The sample buffer is not necessarily running when silent packets are * needed, so use m_last_timestamp (the timestamp of the previously sent * data packet) as the basis for the presentation time of the next * packet. Since we're only writing zeros we don't have to deal with * buffer xruns. */ float ticks_per_frame = m_Parent.getDeviceManager().getStreamProcessorManager().getSyncSource().getTicksPerFrame(); presentation_time = addTicks(m_last_timestamp, (unsigned int)lrintf(n_events * ticks_per_frame)); transmit_at_time = substractTicks(presentation_time, RME_TRANSMIT_TRANSFER_DELAY); presentation_cycle = (unsigned int)(TICKS_TO_CYCLES(presentation_time)); transmit_at_cycle = (unsigned int)(TICKS_TO_CYCLES(transmit_at_time)); cycles_until_presentation = diffCycles(presentation_cycle, cycle); cycles_until_transmit = diffCycles(transmit_at_cycle, cycle); if (cycles_until_transmit < 0) { // At this point we've theoretically missed the cycle at which // the data needs to be transmitted. Technically, if // cycles_until_presentation is greater than or equal to // RME_MIN_CYCLES_BEFORE_PRESENTATION we have an xrun. However, // since this is a silent packet there's no real harm in indicating // that a silent packet can still be sent; it's not as if a silent // packet consumes data. Furthermore, due to the fact that // presentation time is estimated from m_last_timestamp it's possible // that any xrun indication here is a false trigger. // // In any case, when silent packets are utilised during shutdown // an eCRV_XRun return is "invalid" (see StreamProcessor, function // getPacket(). Since silent packets are usually used during startup // and/or shutdown there's little to be gained by flagging xruns; all // they seem to do is prevent an otherwise clean shutdown. m_last_timestamp = presentation_time; m_tx_dbc += fillDataPacketHeader((quadlet_t *)data, length, m_last_timestamp); if (m_tx_dbc > 0xff) m_tx_dbc -= 0x100; return eCRV_Packet; } else if (cycles_until_transmit <= RME_MAX_CYCLES_TO_TRANSMIT_EARLY) { m_last_timestamp = presentation_time; m_tx_dbc += fillDataPacketHeader((quadlet_t *)data, length, m_last_timestamp); if (m_tx_dbc > 0xff) m_tx_dbc -= 0x100; return eCRV_Packet; } else { return eCRV_EmptyPacket; } return eCRV_Invalid; }