From fea9d9e0562d37afeb8f41a799620407667d0e8d Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 23 Sep 2010 21:05:18 +0200 Subject: [WIP] Change TPU offset on TS change or sync change When TPU offset must be changed (assignment to different timeslot or handover to a different cell), all tasks will be disabled until new TPU offset has been applied. Currently scheduled tasks are finished before the new TPU offset is applied. On change of TPU offset, the TPU's interrupt may skip one frame when changing backwards in time. Also it may generate two interrupts when changing significantly forward in time. This is compensated by changing the GSM time. Change-Id: If858484a9cf497e0f6e8d84593ab3637c2668869 --- src/target/firmware/include/layer1/sync.h | 4 ++- src/target/firmware/layer1/l23_api.c | 14 +++++++-- src/target/firmware/layer1/mframe_sched.c | 22 ++++++++----- src/target/firmware/layer1/prim_rach.c | 4 +++ src/target/firmware/layer1/sync.c | 51 ++++++++++++++++++++++++++++--- src/target/firmware/layer1/tpu_window.c | 20 +++++------- 6 files changed, 88 insertions(+), 27 deletions(-) diff --git a/src/target/firmware/include/layer1/sync.h b/src/target/firmware/include/layer1/sync.h index 3565ee20..7ce87566 100644 --- a/src/target/firmware/include/layer1/sync.h +++ b/src/target/firmware/include/layer1/sync.h @@ -72,7 +72,9 @@ struct l1s_state { /* The current TPU offset register */ uint32_t tpu_offset; - int32_t tpu_offset_correction; + int32_t tpu_offset_correction; /* used for TOA adjustment */ + int32_t tpu_offset_shift; /* used for TS or sync change */ + int32_t tpu_offset_changed; /* offset changed at last fiq */ /* TX parameters */ int8_t ta; diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c index daffaf8b..c7849aeb 100644 --- a/src/target/firmware/layer1/l23_api.c +++ b/src/target/firmware/layer1/l23_api.c @@ -235,9 +235,11 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg) struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data; struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data; struct l1ctl_dm_est_req *est_req = (struct l1ctl_dm_est_req *) ul->payload; + uint8_t old_tn; - printd("L1CTL_DM_EST_REQ (arfcn=%u, chan_nr=0x%02x, tsc=%u)\n", - ntohs(est_req->h0.band_arfcn), ul->chan_nr, est_req->tsc); + printd("L1CTL_DM_EST_REQ (arfcn=%u, chan_nr=0x%02x, tsc=%u tn=%u)\n", + ntohs(est_req->h0.band_arfcn), ul->chan_nr, est_req->tsc, + ul->chan_nr & 0x7); /* disable neighbour cell measurement of C0 TS 0 */ mframe_disable(MF_TASK_NEIGH_PM51_C0T0); @@ -245,6 +247,7 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg) /* configure dedicated channel state */ l1s.dedicated.type = chan_nr2dchan_type(ul->chan_nr); l1s.dedicated.tsc = est_req->tsc; + old_tn = l1s.dedicated.tn; l1s.dedicated.tn = ul->chan_nr & 0x7; l1s.dedicated.h = est_req->h; @@ -274,6 +277,13 @@ static void l1ctl_rx_dm_est_req(struct msgb *msg) /* figure out which MF tasks to enable */ l1a_mftask_set(chan_nr2mf_task_mask(ul->chan_nr, NEIGH_MODE_PM)); + + /* shift TPU according to chnage in TN */ + if (l1s.dedicated.tn != old_tn) { + l1s.tpu_offset_shift += (l1s.dedicated.tn - old_tn) * 625; + printf("Shift TPU by %d TN (%d qbits)\n", + l1s.dedicated.tn - old_tn, l1s.tpu_offset_shift); + } } /* receive a L1CTL_DM_FREQ_REQ from L23 */ diff --git a/src/target/firmware/layer1/mframe_sched.c b/src/target/firmware/layer1/mframe_sched.c index 7fa38c13..089ca8ed 100644 --- a/src/target/firmware/layer1/mframe_sched.c +++ b/src/target/firmware/layer1/mframe_sched.c @@ -448,7 +448,7 @@ static void mframe_schedule_set(enum mframe_task task_id) unsigned int trigger = si->frame_nr % si->modulo; unsigned int current = (l1s.current_time.fn + SCHEDULE_AHEAD) % si->modulo; if (current == trigger) { - uint32_t fn; + uint32_t fn, fn_diff; int rv; /* Schedule the set */ @@ -459,8 +459,9 @@ static void mframe_schedule_set(enum mframe_task task_id) /* Compute the next safe time to queue a DSP command */ fn = l1s.current_time.fn; ADD_MODULO(fn, rv - 2, GSM_MAX_FN); /* -2 = worst case last dsp command */ - if ((fn > l1s.mframe_sched.safe_fn) || - (l1s.mframe_sched.safe_fn >= GSM_MAX_FN)) + fn_diff = (fn - l1s.mframe_sched.safe_fn + GSM_MAX_FN) % GSM_MAX_FN; + if (fn_diff < (GSM_MAX_FN >> 1) /* diff is equal or positive */ + || (l1s.mframe_sched.safe_fn >= GSM_MAX_FN)) /* or safe_fn not yet set */ l1s.mframe_sched.safe_fn = fn; } } @@ -488,12 +489,19 @@ void mframe_set(uint32_t tasks) void mframe_schedule(void) { unsigned int i; - int fn_diff; + uint32_t fn_diff; + + /* Do not schedule new tasks until TPU is shifted */ + if (l1s.tpu_offset_shift) +{ +puts("outstanding TPU shift, do not schedule!\n"); + return; +} /* Try to enable/disable task to meet target bitmap */ - fn_diff = l1s.mframe_sched.safe_fn - l1s.current_time.fn; - if ((fn_diff <= 0) || (fn_diff >= (GSM_MAX_FN>>1)) || - (l1s.mframe_sched.safe_fn >= GSM_MAX_FN)) + fn_diff = (l1s.mframe_sched.safe_fn - l1s.current_time.fn + GSM_MAX_FN) % GSM_MAX_FN; + if ((fn_diff == 0 || fn_diff >= (GSM_MAX_FN>>1)) /* safe_fn is now or already was */ + || (l1s.mframe_sched.safe_fn >= GSM_MAX_FN)) /* or safe_fn not yet set */ /* If nothing is in the way, enable new tasks */ l1s.mframe_sched.tasks = l1s.mframe_sched.tasks_tgt; else diff --git a/src/target/firmware/layer1/prim_rach.c b/src/target/firmware/layer1/prim_rach.c index e6ea6568..eeb0435f 100644 --- a/src/target/firmware/layer1/prim_rach.c +++ b/src/target/firmware/layer1/prim_rach.c @@ -138,6 +138,10 @@ void l1a_rach_req(uint16_t offset, uint8_t combined, uint8_t ra) unsigned long flags; offset += 3; + if (l1s.tpu_offset_shift) { + puts("RACH while TPU is not correct, delaying!\n"); + offset += 2; + } local_firq_save(flags); if (combined) { diff --git a/src/target/firmware/layer1/sync.c b/src/target/firmware/layer1/sync.c index 36f42975..a9e51015 100644 --- a/src/target/firmware/layer1/sync.c +++ b/src/target/firmware/layer1/sync.c @@ -183,25 +183,56 @@ void l1s_reset_hw(void) /* Timer for detecting lost IRQ */ #define TIMER_TICKS_PER_TDMA 1875 -#define TIMER_TICK_JITTER 1 +#define TIMER_TICK_JITTER 2 static int last_timestamp; static inline void check_lost_frame(void) { - int diff, timestamp = hwtimer_read(1); + int diff, expected, timestamp = hwtimer_read(1); if (last_timestamp < timestamp) last_timestamp += (4*TIMER_TICKS_PER_TDMA); diff = last_timestamp - timestamp; + /* calculate expected diff */ + expected = (5000 + l1s.tpu_offset_changed) + * TIMER_TICKS_PER_TDMA / 5000; + /* allow for a bit of jitter */ - if (diff < TIMER_TICKS_PER_TDMA - TIMER_TICK_JITTER || - diff > TIMER_TICKS_PER_TDMA + TIMER_TICK_JITTER) - printf("LOST %d!\n", diff); + if (diff < expected - TIMER_TICK_JITTER || + diff > expected + TIMER_TICK_JITTER) + printf("LOST %d (expected %d)\n", diff, expected); + + /* check for lost frames */ + if ((diff - expected) > (TIMER_TICKS_PER_TDMA >> 1)) { + int frames_1000; + int fn_offset; + + frames_1000 = (diff - expected) * 1000 / TIMER_TICKS_PER_TDMA; + fn_offset = (frames_1000 + 500) / 1000; + + printf("%d.%03d frames lost, compensating GSM time\n", + frames_1000 / 1000, frames_1000 % 1000); + + l1s_time_inc(&l1s.current_time, fn_offset); + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.next_time, 1); + if (l1s.mframe_sched.safe_fn < GSM_MAX_FN) + ADD_MODULO(l1s.mframe_sched.safe_fn, fn_offset, + GSM_MAX_FN); + } + + /* check for two FIQs in the same frame */ + if ((diff - expected) < -(TIMER_TICKS_PER_TDMA >> 1)) { + puts("double FIQ in TDMA frame, compensating GSM time\n"); + l1s.next_time = l1s.current_time; + l1s_time_inc(&l1s.current_time, GSM_MAX_FN - 1); + } last_timestamp = timestamp; + l1s.tpu_offset_changed = 0; } /* schedule a completion */ @@ -258,6 +289,10 @@ static void l1_sync(void) * TDMA frame (including setup/cleanup steps) */ sched_flags = tdma_sched_flag_scan(); + /* if tpu offset needs to be shifted, schedule tpu scenario */ + if (l1s.tpu_offset_shift) + sched_flags |= TDMA_IFLG_TPU; + if (sched_flags & TDMA_IFLG_TPU) l1s_win_init(); @@ -367,6 +402,12 @@ void l1s_reset(void) /* Leave dedicated mode */ l1s.dedicated.type = GSM_DCHAN_NONE; + l1s.dedicated.tn = 0; + + /* reset TPU offset shift/correction */ + l1s.tpu_offset_correction = 0; + l1s.tpu_offset_shift = 0; + l1s.tpu_offset_changed = 0; /* reset scheduler and hardware */ sched_gsmtime_reset(); diff --git a/src/target/firmware/layer1/tpu_window.c b/src/target/firmware/layer1/tpu_window.c index f4e76c16..abdad956 100644 --- a/src/target/firmware/layer1/tpu_window.c +++ b/src/target/firmware/layer1/tpu_window.c @@ -67,30 +67,26 @@ static const uint16_t tx_burst_duration[_NUM_L1_TXWIN] = { [L1_TXWIN_AB] = L1_TX_AB_DURATION_Q, }; - static int _win_setup(__unused uint8_t p1, __unused uint8_t p2, __unused uint16_t p3) { - uint8_t tn; - - rfch_get_params(&l1s.next_time, NULL, NULL, &tn); - - l1s.tpu_offset = (5000 + l1s.tpu_offset + l1s.tpu_offset_correction) % 5000; + l1s.tpu_offset = (l1s.tpu_offset + l1s.tpu_offset_correction + + l1s.tpu_offset_shift + L1_TDMA_LENGTH_Q + + L1_TDMA_LENGTH_Q) % L1_TDMA_LENGTH_Q; + l1s.tpu_offset_changed = l1s.tpu_offset_correction + + l1s.tpu_offset_shift; l1s.tpu_offset_correction = 0; + l1s.tpu_offset_shift = 0; tpu_enq_at(4740); - tpu_enq_sync((5000 + l1s.tpu_offset + (L1_BURST_LENGTH_Q * tn)) % 5000); + tpu_enq_sync(l1s.tpu_offset); return 0; } static int _win_cleanup(__unused uint8_t p1, __unused uint8_t p2, __unused uint16_t p3) { - uint8_t tn; - - rfch_get_params(&l1s.next_time, NULL, NULL, &tn); - /* restore offset */ - tpu_enq_offset((5000 + l1s.tpu_offset + (L1_BURST_LENGTH_Q * tn)) % 5000); + tpu_enq_offset(l1s.tpu_offset); return 0; } -- cgit v1.2.3