rxts_calibrator.c 7.16 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * This work is part of the White Rabbit project
 *
 * Copyright (C) 2012 CERN (www.cern.ch)
 * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
 *
 * Released according to the GNU GPL, version 2 or any later version.
 */
#include <stdio.h>
#include <inttypes.h>
#include <stdarg.h>
#include <wrc.h>

#include "board.h"
#include "syscon.h"
#include "endpoint.h"
#include "softpll_ng.h"
18
#include "wrc_ptp.h"
19
#include "storage.h"
20
#include "ptpd_netif.h"
21
#include "rxts_calibrator.h"
22

23 24 25
/* New calibrator for the transition phase value. A major pain in the ass for
   the folks who frequently rebuild their gatewares. The idea is described
   below:
26 27
   - lock the PLL to the master
   - scan the whole phase shifter range
28 29 30
   - at each scanning step, generate a fake RX timestamp.
   - check if the rising edge counter is ahead of the falling edge counter
     (added a special bit for it in the TSU).
31
   - determine phases at which positive/negative transitions occur
32 33
   - transition phase value is in the middle between the rising and falling
     edges.
34
   
35 36 37 38
   This calibration procedure is fast enough to be run on slave nodes whenever
   the link goes up. For master mode, the core must be run at least once as a
   slave to calibrate itself and store the current transition phase value in
   the EEPROM.
39 40
*/

41 42
/* how finely we scan the phase shift range to determine where we have the bit
 * flip */
43 44
#define CAL_SCAN_STEP 100

45 46
/* deglitcher threshold (to remove 1->0->1 flip bit glitches that might occur
   due to jitter) */
47 48
#define CAL_DEGLITCH_THRESHOLD 5

49 50 51 52
/* we scan at least one clock period to look for rising->falling edge transition
   plus some headroom */
#define CAL_SCAN_RANGE (REF_CLOCK_PERIOD_PS + \
		(3 * CAL_DEGLITCH_THRESHOLD * CAL_SCAN_STEP))
53 54

#define TD_WAIT_INACTIVE	0
55 56
#define TD_GOT_TRANSITION	1
#define TD_DONE			2
57

58 59 60
/* Number of retries for rxts_calibration_update
 * value found experimentally */
#define CALIB_RETRIES 1000
61 62


63 64 65 66 67 68 69 70
/* state of transition detector */
struct trans_detect_state {
	int prev_val;
	int sample_count;
	int state;
	int trans_phase;
};

71 72 73 74
/* finds the transition in the value of flip_bit and returns phase associated
   with it. If no transition phase has been found yet, returns 0. Non-zero
   polarity means we are looking for positive transitions, 0 - negative
   transitions */
75 76 77 78 79 80 81 82
static int lookup_transition(struct trans_detect_state *state, int flip_bit,
			     int phase, int polarity)
{
	if (polarity)
		polarity = 1;

	switch (state->state) {
	case TD_WAIT_INACTIVE:
83 84
		/* first, wait until we have at least CAL_DEGLITCH_THRESHOLD of
		   inactive state samples */
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
		if (flip_bit != polarity)
			state->sample_count++;
		else
			state->sample_count = 0;

		if (state->sample_count >= CAL_DEGLITCH_THRESHOLD) {
			state->state = TD_GOT_TRANSITION;
			state->sample_count = 0;
		}

		break;

	case TD_GOT_TRANSITION:
		if (flip_bit != polarity)
			state->sample_count = 0;
		else {
			state->sample_count++;
			if (state->sample_count >= CAL_DEGLITCH_THRESHOLD) {
				state->state = TD_DONE;
				state->trans_phase =
				    phase -
				    CAL_DEGLITCH_THRESHOLD * CAL_SCAN_STEP;
			}
		}
		break;

	case TD_DONE:
		return 1;
		break;
	}
	return 0;
}

static struct trans_detect_state det_rising, det_falling;
static int cal_cur_phase;

121 122 123
/* Starts RX timestamper calibration process state machine. Invoked by
   ptpnetif's check lock function when the PLL has already locked, to avoid
   complicating the API of ptp-noposix/ppsi. */
124

125
void rxts_calibration_start(void)
126 127 128 129
{
	cal_cur_phase = 0;
	det_rising.prev_val = det_falling.prev_val = -1;
	det_rising.state = det_falling.state = TD_WAIT_INACTIVE;
130 131 132 133
	det_rising.sample_count = 0;
	det_falling.sample_count = 0;
	det_rising.trans_phase = 0;
	det_falling.trans_phase = 0;
134 135 136
	spll_set_phase_shift(0, 0);
}

137 138 139
/* Updates RX timestamper state machine. Non-zero return value means that
   calibration is done. */
