Repository: incubator-mynewt-core Updated Branches: refs/heads/nrf_cputime [created] 6db66772c
First pass at low power timer. Only nrf52 right now. Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/94a59a3c Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/94a59a3c Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/94a59a3c Branch: refs/heads/nrf_cputime Commit: 94a59a3c317cd3a067d2480336b53af4cee32ff9 Parents: 0f93b4d Author: William San Filippo <[email protected]> Authored: Thu Feb 16 16:12:39 2017 -0800 Committer: William San Filippo <[email protected]> Committed: Mon Feb 27 13:13:31 2017 -0800 ---------------------------------------------------------------------- hw/drivers/nimble/nrf52/include/ble/xcvr.h | 11 +- hw/drivers/nimble/nrf52/src/ble_phy.c | 408 +++++++++++++++++-- kernel/os/include/os/os_cputime.h | 8 + kernel/os/src/os_cputime.c | 50 +++ .../controller/include/controller/ble_ll.h | 5 +- .../controller/include/controller/ble_ll_conn.h | 5 + .../include/controller/ble_ll_sched.h | 27 +- .../controller/include/controller/ble_phy.h | 23 +- net/nimble/controller/src/ble_ll.c | 16 + net/nimble/controller/src/ble_ll_adv.c | 72 +++- net/nimble/controller/src/ble_ll_conn.c | 256 ++++++++++-- net/nimble/controller/src/ble_ll_conn_priv.h | 3 +- net/nimble/controller/src/ble_ll_scan.c | 18 +- net/nimble/controller/src/ble_ll_sched.c | 246 +++++++---- net/nimble/include/nimble/ble.h | 3 + 15 files changed, 959 insertions(+), 192 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/hw/drivers/nimble/nrf52/include/ble/xcvr.h ---------------------------------------------------------------------- diff --git a/hw/drivers/nimble/nrf52/include/ble/xcvr.h b/hw/drivers/nimble/nrf52/include/ble/xcvr.h index 6a35124..effe81d 100644 --- a/hw/drivers/nimble/nrf52/include/ble/xcvr.h +++ b/hw/drivers/nimble/nrf52/include/ble/xcvr.h @@ -25,9 +25,18 @@ extern "C" { #endif /* Transceiver specific definitions */ +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 +/* + * NOTE: we have to account for the RTC output compare issue, which is why + * this number is much larger when using the 32.768 crystal for cputime. We + * want it to be 5 ticks. + */ +#define XCVR_PROC_DELAY_USECS (153) +#else +#define XCVR_PROC_DELAY_USECS (50) +#endif #define XCVR_RX_START_DELAY_USECS (140) #define XCVR_TX_START_DELAY_USECS (140) -#define XCVR_PROC_DELAY_USECS (50) #define XCVR_TX_SCHED_DELAY_USECS \ (XCVR_TX_START_DELAY_USECS + XCVR_PROC_DELAY_USECS) #define XCVR_RX_SCHED_DELAY_USECS \ http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/hw/drivers/nimble/nrf52/src/ble_phy.c ---------------------------------------------------------------------- diff --git a/hw/drivers/nimble/nrf52/src/ble_phy.c b/hw/drivers/nimble/nrf52/src/ble_phy.c index 8771f49..410e0dc 100644 --- a/hw/drivers/nimble/nrf52/src/ble_phy.c +++ b/hw/drivers/nimble/nrf52/src/ble_phy.c @@ -78,6 +78,9 @@ struct ble_phy_obj struct ble_mbuf_hdr rxhdr; void *txend_arg; ble_phy_tx_end_func txend_cb; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + uint32_t phy_start_cputime; +#endif }; struct ble_phy_obj g_ble_phy_data; @@ -280,6 +283,102 @@ nrf_wait_disabled(void) } } +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 +/** + * + * + */ +int +ble_phy_set_start_time(uint32_t cputime, uint8_t rem_usecs) +{ + uint32_t next_cc; + uint32_t cur_cc; + uint32_t cntr; + uint32_t delta; + + /* + * XXX: The TXEN time is 140 usecs but there may be additional delays + * Need to look at this. + */ + + /* + * With the 32.768 kHz crystal, we may need to adjust the RTC compare + * value by 1 tick due to the time it takes for TXEN. The code uses a 5 RTC + * tick offset, which is 152.5 usecs. The TXEN time is 140 usecs. This + * means that with a remainder of 0, TIMER0 should be set to 12 or 13 (as + * TIMER0 counts at 1MHz). A remainder of 19 or more we will need to add + * 1 tick. We dont need to add 1 tick per se, but it does give us slightly + * more time and thus less of a chance to miss a tick. Another note: we + * cant set TIMER0 CC to 0 as the compare wont occur; it must be 1 or more. + * This is why we subtract 18 (as opposed to 19) as rem_uses will be >= 1. + */ + if (rem_usecs <= 18) { + cputime -= 5; + rem_usecs += 12; + } else { + cputime -= 4; + rem_usecs -= 18; + } + + /* + * Can we set the RTC compare to start TIMER0? We can do it if: + * a) Current compare value is not N+1 or N+2 ticks from current + * counter. + * b) The value we want to set is not at least N+2 from current + * counter. + * + * NOTE: since the counter can tick 1 while we do these calculations we + * need to account for it. + */ + next_cc = cputime & 0xffffff; + cur_cc = NRF_RTC0->CC[0]; + cntr = NRF_RTC0->COUNTER; + + delta = (cur_cc - cntr) & 0xffffff; + if ((delta <= 3) && (delta != 0)) { + return -1; + } + delta = (next_cc - cntr) & 0xffffff; + if ((delta & 0x800000) || (delta < 3)) { + return -1; + } + + /* Clear and set TIMER0 to fire off at proper time */ + NRF_TIMER0->TASKS_CLEAR = 1; + NRF_TIMER0->CC[0] = rem_usecs; + NRF_TIMER0->EVENTS_COMPARE[0] = 0; + + /* Set RTC compare to start TIMER0 */ + NRF_RTC0->EVENTS_COMPARE[0] = 0; + NRF_RTC0->CC[0] = next_cc; + NRF_RTC0->EVTENSET = RTC_EVTENSET_COMPARE0_Msk; + + /* Enable PPI */ + NRF_PPI->CHENSET = PPI_CHEN_CH31_Msk; + + /* Store the cputime at which we set the RTC */ + g_ble_phy_data.phy_start_cputime = cputime; + + return 0; +} + +void +ble_phy_wfr_enable(uint32_t wfr_usecs) +{ + uint32_t start; + + /* CC[0] is set to when RXEN occurs */ + start = NRF_TIMER0->CC[0]; + + /* wfr_secs is the time from rxen until timeout */ + NRF_TIMER0->CC[3] = start + XCVR_RX_START_DELAY_USECS + wfr_usecs; + NRF_TIMER0->EVENTS_COMPARE[3] = 0; + + /* Enable wait for response PPI */ + NRF_PPI->CHENSET = (PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk); +} +#endif + /** * Setup transceiver for receive. */ @@ -360,14 +459,23 @@ ble_phy_tx_end_isr(void) uint8_t transition; uint8_t txlen; uint32_t wfr_time; +#if (MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768) + uint32_t txend; +#else uint32_t txstart; +#endif +#if (MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768) + /* Read captured end time */ + txend = NRF_TIMER0->CC[2]; +#else /* * Read captured tx start time. This is not the actual transmit start * time but it is the time at which the address event occurred * (after transmission of access address) */ txstart = NRF_TIMER0->CC[1]; +#endif /* If this transmission was encrypted we need to remember it */ was_encrypted = g_ble_phy_data.phy_encrypted; @@ -376,8 +484,13 @@ ble_phy_tx_end_isr(void) assert(g_ble_phy_data.phy_state == BLE_PHY_STATE_TX); /* Log the event */ +#if (MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768) + ble_ll_log(BLE_LL_LOG_ID_PHY_TXEND, g_ble_phy_data.phy_tx_pyld_len, + was_encrypted, txend); +#else ble_ll_log(BLE_LL_LOG_ID_PHY_TXEND, g_ble_phy_data.phy_tx_pyld_len, was_encrypted, txstart); +#endif /* Clear events and clear interrupt on disabled event */ NRF_RADIO->EVENTS_DISABLED = 0; @@ -416,13 +529,42 @@ ble_phy_tx_end_isr(void) if (txlen && was_encrypted) { txlen += BLE_LL_DATA_MIC_LEN; } +#if (MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768) + /* + * Set TIMER0 CC[3] to wait for response timeout. This occurs an IFS + * time plus the time it takes to receive the address from the transmit + * end. We add additional time to make sure the address event comes + * before the compare. + */ + wfr_time = BLE_LL_IFS + 40 + 16; + NRF_TIMER0->CC[3] = txend + wfr_time; + NRF_TIMER0->EVENTS_COMPARE[3] = 0; + + /* Enable wait for response PPI */ + NRF_PPI->CHENSET = (PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk); + + /* Enable the disabled interrupt so we time out on events compare */ + NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; +#else wfr_time = BLE_LL_WFR_USECS - BLE_TX_LEN_USECS_M(NRF_RX_START_OFFSET); wfr_time += BLE_TX_DUR_USECS_M(txlen); wfr_time = os_cputime_usecs_to_ticks(wfr_time); ble_ll_wfr_enable(txstart + wfr_time); +#endif } else { +#if (MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768) + /* + * XXX: not sure we need to stop the timer here all the time. Or that + * it should be stopped here. + */ + NRF_TIMER0->TASKS_STOP = 1; + NRF_TIMER0->TASKS_SHUTDOWN = 1; + NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk | + PPI_CHEN_CH20_Msk | PPI_CHEN_CH31_Msk; +#else /* Disable automatic TXEN */ NRF_PPI->CHENCLR = PPI_CHEN_CH20_Msk; +#endif assert(transition == BLE_PHY_TRANSITION_NONE); } } @@ -506,12 +648,48 @@ ble_phy_rx_start_isr(void) { int rc; uint32_t state; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + uint32_t usecs; + uint32_t ticks; +#endif struct ble_mbuf_hdr *ble_hdr; /* Clear events and clear interrupt */ NRF_RADIO->EVENTS_ADDRESS = 0; + + /* Clear wfr timer channels and DISABLED interrupt */ +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + NRF_RADIO->INTENCLR = RADIO_INTENCLR_DISABLED_Msk | RADIO_INTENCLR_ADDRESS_Msk; + NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk; +#else NRF_RADIO->INTENCLR = RADIO_INTENCLR_ADDRESS_Msk; +#endif + + /* Initialize the ble mbuf header */ + ble_hdr = &g_ble_phy_data.rxhdr; + ble_hdr->rxinfo.flags = ble_ll_state_get(); + ble_hdr->rxinfo.channel = g_ble_phy_data.phy_chan; + ble_hdr->rxinfo.handle = 0; + +#if (MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768) + /* + * Calculate receive start time. We assume that the header time is + * 40 usecs (only 1 mbps supported right now). + * + * XXX: possibly use other routine with remainder! + */ + usecs = NRF_TIMER0->CC[1] - 40; + ticks = os_cputime_usecs_to_ticks(usecs); + ble_hdr->rem_usecs = usecs - os_cputime_ticks_to_usecs(ticks); + ble_hdr->beg_cputime = g_ble_phy_data.phy_start_cputime + ticks; +#else + ble_hdr->beg_cputime = NRF_TIMER0->CC[1] - + os_cputime_usecs_to_ticks(BLE_TX_LEN_USECS_M(NRF_RX_START_OFFSET)); +#endif + /* XXX: I wonder if we always have the 1st byte. If we need to wait for + * rx chain delay, it could be 18 usecs from address interrupt. The + nrf52 may be able to get here early. */ /* Wait to get 1st byte of frame */ while (1) { state = NRF_RADIO->STATE; @@ -530,14 +708,6 @@ ble_phy_rx_start_isr(void) } } - /* Initialize flags, channel and state in ble header at rx start */ - ble_hdr = &g_ble_phy_data.rxhdr; - ble_hdr->rxinfo.flags = ble_ll_state_get(); - ble_hdr->rxinfo.channel = g_ble_phy_data.phy_chan; - ble_hdr->rxinfo.handle = 0; - ble_hdr->beg_cputime = NRF_TIMER0->CC[1] - - os_cputime_usecs_to_ticks(BLE_TX_LEN_USECS_M(NRF_RX_START_OFFSET)); - /* Call Link Layer receive start function */ rc = ble_ll_rx_start((uint8_t *)&g_ble_phy_rx_buf[0] + 3, g_ble_phy_data.phy_chan, @@ -573,16 +743,34 @@ ble_phy_isr(void) /* Read irq register to determine which interrupts are enabled */ irq_en = NRF_RADIO->INTENCLR; - /* Check for disabled event. This only happens for transmits now */ - if ((irq_en & RADIO_INTENCLR_DISABLED_Msk) && NRF_RADIO->EVENTS_DISABLED) { - ble_phy_tx_end_isr(); - } + /* + * NOTE: order of checking is important! Possible, if things get delayed, + * we have both an ADDRESS and DISABLED interrupt in rx state. If we get + * an address, we disable the DISABLED interrupt. + */ /* We get this if we have started to receive a frame */ if ((irq_en & RADIO_INTENCLR_ADDRESS_Msk) && NRF_RADIO->EVENTS_ADDRESS) { +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + irq_en &= ~RADIO_INTENCLR_DISABLED_Msk; +#endif ble_phy_rx_start_isr(); } + /* Check for disabled event. This only happens for transmits now */ + if ((irq_en & RADIO_INTENCLR_DISABLED_Msk) && NRF_RADIO->EVENTS_DISABLED) { +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + if (g_ble_phy_data.phy_state == BLE_PHY_STATE_RX) { + NRF_RADIO->EVENTS_DISABLED = 0; + ble_ll_wfr_timer_exp(NULL); + } else { + ble_phy_tx_end_isr(); + } +#else + ble_phy_tx_end_isr(); +#endif + } + /* Receive packet end (we dont enable this for transmit) */ if ((irq_en & RADIO_INTENCLR_END_Msk) && NRF_RADIO->EVENTS_END) { ble_phy_rx_end_isr(); @@ -657,8 +845,13 @@ ble_phy_init(void) /* Configure IFS */ NRF_RADIO->TIFS = BLE_LL_IFS; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* Captures tx/rx start in timer0 cc 1 and tx/rx end in timer0 cc 2 */ + NRF_PPI->CHENSET = PPI_CHEN_CH26_Msk | PPI_CHEN_CH27_Msk; +#else /* Captures tx/rx start in timer0 capture 1 */ NRF_PPI->CHENSET = PPI_CHEN_CH26_Msk; +#endif #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1) NRF_CCM->INTENCLR = 0xffffffff; @@ -677,6 +870,27 @@ ble_phy_init(void) NRF_AAR->NIRK = 0; #endif + /* TIMER0 setup for PHY when using RTC */ +#if (MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768) + NRF_TIMER0->TASKS_STOP = 1; + NRF_TIMER0->TASKS_SHUTDOWN = 1; + NRF_TIMER0->BITMODE = 3; /* 32-bit timer */ + NRF_TIMER0->MODE = 0; /* Timer mode */ + NRF_TIMER0->PRESCALER = 4; /* gives us 1 MHz */ + + /* + * PPI setup. + * Channel 4: Captures TIMER0 in CC[3] when EVENTS_ADDRESS occurs. Used + * to cancel the wait for response timer. + * Channel 5: TIMER0 CC[3] to TASKS_DISABLE on radio. This is the wait + * for response timer. + */ + NRF_PPI->CH[4].EEP = (uint32_t)&(NRF_RADIO->EVENTS_ADDRESS); + NRF_PPI->CH[4].TEP = (uint32_t)&(NRF_TIMER0->TASKS_CAPTURE[3]); + NRF_PPI->CH[5].EEP = (uint32_t)&(NRF_TIMER0->EVENTS_COMPARE[3]); + NRF_PPI->CH[5].TEP = (uint32_t)&(NRF_RADIO->TASKS_DISABLE); +#endif + /* Set isr in vector table and enable interrupt */ NVIC_SetPriority(RADIO_IRQn, 0); NVIC_SetVector(RADIO_IRQn, (uint32_t)ble_phy_isr); @@ -785,6 +999,7 @@ ble_phy_set_txend_cb(ble_phy_tx_end_func txend_cb, void *arg) g_ble_phy_data.txend_arg = arg; } +#if (MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768) /** * Called to set the start time of a transmission. * @@ -794,28 +1009,38 @@ ble_phy_set_txend_cb(ble_phy_tx_end_func txend_cb, void *arg) * NOTE: care must be taken when calling this function. The channel should * already be set. * - * @param cputime - * + * @param cputime This is the tick at which the 1st bit of the preamble + * should be transmitted + * @param rem_usecs This is used only when the underlying timing uses a 32.768 + * kHz crystal. It is the # of usecs from the cputime tick + * at which the first bit of the preamble should be + * transmitted. * @return int */ int -ble_phy_tx_set_start_time(uint32_t cputime) +ble_phy_tx_set_start_time(uint32_t cputime, uint8_t rem_usecs) { int rc; - NRF_TIMER0->CC[0] = cputime; - NRF_PPI->CHENSET = PPI_CHEN_CH20_Msk; + /* XXX: This should not be necessary, but paranoia is good! */ + /* Clear timer0 compare to RXEN since we are transmitting */ NRF_PPI->CHENCLR = PPI_CHEN_CH21_Msk; - if ((int32_t)(os_cputime_get32() - cputime) >= 0) { + + /* + * XXX: The TXEN time is 140 usecs but there may be additional delays + * Need to look at this. + */ + if (ble_phy_set_start_time(cputime, rem_usecs) != 0) { STATS_INC(ble_phy_stats, tx_late); ble_phy_disable(); - rc = BLE_PHY_ERR_TX_LATE; + rc = BLE_PHY_ERR_TX_LATE; } else { + /* Enable PPI to automatically start TXEN */ + NRF_PPI->CHENSET = PPI_CHEN_CH20_Msk; rc = 0; } return rc; } - /** * Called to set the start time of a reception * @@ -830,23 +1055,90 @@ ble_phy_tx_set_start_time(uint32_t cputime) * @return int */ int -ble_phy_rx_set_start_time(uint32_t cputime) +ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs) { int rc; + /* XXX: This should not be necessary, but paranoia is good! */ + /* Clear timer0 compare to TXEN since we are transmitting */ + NRF_PPI->CHENCLR = PPI_CHEN_CH20_Msk; + + /* + * XXX: The RXEN time is 138 usecs but there may be additional delays + * Need to look at this. + */ + if (ble_phy_set_start_time(cputime, rem_usecs) != 0) { + STATS_INC(ble_phy_stats, rx_late); + NRF_PPI->CHENCLR = PPI_CHEN_CH21_Msk; + NRF_RADIO->TASKS_RXEN = 1; + rc = BLE_PHY_ERR_RX_LATE; + } else { + /* Enable PPI to automatically start RXEN */ + NRF_PPI->CHENSET = PPI_CHEN_CH21_Msk; + + /* Start rx */ + rc = ble_phy_rx(); + } + return rc; +} +#else +/** + * Called to set the start time of a transmission. + * + * This function is called to set the start time when we are not going from + * rx to tx automatically. + * + * NOTE: care must be taken when calling this function. The channel should + * already be set. + * + * @param cputime This is the tick at which the 1st bit of the preamble + * should be transmitted + * @return int + */ +int +ble_phy_tx_set_start_time(uint32_t cputime) +{ + int rc; + + cputime -= os_cputime_usecs_to_ticks(XCVR_TX_START_DELAY_USECS); + NRF_PPI->CHENCLR = PPI_CHEN_CH21_Msk; NRF_TIMER0->CC[0] = cputime; + NRF_TIMER0->EVENTS_COMPARE[0] = 0; + NRF_PPI->CHENSET = PPI_CHEN_CH20_Msk; + if ((int32_t)(os_cputime_get32() - cputime) >= 0) { + STATS_INC(ble_phy_stats, tx_late); + ble_phy_disable(); + rc = BLE_PHY_ERR_TX_LATE; + } else { + rc = 0; + } + + return rc; +} + +#if 0 +int +ble_phy_rx_set_start_time(void) +{ + /* + * XXX: For now, we dont use this function if we are not using the + * RTC. Keeping the code around just in case we want to use it later. + */ NRF_PPI->CHENCLR = PPI_CHEN_CH20_Msk; + NRF_TIMER0->CC[0] = cputime; + NRF_TIMER0->EVENTS_COMPARE[0] = 0; NRF_PPI->CHENSET = PPI_CHEN_CH21_Msk; if ((int32_t)(os_cputime_get32() - cputime) >= 0) { STATS_INC(ble_phy_stats, rx_late); NRF_PPI->CHENCLR = PPI_CHEN_CH21_Msk; NRF_RADIO->TASKS_RXEN = 1; - rc = BLE_PHY_ERR_TX_LATE; + rc = BLE_PHY_ERR_RX_LATE; } else { rc = 0; } - return rc; } +#endif +#endif int ble_phy_tx(struct os_mbuf *txpdu, uint8_t end_trans) @@ -871,6 +1163,16 @@ ble_phy_tx(struct os_mbuf *txpdu, uint8_t end_trans) ble_hdr = BLE_MBUF_HDR_PTR(txpdu); payload_len = ble_hdr->txinfo.pyld_len; + /* + * XXX: Although we may not have to do this here, I clear all the PPI + * that should not be used when transmitting. Some of them are only enabled + * if encryption and/or privacy is on, but I dont care. Better to be + * paranoid, and if you are going to clear one, might as well clear them + * all. + */ + NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk | PPI_CHEN_CH23_Msk | + PPI_CHEN_CH25_Msk; + #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1) if (g_ble_phy_data.phy_encrypted) { dptr = (uint8_t *)&g_ble_phy_enc_buf[0]; @@ -883,11 +1185,9 @@ ble_phy_tx(struct os_mbuf *txpdu, uint8_t end_trans) NRF_CCM->EVENTS_ERROR = 0; NRF_CCM->MODE = CCM_MODE_LENGTH_Msk; NRF_CCM->CNFPTR = (uint32_t)&g_nrf_ccm_data; - NRF_PPI->CHENCLR = PPI_CHEN_CH25_Msk | PPI_CHEN_CH23_Msk; NRF_PPI->CHENSET = PPI_CHEN_CH24_Msk; } else { #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1) - NRF_PPI->CHENCLR = PPI_CHEN_CH23_Msk; NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; #endif dptr = (uint8_t *)&g_ble_phy_tx_buf[0]; @@ -895,9 +1195,6 @@ ble_phy_tx(struct os_mbuf *txpdu, uint8_t end_trans) pktptr = dptr; } #else -#if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1) - NRF_PPI->CHENCLR = PPI_CHEN_CH23_Msk; -#endif dptr = (uint8_t *)&g_ble_phy_tx_buf[0]; ++dptr; pktptr = dptr; @@ -1077,29 +1374,62 @@ ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crcinit) return 0; } +#if (MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768) /** - * Disable the PHY. This will do the following: - * -> Turn off all phy interrupts. - * -> Disable internal shortcuts. - * -> Disable the radio. - * -> Make sure we wont automatically go to rx/tx on output compare - * -> Sets phy state to idle. - * -> Clears any pending irqs in the NVIC. Might not be necessary but we do - * it as a precaution. + * Stop the timer used to count microseconds when using RTC for cputime */ void -ble_phy_disable(void) +ble_phy_stop_usec_timer(void) { - ble_ll_log(BLE_LL_LOG_ID_PHY_DISABLE, g_ble_phy_data.phy_state, 0, 0); + NRF_TIMER0->TASKS_STOP = 1; + NRF_TIMER0->TASKS_SHUTDOWN = 1; + NRF_RTC0->EVTENCLR = RTC_EVTENSET_COMPARE0_Msk; +} +#endif +/** + * ble phy disable irq and ppi + * + * This routine is to be called when reception was stopped due to either a + * wait for response timeout or a packet being received and the phy is to be + * restarted in receive mode. Generally, the disable routine is called to stop + * the phy. + */ +void +ble_phy_disable_irq_and_ppi(void) +{ NRF_RADIO->INTENCLR = NRF_RADIO_IRQ_MASK_ALL; NRF_RADIO->SHORTS = 0; NRF_RADIO->TASKS_DISABLE = 1; - NRF_PPI->CHENCLR = PPI_CHEN_CH23_Msk | PPI_CHEN_CH21_Msk | PPI_CHEN_CH20_Msk; + NRF_PPI->CHENCLR = PPI_CHEN_CH4_Msk | PPI_CHEN_CH5_Msk | PPI_CHEN_CH20_Msk | + PPI_CHEN_CH21_Msk | PPI_CHEN_CH23_Msk | PPI_CHEN_CH24_Msk | + PPI_CHEN_CH25_Msk | PPI_CHEN_CH31_Msk; NVIC_ClearPendingIRQ(RADIO_IRQn); g_ble_phy_data.phy_state = BLE_PHY_STATE_IDLE; } +void +ble_phy_restart_rx(void) +{ + ble_phy_disable_irq_and_ppi(); + ble_phy_rx(); +} + +/** + * ble phy disable + * + * Disables the PHY. This should be called when an event is over. It stops + * the usec timer (if used), disables interrupts, disables the RADIO, disables + * PPI and sets state to idle. + */ +void +ble_phy_disable(void) +{ + ble_ll_log(BLE_LL_LOG_ID_PHY_DISABLE, g_ble_phy_data.phy_state, 0, 0); + ble_phy_stop_usec_timer(); + ble_phy_disable_irq_and_ppi(); +} + /* Gets the current access address */ uint32_t ble_phy_access_addr_get(void) { http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/kernel/os/include/os/os_cputime.h ---------------------------------------------------------------------- diff --git a/kernel/os/include/os/os_cputime.h b/kernel/os/include/os/os_cputime.h index f44c941..022efdc 100644 --- a/kernel/os/include/os/os_cputime.h +++ b/kernel/os/include/os/os_cputime.h @@ -45,6 +45,10 @@ extern "C" { #define OS_CPUTIME_FREQ_1MHZ #endif +#if (MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768) +#define OS_CPUTIME_FREQ_32768 +#endif + /* CPUTIME data. */ struct os_cputime_data { @@ -80,6 +84,7 @@ int os_cputime_init(uint32_t clock_freq); */ uint32_t os_cputime_get32(void); +#if !defined(OS_CPUTIME_FREQ_32768) /** * os cputime nsecs to ticks * @@ -101,6 +106,7 @@ uint32_t os_cputime_nsecs_to_ticks(uint32_t nsecs); * @return uint32_t The number of nanoseconds corresponding to 'ticks' */ uint32_t os_cputime_ticks_to_nsecs(uint32_t ticks); +#endif #if defined(OS_CPUTIME_FREQ_1MHZ) #define os_cputime_usecs_to_ticks(x) (x) @@ -138,6 +144,7 @@ uint32_t os_cputime_ticks_to_usecs(uint32_t ticks); */ void os_cputime_delay_ticks(uint32_t ticks); +#if !defined(OS_CPUTIME_FREQ_32768) /** * os cputime delay nsecs * @@ -146,6 +153,7 @@ void os_cputime_delay_ticks(uint32_t ticks); * @param nsecs The number of nanoseconds to wait. */ void os_cputime_delay_nsecs(uint32_t nsecs); +#endif /** * os cputime delay usecs http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/kernel/os/src/os_cputime.c ---------------------------------------------------------------------- diff --git a/kernel/os/src/os_cputime.c b/kernel/os/src/os_cputime.c index 067f1a1..01b15fe 100644 --- a/kernel/os/src/os_cputime.c +++ b/kernel/os/src/os_cputime.c @@ -30,7 +30,9 @@ * @{ */ +#if !defined(OS_CPUTIME_FREQ_32768) && !defined(OS_CPUTIME_FREQ_1MHZ) struct os_cputime_data g_os_cputime; +#endif /** * os cputime init @@ -49,11 +51,14 @@ os_cputime_init(uint32_t clock_freq) int rc; /* Set the ticks per microsecond. */ +#if !defined(OS_CPUTIME_FREQ_32768) && !defined(OS_CPUTIME_FREQ_1MHZ) g_os_cputime.ticks_per_usec = clock_freq / 1000000U; +#endif rc = hal_timer_config(MYNEWT_VAL(OS_CPUTIME_TIMER_NUM), clock_freq); return rc; } +#if !defined(OS_CPUTIME_FREQ_32768) /** * os cputime nsecs to ticks * @@ -99,8 +104,50 @@ os_cputime_ticks_to_nsecs(uint32_t ticks) return nsecs; } +#endif #if !defined(OS_CPUTIME_FREQ_1MHZ) +#if defined(OS_CPUTIME_FREQ_32768) +/** + * os cputime usecs to ticks + * + * Converts the given number of microseconds into cputime ticks. + * + * @param usecs The number of microseconds to convert to ticks + * + * @return uint32_t The number of ticks corresponding to 'usecs' + */ +uint32_t +os_cputime_usecs_to_ticks(uint32_t usecs) +{ + uint64_t ticks; + + ticks = ((uint64_t)usecs << 9) / 15625; + return (uint32_t)ticks; +} + +/** + * cputime ticks to usecs + * + * Convert the given number of ticks into microseconds. + * + * @param ticks The number of ticks to convert to microseconds. + * + * @return uint32_t The number of microseconds corresponding to 'ticks' + * + * NOTE: This calculation will overflow if the value for ticks is greater + * than 140737488. I am not going to check that here because that many ticks + * is about 4222 seconds, way more than what this routine should be used for. + */ +uint32_t +os_cputime_ticks_to_usecs(uint32_t ticks) +{ + uint32_t usecs; + + usecs = ((ticks >> 9) * 15625) + (((ticks & 0x1ff) * 15625) >> 9); + return usecs; +} +#else /** * os cputime usecs to ticks * @@ -138,6 +185,7 @@ os_cputime_ticks_to_usecs(uint32_t ticks) return us; } #endif +#endif /** * os cputime delay ticks @@ -157,6 +205,7 @@ os_cputime_delay_ticks(uint32_t ticks) } } +#if !defined(OS_CPUTIME_FREQ_32768) /** * os cputime delay nsecs * @@ -172,6 +221,7 @@ os_cputime_delay_nsecs(uint32_t nsecs) ticks = os_cputime_nsecs_to_ticks(nsecs); os_cputime_delay_ticks(ticks); } +#endif /** * os cputime delay usecs http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/net/nimble/controller/include/controller/ble_ll.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/include/controller/ble_ll.h b/net/nimble/controller/include/controller/ble_ll.h index e349d71..d46f722 100644 --- a/net/nimble/controller/include/controller/ble_ll.h +++ b/net/nimble/controller/include/controller/ble_ll.h @@ -362,6 +362,9 @@ void ble_ll_wfr_enable(uint32_t cputime); /* Disable wait for response timer */ void ble_ll_wfr_disable(void); +/* Wait for response timer expiration callback */ +void ble_ll_wfr_timer_exp(void *arg); + /* Read set of features supported by the Link Layer */ uint8_t ble_ll_read_supp_features(void); @@ -383,7 +386,7 @@ int ble_ll_rand_start(void); * XXX: temporary LL debug log. Will get removed once we transition to real * log */ -#undef BLE_LL_LOG +#define BLE_LL_LOG #include "console/console.h" #define BLE_LL_LOG_ID_PHY_SETCHAN (1) http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/net/nimble/controller/include/controller/ble_ll_conn.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/include/controller/ble_ll_conn.h b/net/nimble/controller/include/controller/ble_ll_conn.h index b2e6410..041504d 100644 --- a/net/nimble/controller/include/controller/ble_ll_conn.h +++ b/net/nimble/controller/include/controller/ble_ll_conn.h @@ -202,6 +202,11 @@ struct ble_ll_conn_sm uint16_t max_ce_len; uint16_t tx_win_off; uint32_t anchor_point; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + uint8_t anchor_point_usecs; /* XXX: can this be uint8_t ?*/ + uint8_t conn_itvl_usecs; + uint32_t conn_itvl_ticks; +#endif uint32_t last_anchor_point; /* Slave only */ uint32_t slave_cur_tx_win_usecs; uint32_t slave_cur_window_widening; http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/net/nimble/controller/include/controller/ble_ll_sched.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/include/controller/ble_ll_sched.h b/net/nimble/controller/include/controller/ble_ll_sched.h index 3a05cfe..bf3bc6d 100644 --- a/net/nimble/controller/include/controller/ble_ll_sched.h +++ b/net/nimble/controller/include/controller/ble_ll_sched.h @@ -25,7 +25,8 @@ extern "C" { #endif /* Time per BLE scheduler slot */ -#define BLE_LL_SCHED_USECS_PER_SLOT (1250) +#define BLE_LL_SCHED_USECS_PER_SLOT (1250) +#define BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT (41) /* 1 tick = 30.517 usecs */ /* * Worst case time needed for scheduled advertising item. This is the longest @@ -46,6 +47,17 @@ extern "C" { #define BLE_LL_SCHED_DIRECT_ADV_MAX_USECS (502) #define BLE_LL_SCHED_MAX_ADV_PDU_USECS (376) +/* BLE Jitter (+/- 16 useecs) */ +#define BLE_LL_JITTER_USECS (16) + +/* + * This is the offset from the start of the scheduled item until the actual + * tx/rx should occur, in ticks. + */ +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 +extern uint8_t g_ble_ll_sched_offset_ticks; +#endif + /* * This is the number of slots needed to transmit and receive a maximum * size PDU, including an IFS time before each. The actual time is @@ -69,10 +81,19 @@ extern "C" { struct ble_ll_sched_item; typedef int (*sched_cb_func)(struct ble_ll_sched_item *sch); +/* + * Schedule item + * sched_type: This is the type of the schedule item. + * enqueued: Flag denoting if item is on the scheduler list. 0: no, 1:yes + * remainder: # of usecs from offset till tx/rx should occur + * txrx_offset: Number of ticks from start time until tx/rx should occur. + * + */ struct ble_ll_sched_item { uint8_t sched_type; uint8_t enqueued; + uint8_t remainder; uint32_t start_time; uint32_t end_time; void *cb_arg; @@ -94,8 +115,8 @@ void ble_ll_sched_free_item(struct ble_ll_sched_item *sch); /* Schedule a new master connection */ struct ble_ll_conn_sm; -int ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, uint32_t adv_rxend, - uint8_t req_slots); +int ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, + struct ble_mbuf_hdr *ble_hdr, uint8_t pyld_len); /* Schedule a new slave connection */ int ble_ll_sched_slave_new(struct ble_ll_conn_sm *connsm); http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/net/nimble/controller/include/controller/ble_phy.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/include/controller/ble_phy.h b/net/nimble/controller/include/controller/ble_phy.h index 674cbc5..f300a13 100644 --- a/net/nimble/controller/include/controller/ble_phy.h +++ b/net/nimble/controller/include/controller/ble_phy.h @@ -70,6 +70,7 @@ struct os_mbuf; #define BLE_PHY_ERR_INV_PARAM (3) #define BLE_PHY_ERR_NO_BUFS (4) #define BLE_PHY_ERR_TX_LATE (5) +#define BLE_PHY_ERR_RX_LATE (6) /* Maximun PDU length. Includes LL header of 2 bytes and 255 bytes payload. */ #define BLE_PHY_MAX_PDU_LEN (257) @@ -86,11 +87,16 @@ int ble_phy_reset(void); /* Set the PHY channel */ int ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crcinit); +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 /* Set transmit start time */ -int ble_phy_tx_set_start_time(uint32_t cputime); +int ble_phy_tx_set_start_time(uint32_t cputime, uint8_t rem_usecs); /* Set receive start time */ -int ble_phy_rx_set_start_time(uint32_t cputime); +int ble_phy_rx_set_start_time(uint32_t cputime, uint8_t rem_usecs); +#else +/* Set transmit start time */ +int ble_phy_tx_set_start_time(uint32_t cputime); +#endif /* Set the transmit end callback and argument */ void ble_phy_set_txend_cb(ble_phy_tx_end_func txend_cb, void *arg); @@ -116,6 +122,19 @@ int ble_phy_txpwr_get(void); /* Disable the PHY */ void ble_phy_disable(void); +#if (MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768) +void ble_phy_stop_usec_timer(void); +void ble_phy_wfr_enable(uint32_t wfr_usecs); +#else +#define ble_phy_stop_usec_timer() +#endif + +/* + * Used to restart reception on same channel after wfr timer expiration or + * frame received. + */ +void ble_phy_restart_rx(void); + /* Gets the current state of the PHY */ int ble_phy_state_get(void); http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/net/nimble/controller/src/ble_ll.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll.c b/net/nimble/controller/src/ble_ll.c index 6071d3c..0d1fa07 100644 --- a/net/nimble/controller/src/ble_ll.c +++ b/net/nimble/controller/src/ble_ll.c @@ -51,6 +51,11 @@ * 4) Should look into always disabled the wfr interrupt if we receive the * start of a frame. Need to look at the various states to see if this is the * right thing to do. + * 5) I am not sure that if we are passed the output compare that we actually + * get the interrupt. Test this. + * 6) I am not sure that if we receive a packet while scanning that we actually + * go back to scanning. I need to make sure we re-enable the receive. + * Put an event in the log! */ /* Supported states */ @@ -536,7 +541,9 @@ ble_ll_wfr_timer_exp(void *arg) void ble_ll_wfr_enable(uint32_t cputime) { +#if MYNEWT_VAL(OS_CPUTIME_FREQ) != 32768 os_cputime_timer_start(&g_ble_ll_data.ll_wfr_timer, cputime); +#endif } /** @@ -545,7 +552,9 @@ ble_ll_wfr_enable(uint32_t cputime) void ble_ll_wfr_disable(void) { +#if MYNEWT_VAL(OS_CPUTIME_FREQ) != 32768 os_cputime_timer_stop(&g_ble_ll_data.ll_wfr_timer); +#endif } /** @@ -786,7 +795,12 @@ ble_ll_rx_start(uint8_t *rxbuf, uint8_t chan, struct ble_mbuf_hdr *rxhdr) int rc; uint8_t pdu_type; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + ble_ll_log(BLE_LL_LOG_ID_RX_START, chan, rxhdr->rem_usecs, + rxhdr->beg_cputime); +#else ble_ll_log(BLE_LL_LOG_ID_RX_START, chan, 0, rxhdr->beg_cputime); +#endif /* Check channel type */ if (chan < BLE_PHY_NUM_DATA_CHANS) { @@ -1241,9 +1255,11 @@ ble_ll_init(void) ble_ll_hw_err_timer_cb, NULL); +#if MYNEWT_VAL(OS_CPUTIME_FREQ) != 32768 /* Initialize wait for response timer */ os_cputime_timer_init(&g_ble_ll_data.ll_wfr_timer, ble_ll_wfr_timer_exp, NULL); +#endif /* Initialize LL HCI */ ble_ll_hci_init(); http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/net/nimble/controller/src/ble_ll_adv.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_adv.c b/net/nimble/controller/src/ble_ll_adv.c index 203f13a..dd27336 100644 --- a/net/nimble/controller/src/ble_ll_adv.c +++ b/net/nimble/controller/src/ble_ll_adv.c @@ -430,8 +430,14 @@ ble_ll_adv_tx_start_cb(struct ble_ll_sched_item *sch) assert(rc == 0); /* Set transmit start time. */ - txstart = sch->start_time + os_cputime_usecs_to_ticks(XCVR_PROC_DELAY_USECS); +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + txstart = sch->start_time + g_ble_ll_sched_offset_ticks; + rc = ble_phy_tx_set_start_time(txstart, sch->remainder); +#else + txstart = sch->start_time + + os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); rc = ble_phy_tx_set_start_time(txstart); +#endif if (rc) { STATS_INC(ble_ll_stats, adv_late_starts); goto adv_tx_done; @@ -498,7 +504,7 @@ adv_tx_done: } static void -ble_ll_adv_set_sched(struct ble_ll_adv_sm *advsm, int sched_new) +ble_ll_adv_set_sched(struct ble_ll_adv_sm *advsm) { uint32_t max_usecs; struct ble_ll_sched_item *sch; @@ -529,20 +535,15 @@ ble_ll_adv_set_sched(struct ble_ll_adv_sm *advsm, int sched_new) */ max_usecs += XCVR_PROC_DELAY_USECS; - if (sched_new) { - /* - * We have to add the scheduling delay and tx start delay to the max - * time of the event since the pdu does not start at the scheduled start. - */ - max_usecs += XCVR_TX_SCHED_DELAY_USECS; - sch->start_time = os_cputime_get32(); - sch->end_time = sch->start_time + os_cputime_usecs_to_ticks(max_usecs); - } else { - sch->start_time = advsm->adv_pdu_start_time - - os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); - sch->end_time = advsm->adv_pdu_start_time + - os_cputime_usecs_to_ticks(max_usecs); - } +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + sch->start_time = advsm->adv_pdu_start_time - g_ble_ll_sched_offset_ticks; + sch->remainder = 0; +#else + sch->start_time = advsm->adv_pdu_start_time - + os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); +#endif + sch->end_time = advsm->adv_pdu_start_time + + os_cputime_usecs_to_ticks(max_usecs); } /** @@ -872,10 +873,23 @@ ble_ll_adv_sm_start(struct ble_ll_adv_sm *advsm) advsm->adv_chan = adv_chan; /* + * XXX: while this may not be the most efficient, schedule the first + * advertising event some time in the future (5 msecs). This will give + * time to start up any clocks or anything and also avoid a bunch of code + * to check if we are currently doing anything. Just makes this simple. + * + * Might also want to align this on a slot in the future. + * + * NOTE: adv_event_start_time gets set by the sched_adv_new + */ + advsm->adv_pdu_start_time = os_cputime_get32() + + os_cputime_usecs_to_ticks(BLE_LL_SCHED_MAX_TXRX_SLOT); + + /* * Schedule advertising. We set the initial schedule start and end * times to the earliest possible start/end. */ - ble_ll_adv_set_sched(advsm, 1); + ble_ll_adv_set_sched(advsm); ble_ll_sched_adv_new(&advsm->adv_sch); return BLE_ERR_SUCCESS; @@ -884,9 +898,14 @@ ble_ll_adv_sm_start(struct ble_ll_adv_sm *advsm) void ble_ll_adv_scheduled(struct ble_ll_adv_sm *advsm, uint32_t sch_start) { +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* The event start time is when we start transmission of the adv PDU */ + advsm->adv_event_start_time = sch_start + g_ble_ll_sched_offset_ticks; +#else /* The event start time is when we start transmission of the adv PDU */ advsm->adv_event_start_time = sch_start + os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); +#endif advsm->adv_pdu_start_time = advsm->adv_event_start_time; @@ -1511,8 +1530,12 @@ ble_ll_adv_done(struct ble_ll_adv_sm *advsm) * The scheduled time better be in the future! If it is not, we will * just keep advancing until we the time is in the future */ +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + start_time = advsm->adv_pdu_start_time - g_ble_ll_sched_offset_ticks; +#else start_time = advsm->adv_pdu_start_time - os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); +#endif delta_t = (int32_t)(start_time - os_cputime_get32()); if (delta_t < 0) { @@ -1543,9 +1566,13 @@ ble_ll_adv_done(struct ble_ll_adv_sm *advsm) * We will transmit right away. Set next pdu start time to now * plus a xcvr start delay just so we dont count late adv starts */ - advsm->adv_pdu_start_time = os_cputime_get32() + + advsm->adv_pdu_start_time = os_cputime_get32(); +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + advsm->adv_pdu_start_time += g_ble_ll_sched_offset_ticks; +#else + advsm->adv_pdu_start_time += os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); - +#endif resched_pdu = 1; } @@ -1571,7 +1598,7 @@ ble_ll_adv_done(struct ble_ll_adv_sm *advsm) #endif /* Schedule advertising transmit */ - ble_ll_adv_set_sched(advsm, 0); + ble_ll_adv_set_sched(advsm); /* * In the unlikely event we cant reschedule this, just post a done @@ -1583,6 +1610,11 @@ ble_ll_adv_done(struct ble_ll_adv_sm *advsm) rc = ble_ll_sched_adv_reschedule(&advsm->adv_sch, &start_time, max_delay_ticks); if (!rc) { +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + start_time += g_ble_ll_sched_offset_ticks; +#else + start_time += os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); +#endif advsm->adv_event_start_time = start_time; advsm->adv_pdu_start_time = start_time; } http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/net/nimble/controller/src/ble_ll_conn.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_conn.c b/net/nimble/controller/src/ble_ll_conn.c index c0cda6d..1f4ab8b 100644 --- a/net/nimble/controller/src/ble_ll_conn.c +++ b/net/nimble/controller/src/ble_ll_conn.c @@ -95,15 +95,26 @@ extern void bletest_completed_pkt(uint16_t handle); * response. Well, it should. If this packet will overrun the next scheduled * event, what should we do? Transmit anyway? Not transmit? For now, we just * transmit. + * + * 32kHz crystal + * 1) When scheduling, I need to make sure I have time between + * this one and the next. Should I deal with this in the sched. Or + * is this basically accounted for given a slot? I really just need to + * make sure everything is over N ticks before the next sched start! + * Just add to end time? + * + * 2) I think one way to handle the problem of losing up to a microsecond + * every time we call ble_ll_conn_next_event in a loop is to do everything by + * keeping track of last anchor point. Would need last anchor usecs too. I guess + * we could also keep last anchor usecs as a uint32 or something and when we + * do the next event keep track of the residual using a different ticks to + * usecs calculation. Not sure. */ /* * XXX: How should we deal with a late connection event? We need to determine * what we want to do under the following cases: * 1) The current connection event has not ended but a schedule item starts - * 2) The connection event start cb is called but we are later than we - * expected. What to do? If we cant transmit at correct point in slot we - * are hosed. Well, anchor point can get really messed up! */ /* @@ -218,6 +229,25 @@ STATS_NAME_END(ble_ll_conn_stats) static void ble_ll_conn_event_end(struct os_event *ev); +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 +static void +ble_ll_conn_calc_itvl_ticks(struct ble_ll_conn_sm *connsm) +{ + uint32_t ticks; + uint32_t usecs; + + /* + * Precalculate the number of ticks and remaining microseconds for + * the connection interval + */ + usecs = connsm->conn_itvl * BLE_LL_CONN_ITVL_USECS; + ticks = os_cputime_usecs_to_ticks(usecs); + connsm->conn_itvl_ticks = ticks; + connsm->conn_itvl_usecs = (uint8_t)(usecs - + os_cputime_ticks_to_usecs(ticks)); +} +#endif + /** * Get the event buffer allocated to send the connection complete event * when we are initiating. @@ -402,7 +432,6 @@ ble_ll_conn_calc_window_widening(struct ble_ll_conn_sm *connsm) window_widening = (total_sca_ppm * delta_msec) / 1000; } - /* XXX: spec gives 16 usecs error btw. Probably should add that in */ return window_widening; } @@ -689,7 +718,10 @@ ble_ll_conn_continue_rx_encrypt(void *arg) * the current connection event. The current connection event must end before * the next scheduled item. However, the current connection itself is not * in the scheduler list! Thus, we need to calculate the time at which the - * next connection will start and not overrun it. + * next connection will start (the schedule start time; not the anchor point) + * and not overrun it. + * + * Context: Interrupt * * @param connsm * @@ -698,13 +730,25 @@ ble_ll_conn_continue_rx_encrypt(void *arg) static uint32_t ble_ll_conn_get_next_sched_time(struct ble_ll_conn_sm *connsm) { +#if MYNEWT_VAL(OS_CPUTIME_FREQ) != 32768 uint32_t itvl; +#endif uint32_t ce_end; uint32_t next_sched_time; /* Calculate time at which next connection event will start */ - itvl = connsm->conn_itvl * BLE_LL_CONN_ITVL_USECS; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* NOTE: We dont care if this time is tick short. */ + ce_end = connsm->anchor_point + connsm->conn_itvl_ticks - + g_ble_ll_sched_offset_ticks; + if ((connsm->anchor_point_usecs + connsm->conn_itvl_usecs) >= 31) { + ++ce_end; + } +#else + itvl = (connsm->conn_itvl * BLE_LL_CONN_ITVL_USECS) - + XCVR_TX_SCHED_DELAY_USECS; ce_end = connsm->anchor_point + os_cputime_usecs_to_ticks(itvl); +#endif if (ble_ll_sched_next_time(&next_sched_time)) { if (CPUTIME_LT(next_sched_time, ce_end)) { @@ -1121,8 +1165,10 @@ ble_ll_conn_event_start_cb(struct ble_ll_sched_item *sch) { int rc; uint32_t usecs; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) != 32768 uint32_t wfr_time; - uint32_t txstart; +#endif + uint32_t start; struct ble_ll_conn_sm *connsm; /* XXX: note that we can extend end time here if we want. Look at this */ @@ -1151,9 +1197,15 @@ ble_ll_conn_event_start_cb(struct ble_ll_sched_item *sch) #endif if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* Set start time of transmission */ + start = sch->start_time + g_ble_ll_sched_offset_ticks; + rc = ble_phy_tx_set_start_time(start, sch->remainder); +#else /* Set start time of transmission */ - txstart = sch->start_time + os_cputime_usecs_to_ticks(XCVR_PROC_DELAY_USECS); - rc = ble_phy_tx_set_start_time(txstart); + start = sch->start_time + os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); + rc = ble_phy_tx_set_start_time(start); +#endif if (!rc) { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) if (CONN_F_ENCRYPTED(connsm)) { @@ -1187,11 +1239,13 @@ ble_ll_conn_event_start_cb(struct ble_ll_sched_item *sch) ble_phy_encrypt_disable(); } #endif - /* - * XXX: make sure I dont care that I get here early to start receiving. - * I could use events compare and all that shit to start rx. - */ +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* XXX: what is this really for the slave? */ + start = sch->start_time + g_ble_ll_sched_offset_ticks; + rc = ble_phy_rx_set_start_time(start, sch->remainder); +#else rc = ble_phy_rx(); +#endif if (rc) { /* End the connection event as we have no more buffers */ STATS_INC(ble_ll_conn_stats, slave_ce_failures); @@ -1207,15 +1261,32 @@ ble_ll_conn_event_start_cb(struct ble_ll_sched_item *sch) * Set the wait for response time. The anchor point is when we * expect the master to start transmitting. Worst-case, we expect * to hear a reply within the anchor point plus: - * -> the current tx window size - * -> The current window widening amount + * -> current tx window size + * -> current window widening amount (includes +/- 16 usec jitter) * -> Amount of time it takes to detect packet start. + * -> Some extra time (16 usec) to insure timing is OK */ +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* + * For the 32 kHz crystal, the amount of usecs we have to wait + * is not from the anchor point; we have to account for the time + * from when the receiver is enabled until the anchor point. The + * time we start before the anchor point is this: + * -> current window widening. + * -> up to one 32 kHz tick since we discard remainder. + * -> Up to one tick since the usecs to ticks calc can be off + * by up to one tick. + * NOTE: the 61 we add is for the two ticks mentioned above. + */ + usecs = connsm->slave_cur_tx_win_usecs + 40 + 16 + 61 + + (2 * connsm->slave_cur_window_widening); + ble_phy_wfr_enable(usecs); +#else usecs = connsm->slave_cur_tx_win_usecs + BLE_LL_WFR_USECS + connsm->slave_cur_window_widening; wfr_time = connsm->anchor_point + os_cputime_usecs_to_ticks(usecs); ble_ll_wfr_enable(wfr_time); - +#endif /* Set next wakeup time to connection event end time */ rc = BLE_LL_SCHED_STATE_RUNNING; } @@ -1246,15 +1317,20 @@ ble_ll_conn_event_start_cb(struct ble_ll_sched_item *sch) * @return int 0: not allowed to send 1: allowed to send */ static int -ble_ll_conn_can_send_next_pdu(struct ble_ll_conn_sm *connsm, uint32_t begtime) +ble_ll_conn_can_send_next_pdu(struct ble_ll_conn_sm *connsm, uint32_t begtime, + uint32_t add_usecs) { int rc; uint8_t rem_bytes; uint32_t ticks; + uint32_t usecs; uint32_t next_sched_time; struct os_mbuf *txpdu; struct os_mbuf_pkthdr *pkthdr; struct ble_mbuf_hdr *txhdr; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + uint32_t allowed_usecs; +#endif rc = 1; if (connsm->conn_role == BLE_LL_CONN_ROLE_MASTER) { @@ -1277,16 +1353,25 @@ ble_ll_conn_can_send_next_pdu(struct ble_ll_conn_sm *connsm, uint32_t begtime) if (rem_bytes > connsm->eff_max_tx_octets) { rem_bytes = connsm->eff_max_tx_octets; } - ticks = BLE_TX_DUR_USECS_M(rem_bytes); + usecs = BLE_TX_DUR_USECS_M(rem_bytes); } else { /* We will send empty pdu (just a LL header) */ - ticks = BLE_TX_DUR_USECS_M(0); + usecs = BLE_TX_DUR_USECS_M(0); } - ticks += (BLE_LL_IFS * 2) + connsm->eff_max_rx_time; - ticks = os_cputime_usecs_to_ticks(ticks); + usecs += (BLE_LL_IFS * 2) + connsm->eff_max_rx_time; + +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + ticks = (uint32_t)(next_sched_time - begtime); + allowed_usecs = os_cputime_ticks_to_usecs(ticks); + if ((usecs + add_usecs) >= allowed_usecs) { + rc = 0; + } +#else + ticks = os_cputime_usecs_to_ticks(usecs); if ((begtime + ticks) >= next_sched_time) { rc = 0; } +#endif } return rc; @@ -1347,7 +1432,15 @@ ble_ll_conn_master_init(struct ble_ll_conn_sm *connsm, connsm->conn_role = BLE_LL_CONN_ROLE_MASTER; /* Set default ce parameters */ +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* + * XXX: for now, we need twice the transmit window as our calculations + * for the transmit window offset could be off. + */ + connsm->tx_win_size = BLE_LL_CONN_TX_WIN_MIN + 1; +#else connsm->tx_win_size = BLE_LL_CONN_TX_WIN_MIN; +#endif connsm->tx_win_off = 0; connsm->master_sca = MYNEWT_VAL(BLE_LL_MASTER_SCA); @@ -1482,6 +1575,10 @@ ble_ll_conn_sm_new(struct ble_ll_conn_sm *connsm) connsm); #endif +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + ble_ll_conn_calc_itvl_ticks(connsm); +#endif + /* Add to list of active connections */ SLIST_INSERT_HEAD(&g_ble_ll_conn_active_list, connsm, act_sle); } @@ -1629,6 +1726,11 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm) uint32_t cur_ww; uint32_t max_ww; struct ble_ll_conn_upd_req *upd; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + uint32_t ticks; + uint32_t usecs; +#endif + /* XXX: deal with connection request procedure here as well */ ble_ll_conn_chk_csm_flags(connsm); @@ -1647,7 +1749,24 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm) connsm->event_cntr += latency; /* Set next connection event start time */ +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* We can use pre-calculated values for one interval if latency is 1. */ + if (latency == 1) { + connsm->anchor_point += connsm->conn_itvl_ticks; + connsm->anchor_point_usecs += connsm->conn_itvl_usecs; + } else { + uint32_t ticks; + ticks = os_cputime_usecs_to_ticks(itvl); + connsm->anchor_point += ticks; + connsm->anchor_point_usecs += (itvl - os_cputime_ticks_to_usecs(ticks)); + } + if (connsm->anchor_point_usecs >= 31) { + ++connsm->anchor_point; + connsm->anchor_point_usecs -= 31; + } +#else connsm->anchor_point += os_cputime_usecs_to_ticks(itvl); +#endif /* * If a connection update has been scheduled and the event counter @@ -1669,15 +1788,30 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm) connsm->csmflags.cfbit.host_expects_upd_event = 1; } - connsm->conn_itvl = upd->interval; connsm->supervision_tmo = upd->timeout; connsm->slave_latency = upd->latency; connsm->tx_win_size = upd->winsize; connsm->slave_cur_tx_win_usecs = connsm->tx_win_size * BLE_LL_CONN_TX_WIN_USECS; connsm->tx_win_off = upd->winoffset; + connsm->conn_itvl = upd->interval; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + ble_ll_conn_calc_itvl_ticks(connsm); + if (upd->winoffset != 0) { + usecs = upd->winoffset * BLE_LL_CONN_ITVL_USECS; + ticks = os_cputime_usecs_to_ticks(usecs); + connsm->anchor_point += ticks; + usecs = usecs - os_cputime_ticks_to_usecs(ticks); + connsm->anchor_point_usecs += usecs; + if (connsm->anchor_point_usecs >= 31) { + ++connsm->anchor_point; + connsm->anchor_point_usecs -= 31; + } + } +#else connsm->anchor_point += os_cputime_usecs_to_ticks(upd->winoffset * BLE_LL_CONN_ITVL_USECS); +#endif /* Reset the starting point of the connection supervision timeout */ connsm->last_rxd_pdu_cputime = connsm->anchor_point; @@ -1736,20 +1870,35 @@ ble_ll_conn_next_event(struct ble_ll_conn_sm *connsm) * Calculate ce end time. For a slave, we need to add window widening and * the transmit window if we still have one. */ - itvl = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_USECS_PER_SLOT; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + itvl = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT; if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { cur_ww = ble_ll_conn_calc_window_widening(connsm); max_ww = (connsm->conn_itvl * (BLE_LL_CONN_ITVL_USECS/2)) - BLE_LL_IFS; if (cur_ww >= max_ww) { return -1; } + cur_ww += BLE_LL_JITTER_USECS; connsm->slave_cur_window_widening = cur_ww; + itvl += os_cputime_usecs_to_ticks(cur_ww + connsm->slave_cur_tx_win_usecs); + } + itvl -= g_ble_ll_sched_offset_ticks; + connsm->ce_end_time = connsm->anchor_point + itvl; +#else + itvl = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_USECS_PER_SLOT; + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + cur_ww = ble_ll_conn_calc_window_widening(connsm); + max_ww = (connsm->conn_itvl * (BLE_LL_CONN_ITVL_USECS/2)) - BLE_LL_IFS; + if (cur_ww >= max_ww) { + return -1; + } + connsm->slave_cur_window_widening = cur_ww + BLE_LL_JITTER_USECS; itvl += cur_ww + connsm->slave_cur_tx_win_usecs; } else { - /* We adjust end time for connection to end of time slot */ itvl -= XCVR_TX_SCHED_DELAY_USECS; } connsm->ce_end_time = connsm->anchor_point + os_cputime_usecs_to_ticks(itvl); +#endif return 0; } @@ -1799,9 +1948,38 @@ ble_ll_conn_created(struct ble_ll_conn_sm *connsm, struct ble_mbuf_hdr *rxhdr) */ rc = 1; if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* + * With a 32.768 kHz crystal we dont care about the remaining usecs + * when setting last anchor point. The only thing last anchor is used + * for is to calculate window widening. The effect of this is + * negligible. + */ + connsm->last_anchor_point = rxhdr->beg_cputime; + + usecs = rxhdr->rem_usecs + 1250 + + (connsm->tx_win_off * BLE_LL_CONN_TX_WIN_USECS) + + BLE_TX_DUR_USECS_M(BLE_CONNECT_REQ_LEN); + + /* Anchor point is cputime. */ + endtime = os_cputime_usecs_to_ticks(usecs); + connsm->anchor_point = rxhdr->beg_cputime + endtime; + connsm->anchor_point_usecs = usecs - os_cputime_ticks_to_usecs(endtime); + if (connsm->anchor_point_usecs == 31) { + ++connsm->anchor_point; + connsm->anchor_point_usecs = 0; + } + + connsm->slave_cur_tx_win_usecs = + connsm->tx_win_size * BLE_LL_CONN_TX_WIN_USECS; + + connsm->ce_end_time = connsm->anchor_point + + (MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT) + + os_cputime_usecs_to_ticks(connsm->slave_cur_tx_win_usecs) + 1; +#else + connsm->last_anchor_point = rxhdr->beg_cputime; endtime = rxhdr->beg_cputime + os_cputime_usecs_to_ticks(BLE_TX_DUR_USECS_M(BLE_CONNECT_REQ_LEN)); - connsm->last_anchor_point = endtime; connsm->slave_cur_tx_win_usecs = connsm->tx_win_size * BLE_LL_CONN_TX_WIN_USECS; usecs = 1250 + (connsm->tx_win_off * BLE_LL_CONN_TX_WIN_USECS); @@ -1810,7 +1988,8 @@ ble_ll_conn_created(struct ble_ll_conn_sm *connsm, struct ble_mbuf_hdr *rxhdr) (MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS) * BLE_LL_SCHED_USECS_PER_SLOT); connsm->ce_end_time = connsm->anchor_point + os_cputime_usecs_to_ticks(usecs); - connsm->slave_cur_window_widening = 0; +#endif + connsm->slave_cur_window_widening = BLE_LL_JITTER_USECS; /* Start the scheduler for the first connection event */ while (ble_ll_sched_slave_new(connsm)) { @@ -1958,6 +2137,7 @@ ble_ll_conn_event_end(struct os_event *ev) } else { tmo = connsm->supervision_tmo * BLE_HCI_CONN_SPVN_TMO_UNITS * 1000UL; } + /* XXX: Convert to ticks to usecs calculation instead??? */ tmo = os_cputime_usecs_to_ticks(tmo); if ((int32_t)(connsm->anchor_point - connsm->last_rxd_pdu_cputime) >= tmo) { ble_ll_conn_end(connsm, BLE_ERR_CONN_SPVN_TMO); @@ -2251,7 +2431,6 @@ ble_ll_init_rx_isr_end(uint8_t *rxbuf, uint8_t crcok, uint8_t *init_addr = NULL; uint8_t pyld_len; uint8_t inita_is_rpa; - uint32_t endtime; struct os_mbuf *rxpdu; struct ble_ll_conn_sm *connsm; @@ -2366,10 +2545,7 @@ ble_ll_init_rx_isr_end(uint8_t *rxbuf, uint8_t crcok, } /* Attempt to schedule new connection. Possible that this might fail */ - endtime = ble_hdr->beg_cputime + - os_cputime_usecs_to_ticks(BLE_TX_DUR_USECS_M(pyld_len)); - if (!ble_ll_sched_master_new(connsm, endtime, - MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS))) { + if (!ble_ll_sched_master_new(connsm, ble_hdr, pyld_len)) { /* Setup to transmit the connect request */ rc = ble_ll_conn_request_send(addr_type, adv_addr, connsm->tx_win_off, index); @@ -2392,8 +2568,7 @@ init_rx_isr_exit: */ rxpdu = ble_ll_rxpdu_alloc(pyld_len + BLE_LL_PDU_HDR_LEN); if (rxpdu == NULL) { - ble_phy_disable(); - ble_phy_rx(); + ble_phy_restart_rx(); rc = 0; } else { ble_phy_rxpdu_copy(rxbuf, rxpdu); @@ -2485,6 +2660,9 @@ ble_ll_conn_rx_isr_start(struct ble_mbuf_hdr *rxhdr, uint32_t aa) connsm->csmflags.cfbit.slave_set_last_anchor = 0; connsm->last_anchor_point = rxhdr->beg_cputime; connsm->anchor_point = connsm->last_anchor_point; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + connsm->anchor_point_usecs = rxhdr->rem_usecs; +#endif } } return 1; @@ -2646,6 +2824,7 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) uint8_t opcode = 0; uint8_t rx_pyld_len; uint32_t endtime; + uint32_t add_usecs; struct os_mbuf *txpdu; struct ble_ll_conn_sm *connsm; struct os_mbuf *rxpdu; @@ -2676,8 +2855,14 @@ ble_ll_conn_rx_isr_end(uint8_t *rxbuf, struct ble_mbuf_hdr *rxhdr) } /* Calculate the end time of the received PDU */ +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + endtime = rxhdr->beg_cputime; + add_usecs = rxhdr->rem_usecs + BLE_TX_DUR_USECS_M(rx_pyld_len); +#else endtime = rxhdr->beg_cputime + os_cputime_usecs_to_ticks(BLE_TX_DUR_USECS_M(rx_pyld_len)); + add_usecs = 0; +#endif /* * Check the packet CRC. A connection event can continue even if the @@ -2848,7 +3033,7 @@ chk_rx_terminate_ind: if (rx_pyld_len && CONN_F_ENCRYPTED(connsm)) { rx_pyld_len += BLE_LL_DATA_MIC_LEN; } - if (reply && ble_ll_conn_can_send_next_pdu(connsm, endtime)) { + if (reply && ble_ll_conn_can_send_next_pdu(connsm, endtime, add_usecs)) { rc = ble_ll_conn_tx_data_pdu(connsm); } @@ -3032,7 +3217,6 @@ ble_ll_conn_set_global_chanmap(uint8_t num_used_chans, uint8_t *chanmap) * Context: Link Layer * * @param rxbuf Pointer to received Connect Request PDU - * @param conn_req_end receive end time of connect request * * @return 0: connection not started; 1 connecton started */ @@ -3256,3 +3440,5 @@ ble_ll_conn_module_init(void) /* Call reset to finish reset of initialization */ ble_ll_conn_module_reset(); } + + http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/net/nimble/controller/src/ble_ll_conn_priv.h ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_conn_priv.h b/net/nimble/controller/src/ble_ll_conn_priv.h index 0913892..02cac60 100644 --- a/net/nimble/controller/src/ble_ll_conn_priv.h +++ b/net/nimble/controller/src/ble_ll_conn_priv.h @@ -42,12 +42,13 @@ extern "C" { #define BLE_LL_CONN_INITIAL_OFFSET (1250) #define BLE_LL_CONN_ITVL_USECS (1250) #define BLE_LL_CONN_TX_WIN_USECS (1250) +#define BLE_LL_CONN_TX_OFF_USECS (1250) #define BLE_LL_CONN_CE_USECS (625) #define BLE_LL_CONN_TX_WIN_MIN (1) /* in tx win units */ #define BLE_LL_CONN_SLAVE_LATENCY_MAX (499) /* Connection request duration (in usecs) */ -#define BLE_LL_CONN_REQ_DURATION (352) +#define BLE_LL_CONN_REQ_DURATION (352) /* 1 Mbps only */ /* Connection handle range */ #define BLE_LL_CONN_MAX_CONN_HANDLE (0x0EFF) http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/net/nimble/controller/src/ble_ll_scan.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_scan.c b/net/nimble/controller/src/ble_ll_scan.c index f732753..306b8cc 100644 --- a/net/nimble/controller/src/ble_ll_scan.c +++ b/net/nimble/controller/src/ble_ll_scan.c @@ -588,8 +588,14 @@ ble_ll_scan_start(struct ble_ll_scan_sm *scansm, uint8_t chan) } #endif - /* Start receiving */ - rc = ble_phy_rx(); +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* XXX: probably need to make sure hfxo is running too */ + /* XXX: can make this better; want to just start asap. */ + rc = ble_phy_rx_set_start_time(os_cputime_get32() + + g_ble_ll_sched_offset_ticks, 0); +#else + rc = ble_phy_rx(); +#endif if (!rc) { /* Enable/disable whitelisting */ if (scansm->scan_filt_policy & 1) { @@ -929,8 +935,7 @@ ble_ll_scan_rx_isr_end(struct os_mbuf *rxpdu, uint8_t crcok) if (scansm->scan_rsp_pending) { ble_ll_scan_req_backoff(scansm, 0); } - ble_phy_disable(); - ble_phy_rx(); + ble_phy_restart_rx(); return 0; } @@ -1081,8 +1086,6 @@ ble_ll_scan_wfr_timer_exp(void) { struct ble_ll_scan_sm *scansm; - ble_phy_disable(); - /* * If we timed out waiting for a response, the scan response pending * flag should be set. Deal with scan backoff. Put device back into rx. @@ -1091,7 +1094,8 @@ ble_ll_scan_wfr_timer_exp(void) if (scansm->scan_rsp_pending) { ble_ll_scan_req_backoff(scansm, 0); } - ble_phy_rx(); + + ble_phy_restart_rx(); } /** http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/net/nimble/controller/src/ble_ll_sched.c ---------------------------------------------------------------------- diff --git a/net/nimble/controller/src/ble_ll_sched.c b/net/nimble/controller/src/ble_ll_sched.c index dd42c3f..2c18e0c 100644 --- a/net/nimble/controller/src/ble_ll_sched.c +++ b/net/nimble/controller/src/ble_ll_sched.c @@ -21,6 +21,7 @@ #include <string.h> #include "os/os.h" #include "os/os_cputime.h" +#include "bsp.h" #include "ble/xcvr.h" #include "controller/ble_phy.h" #include "controller/ble_ll.h" @@ -32,13 +33,17 @@ /* XXX: this is temporary. Not sure what I want to do here */ struct hal_timer g_ble_ll_sched_timer; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 +uint8_t g_ble_ll_sched_offset_ticks; +#endif + #define BLE_LL_SCHED_ADV_WORST_CASE_USECS \ (BLE_LL_SCHED_MAX_ADV_PDU_USECS + BLE_LL_IFS + BLE_LL_SCHED_ADV_MAX_USECS \ + XCVR_TX_SCHED_DELAY_USECS) - #if (BLE_LL_SCHED_DEBUG == 1) int32_t g_ble_ll_sched_max_late; +int32_t g_ble_ll_sched_max_early; #endif /* XXX: TODO: @@ -155,6 +160,17 @@ ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm) /* Get schedule element from connection */ sch = &connsm->conn_sch; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* Set schedule start and end times */ + sch->start_time = connsm->anchor_point - g_ble_ll_sched_offset_ticks; + if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { + usecs = connsm->slave_cur_window_widening; + sch->start_time -= (os_cputime_usecs_to_ticks(usecs) + 1); + sch->remainder = 0; + } else { + sch->remainder = connsm->anchor_point_usecs; + } +#else /* Set schedule start and end times */ if (connsm->conn_role == BLE_LL_CONN_ROLE_SLAVE) { usecs = XCVR_RX_SCHED_DELAY_USECS; @@ -163,6 +179,7 @@ ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm) usecs = XCVR_TX_SCHED_DELAY_USECS; } sch->start_time = connsm->anchor_point - os_cputime_usecs_to_ticks(usecs); +#endif sch->end_time = connsm->ce_end_time; /* Better be past current time or we just leave */ @@ -247,67 +264,108 @@ ble_ll_sched_conn_reschedule(struct ble_ll_conn_sm *connsm) return rc; } +/** + * Called to schedule a connection when the current role is master. + * + * Context: Interrupt + * + * @param connsm + * @param ble_hdr + * @param pyld_len + * + * @return int + */ int -ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, uint32_t adv_rxend, - uint8_t req_slots) +ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, + struct ble_mbuf_hdr *ble_hdr, uint8_t pyld_len) { int rc; os_sr_t sr; - uint32_t tps; + uint8_t req_slots; uint32_t initial_start; uint32_t earliest_start; uint32_t earliest_end; uint32_t dur; uint32_t itvl_t; - uint32_t ce_end_time; + uint32_t adv_rxend; struct ble_ll_sched_item *entry; struct ble_ll_sched_item *sch; - /* Better have a connsm */ - assert(connsm != NULL); - /* Get schedule element from connection */ rc = -1; sch = &connsm->conn_sch; - + req_slots = MYNEWT_VAL(BLE_LL_CONN_INIT_SLOTS); + +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* XXX: + * The calculations for the 32kHz crystal bear alot of explanation. The + * earliest possible time that the master can start the connection with a + * slave is 1.25 msecs from the end of the connection request. The + * connection request is sent an IFS time from the end of the advertising + * packet that was received plus the time it takes to send the connection + * request. At 1 Mbps, this is 1752 usecs, or 57.41 ticks. Using 57 ticks + * makes us off ~13 usecs. Since we dont want to actually calculate the + * receive end time tick (this would take too long), we assume the end of + * the advertising PDU is 'now' (we call os_cputime_get32). We dont know + * how much time it will take to service the ISR but if we are more than the + * rx to tx time of the chip we will not be successful transmitting the + * connect request. All this means is that we presume that the slave will + * receive the connect request later than we expect but no earlier than + * 13 usecs before (this is important). + * + * The code then attempts to schedule the connection at the + * earliest time although this may not be possible. When the actual + * schedule start time is determined, the master has to determine if this + * time is more than a transmit window offset interval (1.25 msecs). The + * master has to tell the slave how many transmit window offsets there are + * from the earliest possible time to when the actual transmit start will + * occur. Later in this function you will see the calculation. The actual + * transmission start has to occur within the transmit window. The transmit + * window interval is in units of 1.25 msecs and has to be at least 1. To + * make things a bit easier (but less power efficient for the slave), we + * use a transmit window of 2. We do this because we dont quite know the + * exact start of the transmission and if we are too early or too late we + * could miss the transmit window. A final note: the actual transmission + * start (the anchor point) is sched offset ticks from the schedule start + * time. We dont add this to the calculation when calculating the window + * offset. The reason we dont do this is we want to insure we transmit + * after the window offset we tell the slave. For example, say we think + * we are transmitting 1253 usecs from the earliest start. This would cause + * us to send a transmit window offset of 1. Since we are actually + * transmitting earlier than the slave thinks we could end up transmitting + * before the window offset. Transmitting later is fine since we have the + * transmit window to do so. Transmitting before is bad, since the slave + * wont be listening. We could do better calculation if we wanted to use + * a transmit window of 1 as opposed to 2, but for now we dont care. + */ + dur = req_slots * BLE_LL_SCHED_32KHZ_TICKS_PER_SLOT; + adv_rxend = os_cputime_get32(); + earliest_start = adv_rxend + 57; /* XXX: only works for 1 Mbps */ + earliest_end = earliest_start + dur; + itvl_t = connsm->conn_itvl_ticks; +#else + adv_rxend = ble_hdr->beg_cputime + + os_cputime_usecs_to_ticks(BLE_TX_DUR_USECS_M(pyld_len)); /* * The earliest start time is 1.25 msecs from the end of the connect * request transmission. Note that adv_rxend is the end of the received * advertisement, so we need to add an IFS plus the time it takes to send - * the connection request + * the connection request. The 1.25 msecs starts from the end of the conn + * request. */ dur = os_cputime_usecs_to_ticks(req_slots * BLE_LL_SCHED_USECS_PER_SLOT); earliest_start = adv_rxend + os_cputime_usecs_to_ticks(BLE_LL_IFS + BLE_LL_CONN_REQ_DURATION + BLE_LL_CONN_INITIAL_OFFSET); earliest_end = earliest_start + dur; - itvl_t = os_cputime_usecs_to_ticks(connsm->conn_itvl * BLE_LL_CONN_ITVL_USECS); +#endif /* We have to find a place for this schedule */ OS_ENTER_CRITICAL(sr); /* The schedule item must occur after current running item (if any) */ sch->start_time = earliest_start; - - /* - * If we are currently in a connection, we add one slot time to the - * earliest start so we can end the connection reasonably. - */ - if (ble_ll_state_get() == BLE_LL_STATE_CONNECTION) { - tps = os_cputime_usecs_to_ticks(BLE_LL_SCHED_USECS_PER_SLOT); - ce_end_time = ble_ll_conn_get_ce_end_time(); - while ((int32_t)(ce_end_time - os_cputime_get32()) < 0) { - ce_end_time += tps; - } - - /* Start at next slot boundary past earliest */ - while ((int32_t)(ce_end_time - earliest_start) < 0) { - ce_end_time += tps; - } - earliest_start = ce_end_time; - earliest_end = earliest_start + dur; - } initial_start = earliest_start; if (!ble_ll_sched_insert_if_empty(sch)) { @@ -338,6 +396,7 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, uint32_t adv_rxend, } } + /* Must be able to schedule within one connection interval */ if (!entry) { if ((earliest_start - initial_start) <= itvl_t) { rc = 0; @@ -346,18 +405,38 @@ ble_ll_sched_master_new(struct ble_ll_conn_sm *connsm, uint32_t adv_rxend, } if (!rc) { - /* calculate number of connection intervals before start */ + /* calculate number of window offsets. Each offset is 1.25 ms */ sch->enqueued = 1; - connsm->tx_win_off = (earliest_start - initial_start) / - os_cputime_usecs_to_ticks(BLE_LL_CONN_ITVL_USECS); +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* + * NOTE: we dont add sched offset ticks as we want to under-estimate + * the transmit window slightly since the window size is currently + * 2 when using a 32768 crystal. + */ + dur = os_cputime_ticks_to_usecs(earliest_start - initial_start); + connsm->tx_win_off = dur / BLE_LL_CONN_TX_OFF_USECS; +#else + dur = os_cputime_ticks_to_usecs(earliest_start - initial_start); + dur += XCVR_TX_SCHED_DELAY_USECS; + connsm->tx_win_off = dur / BLE_LL_CONN_TX_OFF_USECS; +#endif } } if (!rc) { sch->start_time = earliest_start; sch->end_time = earliest_end; +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* + * Since we have the transmit window to transmit in, we dont need + * to set the anchor point usecs; just transmit to the nearest tick. + */ + connsm->anchor_point = earliest_start + g_ble_ll_sched_offset_ticks; + connsm->anchor_point_usecs = 0; +#else connsm->anchor_point = earliest_start + os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); +#endif connsm->ce_end_time = earliest_end; } @@ -385,10 +464,22 @@ ble_ll_sched_slave_new(struct ble_ll_conn_sm *connsm) sch = &connsm->conn_sch; /* Set schedule start and end times */ +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + /* + * XXX: for now, we dont care about anchor point usecs for the slave. It + * does not matter if we turn on the receiver up to one tick before w + * need to. We also subtract one extra tick since the conversion from + * usecs to ticks could be off by up to 1 tick. + */ + sch->start_time = connsm->anchor_point - g_ble_ll_sched_offset_ticks - + os_cputime_usecs_to_ticks(connsm->slave_cur_window_widening) - 1; +#else sch->start_time = connsm->anchor_point - os_cputime_usecs_to_ticks(XCVR_RX_SCHED_DELAY_USECS + connsm->slave_cur_window_widening); +#endif sch->end_time = connsm->ce_end_time; + sch->remainder = 0; /* We have to find a place for this schedule */ OS_ENTER_CRITICAL(sr); @@ -450,9 +541,6 @@ ble_ll_sched_adv_new(struct ble_ll_sched_item *sch) { int rc; os_sr_t sr; - uint8_t ll_state; - int32_t ticks; - uint32_t ce_end_time; uint32_t adv_start; uint32_t duration; struct ble_ll_sched_item *entry; @@ -463,39 +551,12 @@ ble_ll_sched_adv_new(struct ble_ll_sched_item *sch) orig = sch; OS_ENTER_CRITICAL(sr); - - /* - * If we are currently in a connection, we add one slot time to the - * earliest start so we can end the connection reasonably. - */ - ll_state = ble_ll_state_get(); - if (ll_state == BLE_LL_STATE_CONNECTION) { - ticks = (int32_t)os_cputime_usecs_to_ticks(BLE_LL_SCHED_MAX_TXRX_SLOT); - ce_end_time = ble_ll_conn_get_ce_end_time(); - if ((int32_t)(ce_end_time - sch->start_time) < ticks) { - ce_end_time += ticks; - } - sch->start_time = ce_end_time; - sch->end_time = ce_end_time + duration; - } -#if MYNEWT_VAL(BLE_MULTI_ADV_SUPPORT) - else if ((ll_state == BLE_LL_STATE_ADV) && (BLE_LL_ADV_INSTANCES > 1)) { - /* - * Since we currently dont know how long this item might be scheduled - * for we add what we think the worst-case time for the advertising - * scheduled item to be over. We add in a IFS for good measure. - */ - sch->start_time += BLE_LL_SCHED_MAX_ADV_PDU_USECS + BLE_LL_IFS + - BLE_LL_SCHED_ADV_MAX_USECS + XCVR_TX_SCHED_DELAY_USECS; - sch->end_time = sch->start_time + duration; - } -#endif - entry = ble_ll_sched_insert_if_empty(sch); if (!entry) { rc = 0; adv_start = sch->start_time; } else { + /* XXX: no need to stop timer if not first on list. Modify code? */ os_cputime_timer_stop(&g_ble_ll_sched_timer); TAILQ_FOREACH(entry, &g_ble_ll_sched_q, link) { /* We can insert if before entry in list */ @@ -531,12 +592,6 @@ ble_ll_sched_adv_new(struct ble_ll_sched_item *sch) OS_EXIT_CRITICAL(sr); - /* XXX: some things to test. I am not sure that if we are passed the - output compare that we actually get the interrupt. */ - /* XXX: I am not sure that if we receive a packet while scanning - * that we actually go back to scanning. I need to make sure - we re-enable the receive. Put an event in the log! */ - os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); return rc; @@ -784,26 +839,33 @@ ble_ll_sched_execute_item(struct ble_ll_sched_item *sch) void ble_ll_sched_run(void *arg) { - int32_t dt; struct ble_ll_sched_item *sch; /* Look through schedule queue */ - while ((sch = TAILQ_FIRST(&g_ble_ll_sched_q)) != NULL) { + sch = TAILQ_FIRST(&g_ble_ll_sched_q); + if (sch) { +#if (BLE_LL_SCHED_DEBUG == 1) + int32_t dt; + /* Make sure we have passed the start time of the first event */ dt = (int32_t)(os_cputime_get32() - sch->start_time); - if (dt >= 0) { -#if (BLE_LL_SCHED_DEBUG == 1) - if (dt > g_ble_ll_sched_max_late) { - g_ble_ll_sched_max_late = dt; - } + if (dt > g_ble_ll_sched_max_late) { + g_ble_ll_sched_max_late = dt; + } + if (dt < g_ble_ll_sched_max_early) { + g_ble_ll_sched_max_early = dt; + } #endif - /* Remove schedule item and execute the callback */ - TAILQ_REMOVE(&g_ble_ll_sched_q, sch, link); - sch->enqueued = 0; - ble_ll_sched_execute_item(sch); - } else { + + /* Remove schedule item and execute the callback */ + TAILQ_REMOVE(&g_ble_ll_sched_q, sch, link); + sch->enqueued = 0; + ble_ll_sched_execute_item(sch); + + /* Restart if there is an item on the schedule */ + sch = TAILQ_FIRST(&g_ble_ll_sched_q); + if (sch) { os_cputime_timer_start(&g_ble_ll_sched_timer, sch->start_time); - break; } } } @@ -857,6 +919,24 @@ ble_ll_sched_stop(void) int ble_ll_sched_init(void) { + /* + * Initialize max early to large negative number. This is used + * to determine the worst-case "early" time the schedule was called. Dont + * expect this to be less than -3 or -4. + */ +#if (BLE_LL_SCHED_DEBUG == 1) + g_ble_ll_sched_max_early = -50000; +#endif + + /* + * This is the offset from the start of the scheduled item until the actual + * tx/rx should occur, in ticks. We also "round up" to the nearest tick. + */ +#if MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768 + g_ble_ll_sched_offset_ticks = + os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS + 30); +#endif + /* Initialize cputimer for the scheduler */ os_cputime_timer_init(&g_ble_ll_sched_timer, ble_ll_sched_run, NULL); return 0; http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/94a59a3c/net/nimble/include/nimble/ble.h ---------------------------------------------------------------------- diff --git a/net/nimble/include/nimble/ble.h b/net/nimble/include/nimble/ble.h index b514131..6f29fd4 100644 --- a/net/nimble/include/nimble/ble.h +++ b/net/nimble/include/nimble/ble.h @@ -96,6 +96,9 @@ struct ble_mbuf_hdr struct ble_mbuf_hdr_txinfo txinfo; }; uint32_t beg_cputime; +#if (MYNEWT_VAL(OS_CPUTIME_FREQ) == 32768) + uint32_t rem_usecs; +#endif }; #define BLE_MBUF_HDR_CRC_OK(hdr) \