int rxts_calibration_update(uint32_t *t24p_value)
140
{
141
	int32_t ttrans = 0;
142

143 144 145
	if (spll_shifter_busy(0))
		return 0;

146 147
	/* generate a fake RX timestamp and check if falling edge counter is
	   ahead of rising edge counter */
148 149 150 151 152 153 154
	int flip = ep_timestamper_cal_pulse();

	/* look for transitions (with deglitching) */
	lookup_transition(&det_rising, flip, cal_cur_phase, 1);
	lookup_transition(&det_falling, flip, cal_cur_phase, 0);

	if (cal_cur_phase >= CAL_SCAN_RANGE) {
155 156
		if (det_rising.state != TD_DONE || det_falling.state != TD_DONE) 
		{
157
			wrc_verbose("RXTS calibration error.\n");
158 159 160 161
			return -1;
		}

		/* normalize */
162 163 164 165 166 167
		while (det_falling.trans_phase >= REF_CLOCK_PERIOD_PS)
			det_falling.trans_phase -= REF_CLOCK_PERIOD_PS;
		while (det_rising.trans_phase >= REF_CLOCK_PERIOD_PS)
			det_rising.trans_phase -= REF_CLOCK_PERIOD_PS;

		/* Use falling edge as second sample of rising edge */
168 169 170 171
		if (det_falling.trans_phase > det_rising.trans_phase)
			ttrans = det_falling.trans_phase - REF_CLOCK_PERIOD_PS/2;
		else if(det_falling.trans_phase < det_rising.trans_phase)
			ttrans = det_falling.trans_phase + REF_CLOCK_PERIOD_PS/2;
172 173 174
		ttrans += det_rising.trans_phase;
		ttrans /= 2;

175 176 177 178
		/*normalize ttrans*/
		if(ttrans < 0) ttrans += REF_CLOCK_PERIOD_PS;
		if(ttrans >= REF_CLOCK_PERIOD_PS) ttrans -= REF_CLOCK_PERIOD_PS;

179

180
		wrc_verbose("RXTS calibration: R@%dps, F@%dps, transition@%dps\n",
181 182
			  det_rising.trans_phase, det_falling.trans_phase,
			  ttrans);
183

184
		*t24p_value = (uint32_t)ttrans;
185 186 187 188 189 190 191 192 193 194 195
		return 1;
	}

	cal_cur_phase += CAL_SCAN_STEP;

	spll_set_phase_shift(0, cal_cur_phase);

	return 0;
}

/* legacy function for 'calibration force' command */
196
int measure_t24p(uint32_t *value)
197 198
{
	int rv;
199
	pp_printf("Waiting for link...\n");
200
	while (!ep_link_up(NULL))
201
		timer_delay_ms(100);
202 203

	spll_init(SPLL_MODE_SLAVE, 0, 1);
204
	pp_printf("Locking PLL...\n");
205
	while (!spll_check_lock(0))
206
		timer_delay_ms(100);
207
	pp_printf("\n");
208

209
	pp_printf("Calibrating RX timestamper...\n");
210 211 212 213 214
	rxts_calibration_start();

	while (!(rv = rxts_calibration_update(value))) ;
	return rv;
}
215

216 217
/* Delays for master must have been calibrated while running as slave */
static int calib_t24p_master(uint32_t *value)
218 219 220
{
	int rv;

221
	rv = storage_phtrans(value, 0);
222
	if(rv < 0) {
223
		pp_printf("Error %d while reading t24p from storage\n", rv);
224
		return rv;
225
	}
226
	pp_printf("t24p read from storage: %d ps\n", *value);
227
	return rv;
228 229
}

230 231 232

/*SoftPLL must be locked prior calling this function*/
static int calib_t24p_slave(uint32_t *value)
233 234
{
	int rv;
235
	uint32_t prev;
236
	int retries = 0;
237 238

	while (!(rv = rxts_calibration_update(value))) {
239
		if (retries > CALIB_RETRIES || ep_link_up(NULL) == LINK_DOWN)
240
			return -1;
241
 		retries++;
242
	}
243 244 245 246
	if (rv < 0) {
		/* Fall back on master == eeprom-or-error */
		return calib_t24p_master(value);
	}
247

248 249 250 251
	/*
	 * Let's see if we have a matching value in EEPROM:
	 * accept a 200ps difference, otherwise rewrite eeprom
	 */
252
	rv = storage_phtrans(&prev, 0 /* rd */);
253
	if (rv < 0 || (prev < *value - 200) || (prev > *value + 200)) {
254
		rv = storage_phtrans(value, 1);
255 256 257
		pp_printf("Wrote new t24p value: %d ps (%s)\n", *value,
			  rv < 0 ? "Failed" : "Success");
	}
258
	return 0;
259 260
}

261
int calib_t24p(int mode, uint32_t *value)
262
{
263 264
	int ret;

265
	if (mode == WRC_MODE_SLAVE)
266
		ret = calib_t24p_slave(value);
267
	else
268 269 270
		ret = calib_t24p_master(value);

	//update phtrans value in socket struct
271 272
	if (ret >= 0)
		ptpd_netif_set_phase_transition(*value);
273
	return ret;
274
}