diff --git a/nimble/controller/include/controller/ble_phy.h b/nimble/controller/include/controller/ble_phy.h index fe70044234..6c9878a542 100644 --- a/nimble/controller/include/controller/ble_phy.h +++ b/nimble/controller/include/controller/ble_phy.h @@ -78,6 +78,60 @@ struct os_mbuf; /* Maximun PDU length. Includes LL header of 2 bytes and 255 bytes payload. */ #define BLE_PHY_MAX_PDU_LEN (257) +#if MYNEWT_VAL(BLE_LL_CHANNEL_SOUNDING) +#define BLE_PHY_CS_TRANSM_MODE_SYNC (0) +#define BLE_PHY_CS_TRANSM_MODE_TONE (1) + +#define BLE_PHY_CS_TONE_MODE_PM (0) +#define BLE_PHY_CS_TONE_MODE_FM (1) + +#define BLE_PHY_CS_STATUS_CONTINUE (0) +#define BLE_PHY_CS_STATUS_COMPLETE (1) +#define BLE_PHY_CS_STATUS_SYNC_LOST (2) + +struct ble_phy_cs_transmission { + struct ble_phy_cs_transmission *next; + uint8_t *rtt_sequence; + uint32_t duration_usecs; + uint32_t aa; + uint16_t end_tifs; + uint8_t channel; + uint8_t mode; + uint8_t is_tx; + uint8_t tone_mode; + uint8_t rtt_sequence_len; +}; + +struct ble_phy_cs_sync_results { + uint32_t time_of_arrival_ns; + uint32_t time_of_departure_ns; + int16_t rssi; +}; + +struct ble_phy_cs_tone_results { + int16_t measured_freq_offset; + int16_t I16; + int16_t Q16; + uint16_t PHASE; +}; + +struct ble_phy_cs_subevent_results { + uint8_t status; +}; + +#if MYNEWT_VAL(BLE_PHY_CHANNEL_SOUNDING) +/* Configure the PHY for CS subevent sequence */ +int ble_phy_cs_subevent_start(struct ble_phy_cs_transmission *transm, uint32_t cputime, + uint8_t rem_usecs, uint8_t phy_mode); +#else +static inline int ble_phy_cs_subevent_start(struct ble_phy_cs_transmission *transm, uint32_t cputime, + uint8_t rem_usecs, uint8_t phy_mode) +{ + return 0; +} +#endif +#endif + /* Wait for response timer */ typedef void (*ble_phy_tx_end_func)(void *arg); diff --git a/nimble/drivers/nrf5x/pkg.yml b/nimble/drivers/nrf5x/pkg.yml index b99e669248..90893277c1 100644 --- a/nimble/drivers/nrf5x/pkg.yml +++ b/nimble/drivers/nrf5x/pkg.yml @@ -30,7 +30,22 @@ pkg.deps: - nimble - nimble/controller -pkg.ign_dirs.'MCU_TARGET=="nRF5340_NET"': - - nrf52 -pkg.ign_dirs.'MCU_TARGET!="nRF5340_NET"': - - nrf53 +pkg.source_files: + - "src/ble_hw.c" + - "src/ble_phy.c" + - "src/ble_phy_trace.c" + +pkg.source_files.'MCU_TARGET=="nRF52810" || MCU_TARGET=="nRF52811" || MCU_TARGET=="nRF52832" || MCU_TARGET=="nRF52840"': + - "src/nrf52/phy.c" + +pkg.source_files.'MCU_TARGET=="nRF5340_NET"': + - "src/nrf53/phy.c" + +pkg.source_files.'MCU_TARGET=="nRF54L15"': + - "src/nrf54l/phy.c" + +pkg.source_files.'BLE_CHANNEL_SOUNDING': + - "src/ble_phy_cs.c" + +pkg.init.'BLE_PHY_CS_TEST_MODE_CONTINUES_TONE': + ble_phy_cs_tx_continuous_tone: $after:ble_transport_ll_init diff --git a/nimble/drivers/nrf5x/src/ble_hw.c b/nimble/drivers/nrf5x/src/ble_hw.c index f8cd59f098..44b566af1a 100644 --- a/nimble/drivers/nrf5x/src/ble_hw.c +++ b/nimble/drivers/nrf5x/src/ble_hw.c @@ -36,9 +36,25 @@ #include #endif #include "os/os_trace_api.h" +#ifndef NRF54L_SERIES #include +#endif #include "hal/nrf_ecb.h" +#ifdef NRF54L_SERIES +#include + +#define NRF_ECB NRF_ECB00 +#define NRF_AAR NRF_AAR00 + +struct nrf_ecb_job_list { + nrf_vdma_job_t in[2]; + nrf_vdma_job_t out[2]; +}; + +static struct nrf_ecb_job_list g_ecb_job_list; +#endif + /* Total number of resolving list elements */ #define BLE_HW_RESOLV_LIST_SIZE (16) @@ -276,6 +292,47 @@ ble_hw_encrypt_block(struct ble_encryption_block *ecb) uint32_t end; uint32_t err; +#ifdef NRF54L_SERIES + /* Stop ECB */ + nrf_ecb_task_trigger(NRF_ECB, NRF_ECB_TASK_STOP); + + /* Init job lists */ + g_ecb_job_list.in[0].p_buffer = ecb->plain_text; + g_ecb_job_list.in[0].attributes = NRF_VDMA_ATTRIBUTE_CRC; + g_ecb_job_list.in[0].size = BLE_ENC_BLOCK_SIZE; + memset(&g_ecb_job_list.in[1], 0, sizeof(g_ecb_job_list.in[1])); + + g_ecb_job_list.out[0].p_buffer = ecb->cipher_text; + g_ecb_job_list.out[0].attributes = NRF_VDMA_ATTRIBUTE_CRC; + g_ecb_job_list.out[0].size = BLE_ENC_BLOCK_SIZE; + memset(&g_ecb_job_list.out[1], 0, sizeof(g_ecb_job_list.out[1])); + + NRF_ECB->KEY.VALUE[3] = get_be32(&ecb->key[0]); + NRF_ECB->KEY.VALUE[2] = get_be32(&ecb->key[4]); + NRF_ECB->KEY.VALUE[1] = get_be32(&ecb->key[8]); + NRF_ECB->KEY.VALUE[0] = get_be32(&ecb->key[12]); + + NRF_ECB->EVENTS_END = 0; + NRF_ECB->EVENTS_ERROR = 0; + NRF_ECB->IN.PTR = (uint32_t)g_ecb_job_list.in; + NRF_ECB->OUT.PTR = (uint32_t)g_ecb_job_list.out; + + /* Start ECB */ + nrf_ecb_task_trigger(NRF_ECB, NRF_ECB_TASK_START); + + /* Wait till error or done */ + rc = 0; + while (1) { + end = NRF_ECB->EVENTS_END; + err = NRF_ECB->EVENTS_ERROR; + if (end || err) { + if (err) { + rc = -1; + } + break; + } + } +#else /* Stop ECB */ nrf_ecb_task_trigger(NRF_ECB, NRF_ECB_TASK_STOPECB); /* XXX: does task stop clear these counters? Anyway to do this quicker? */ @@ -301,7 +358,7 @@ ble_hw_encrypt_block(struct ble_encryption_block *ecb) tm_tick(); #endif } - +#endif return rc; } @@ -314,7 +371,27 @@ ble_rng_isr(void) uint8_t rnum; os_trace_isr_enter(); +#ifdef NRF54L_SERIES + (void)rnum; + /* No callback? Clear and disable interrupts */ + if (g_ble_rng_isr_cb == NULL) { + nrf_cracen_int_disable(NRF_CRACEN, NRF_CRACEN_INT_RNG_MASK); + NRF_CRACENCORE->RNGCONTROL.CONTROL &= ~CRACENCORE_RNGCONTROL_CONTROL_INTENFULL_Msk; + NRF_CRACEN->EVENTS_RNG = 0; + os_trace_isr_exit(); + return; + } + + if (g_ble_rng_isr_cb) { + /* If there is a value ready in the queue grab it */ + while ((NRF_CRACENCORE->RNGCONTROL.CONTROL & + CRACENCORE_RNGCONTROL_CONTROL_INTENFULL_Msk) && + (NRF_CRACENCORE->RNGCONTROL.FIFOLEVEL > 0)) { + g_ble_rng_isr_cb(ble_hw_rng_read()); + } + } +#else /* No callback? Clear and disable interrupts */ if (g_ble_rng_isr_cb == NULL) { nrf_rng_int_disable(NRF_RNG, NRF_RNG_INT_VALRDY_MASK); @@ -330,7 +407,7 @@ ble_rng_isr(void) rnum = (uint8_t)NRF_RNG->VALUE; (*g_ble_rng_isr_cb)(rnum); } - +#endif os_trace_isr_exit(); } @@ -345,6 +422,24 @@ ble_rng_isr(void) int ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias) { +#ifdef NRF54L_SERIES + NRF_CRACEN->ENABLE = CRACEN_ENABLE_CRYPTOMASTER_Msk | + CRACEN_ENABLE_RNG_Msk | + CRACEN_ENABLE_PKEIKG_Msk; + + while (NRF_CRACENCORE->PK.STATUS & CRACENCORE_PK_STATUS_PKBUSY_Msk); + NRF_CRACENCORE->PK.CONTROL &= ~CRACENCORE_IKG_PKECONTROL_CLEARIRQ_Msk; + + NRF_CRACENCORE->RNGCONTROL.CONTROL = CRACENCORE_RNGCONTROL_CONTROL_ResetValue | + CRACENCORE_RNGCONTROL_CONTROL_ENABLE_Msk; + + /* If we were passed a function pointer we need to enable the interrupt */ + if (cb != NULL) { + NVIC_SetVector(CRACEN_IRQn, (uint32_t)ble_rng_isr); + NVIC_EnableIRQ(CRACEN_IRQn); + g_ble_rng_isr_cb = cb; + } +#else /* Set bias */ if (bias) { NRF_RNG->CONFIG = 1; @@ -365,6 +460,7 @@ ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias) NVIC_EnableIRQ(RNG_IRQn); g_ble_rng_isr_cb = cb; } +#endif return 0; } @@ -381,12 +477,23 @@ ble_hw_rng_start(void) /* No need for interrupt if there is no callback */ OS_ENTER_CRITICAL(sr); +#ifdef NRF54L_SERIES + NRF_CRACEN->EVENTS_RNG = 0; + + if (g_ble_rng_isr_cb) { + nrf_cracen_int_enable(NRF_CRACEN, NRF_CRACEN_INT_RNG_MASK); + NRF_CRACENCORE->RNGCONTROL.CONTROL |= CRACENCORE_RNGCONTROL_CONTROL_INTENFULL_Msk; + /* Force regeneration of the samples */ + NRF_CRACENCORE->RNGCONTROL.FIFOLEVEL = 0; + } +#else NRF_RNG->EVENTS_VALRDY = 0; if (g_ble_rng_isr_cb) { nrf_rng_int_enable(NRF_RNG, NRF_RNG_INT_VALRDY_MASK); } nrf_rng_task_trigger(NRF_RNG, NRF_RNG_TASK_START); +#endif OS_EXIT_CRITICAL(sr); return 0; @@ -404,9 +511,15 @@ ble_hw_rng_stop(void) /* No need for interrupt if there is no callback */ OS_ENTER_CRITICAL(sr); +#ifdef NRF54L_SERIES + nrf_cracen_int_disable(NRF_CRACEN, NRF_CRACEN_INT_RNG_MASK); + NRF_CRACENCORE->RNGCONTROL.CONTROL &= ~CRACENCORE_RNGCONTROL_CONTROL_INTENFULL_Msk; + NRF_CRACEN->EVENTS_RNG = 0; +#else nrf_rng_int_disable(NRF_RNG, NRF_RNG_INT_VALRDY_MASK); nrf_rng_task_trigger(NRF_RNG, NRF_RNG_TASK_STOP); NRF_RNG->EVENTS_VALRDY = 0; +#endif OS_EXIT_CRITICAL(sr); return 0; @@ -421,14 +534,28 @@ uint8_t ble_hw_rng_read(void) { uint8_t rnum; +#ifdef NRF54L_SERIES + uint8_t slot_id; + /* Wait for a sample */ + while (NRF_CRACENCORE->RNGCONTROL.FIFOLEVEL == 0) { + assert((NRF_CRACENCORE->RNGCONTROL.STATUS & + CRACENCORE_RNGCONTROL_STATUS_STATE_Msk) != + (CRACENCORE_RNGCONTROL_STATUS_STATE_ERROR << + CRACENCORE_RNGCONTROL_STATUS_STATE_Pos)); + } + + NRF_CRACEN->EVENTS_RNG = 0; + slot_id = NRF_CRACENCORE->RNGCONTROL.FIFODEPTH - NRF_CRACENCORE->RNGCONTROL.FIFOLEVEL; + rnum = (uint8_t)NRF_CRACENCORE->RNGCONTROL.FIFO[slot_id]; +#else /* Wait for a sample */ while (NRF_RNG->EVENTS_VALRDY == 0) { } NRF_RNG->EVENTS_VALRDY = 0; rnum = (uint8_t)NRF_RNG->VALUE; - +#endif return rnum; } @@ -511,7 +638,11 @@ int ble_hw_resolv_list_match(void) { if (NRF_AAR->ENABLE && NRF_AAR->EVENTS_END && NRF_AAR->EVENTS_RESOLVED) { +#ifdef NRF54L_SERIES + return (int)NRF_AAR->ERRORSTATUS; +#else return (int)NRF_AAR->STATUS; +#endif } return -1; diff --git a/nimble/drivers/nrf5x/src/ble_phy.c b/nimble/drivers/nrf5x/src/ble_phy.c index 8be0771bd4..1c61be8700 100644 --- a/nimble/drivers/nrf5x/src/ble_phy.c +++ b/nimble/drivers/nrf5x/src/ble_phy.c @@ -25,7 +25,9 @@ #include #include #include +#ifndef NRF54L_SERIES #include +#endif #include "syscfg/syscfg.h" #include "os/os.h" /* Keep os_cputime explicitly to enable build on non-Mynewt platforms */ @@ -45,6 +47,10 @@ #ifdef NRF53_SERIES #include #endif +#ifdef NRF54L_SERIES +#include +#include +#endif #include "mcu/cmsis_nvic.h" #include "hal/hal_gpio.h" #else @@ -68,6 +74,13 @@ #endif #endif +#ifdef NRF54L_SERIES +#define EVENTS_ENDCRYPT EVENTS_END +#define MICSTATUS MACSTATUS +#define INTENSET INTENSET00 +#define HEADERMASK ADATAMASK +#endif + #if BABBLESIM extern void tm_tick(void); #endif @@ -103,8 +116,21 @@ extern uint8_t g_nrf_num_irks; extern uint32_t g_nrf_irk_list[]; /* To disable all radio interrupts */ +#ifdef NRF54L_SERIES +#define NRF_RADIO_IRQ_MASK_ALL (RADIO_INTENSET00_READY_Msk | \ + RADIO_INTENSET00_ADDRESS_Msk | \ + RADIO_INTENSET00_PAYLOAD_Msk | \ + RADIO_INTENSET00_END_Msk | \ + RADIO_INTENSET00_PHYEND_Msk | \ + RADIO_INTENSET00_DISABLED_Msk | \ + RADIO_INTENSET00_DEVMATCH_Msk | \ + RADIO_INTENSET00_DEVMISS_Msk | \ + RADIO_INTENSET00_BCMATCH_Msk | \ + RADIO_INTENSET00_CRCOK_Msk | \ + RADIO_INTENSET00_CRCERROR_Msk) +#else #define NRF_RADIO_IRQ_MASK_ALL (0x34FF) - +#endif /* * We configure the nrf with a 1 byte S0 field, 8 bit length field, and * zero bit S1 field. The preamble is 8 bits long. @@ -121,7 +147,7 @@ extern uint32_t g_nrf_irk_list[]; /* NRF_RADIO->PCNF0 configuration values */ #define NRF_PCNF0 (NRF_LFLEN_BITS << RADIO_PCNF0_LFLEN_Pos) | \ - (RADIO_PCNF0_S1INCL_Msk) | \ + (RADIO_PCNF0_S1INCL_Include << RADIO_PCNF0_S1INCL_Pos) | \ (NRF_S0LEN << RADIO_PCNF0_S0LEN_Pos) | \ (NRF_S1LEN_BITS << RADIO_PCNF0_S1LEN_Pos) #define NRF_PCNF0_1M (NRF_PCNF0) | \ @@ -223,6 +249,42 @@ static const uint8_t g_ble_phy_t_rxenddelay[BLE_PHY_NUM_MODE] = { [BLE_PHY_MODE_1M] = 9, [BLE_PHY_MODE_2M] = 5, }; +#elif defined(NRF54L_SERIES) +/* delay between EVENTS_READY and start of tx */ +static const uint8_t g_ble_phy_t_txdelay[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 2, /* ~1.6us */ + [BLE_PHY_MODE_2M] = 5, + [BLE_PHY_MODE_CODED_125KBPS] = 5, + [BLE_PHY_MODE_CODED_500KBPS] = 5 +}; +/* delay between EVENTS_ADDRESS and txd access address */ +static const uint8_t g_ble_phy_t_txaddrdelay[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 3, /* ~3.2us */ + [BLE_PHY_MODE_2M] = 5, + [BLE_PHY_MODE_CODED_125KBPS] = 17, + [BLE_PHY_MODE_CODED_500KBPS] = 17 +}; +/* delay between EVENTS_END and end of txd packet */ +static const uint8_t g_ble_phy_t_txenddelay[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 2, /* ~2.3us */ + [BLE_PHY_MODE_2M] = 4, + [BLE_PHY_MODE_CODED_125KBPS] = 9, + [BLE_PHY_MODE_CODED_500KBPS] = 3 +}; +/* delay between rxd access address (w/ TERM1 for coded) and EVENTS_ADDRESS */ +static const uint8_t g_ble_phy_t_rxaddrdelay[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 9, /* ~9.2us */ + [BLE_PHY_MODE_2M] = 2, + [BLE_PHY_MODE_CODED_125KBPS] = 17, + [BLE_PHY_MODE_CODED_500KBPS] = 17 +}; +/* delay between end of rxd packet and EVENTS_END */ +static const uint8_t g_ble_phy_t_rxenddelay[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 9, /* ~9.1us */ + [BLE_PHY_MODE_2M] = 1, + [BLE_PHY_MODE_CODED_125KBPS] = 27, + [BLE_PHY_MODE_CODED_500KBPS] = 22 +}; #else /* delay between EVENTS_READY and start of tx */ static const uint8_t g_ble_phy_t_txdelay[BLE_PHY_NUM_MODE] = { @@ -325,7 +387,19 @@ STATS_NAME_END(ble_phy_stats) */ #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) +#ifdef NRF54L_SERIES +struct nrf_ccm_job_list { + uint16_t in_alen; + uint16_t in_mlen; + uint8_t in_mlen_msb; + uint8_t out_mlen_msb; + uint16_t out_alen; + nrf_vdma_job_t in[6]; + nrf_vdma_job_t out[6]; +}; +struct nrf_ccm_job_list g_ccm_job_list; +#else /* * Per nordic, the number of bytes needed for scratch is 16 + MAX_PKT_SIZE. * However, when I used a smaller size it still overwrote the scratchpad. Until @@ -348,6 +422,7 @@ struct nrf_ccm_data struct nrf_ccm_data g_nrf_ccm_data; #endif +#endif static void timer0_reset(void) @@ -948,9 +1023,17 @@ ble_phy_get_ccm_datarate(void) return CCM_MODE_DATARATE_2Mbit << CCM_MODE_DATARATE_Pos; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY) case BLE_PHY_MODE_CODED_125KBPS: +#ifdef NRF54L_SERIES + return CCM_MODE_DATARATE_125Kbit << CCM_MODE_DATARATE_Pos; +#else return CCM_MODE_DATARATE_125Kbps << CCM_MODE_DATARATE_Pos; +#endif case BLE_PHY_MODE_CODED_500KBPS: +#ifdef NRF54L_SERIES + return CCM_MODE_DATARATE_500Kbit << CCM_MODE_DATARATE_Pos; +#else return CCM_MODE_DATARATE_500Kbps << CCM_MODE_DATARATE_Pos; +#endif #endif } @@ -975,6 +1058,74 @@ ble_phy_rx_xcvr_setup(void) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) if (g_ble_phy_data.phy_encrypted) { +#ifdef NRF54L_SERIES + /* AAD length */ + g_ccm_job_list.in_alen = 1; + g_ccm_job_list.in[0].p_buffer = (uint8_t *) &g_ccm_job_list.in_alen; + g_ccm_job_list.in[0].size = sizeof(g_ccm_job_list.in_alen); + g_ccm_job_list.in[0].attributes = 11; + + /* Encrypted message length LSB */ + g_ccm_job_list.in[1].p_buffer = ((uint8_t *)g_ble_phy_enc_buf) + 1; + g_ccm_job_list.in[1].size = 1; + g_ccm_job_list.in[1].attributes = 12; + + /* Encrypted message length MSB */ + g_ccm_job_list.in[2].p_buffer = &g_ccm_job_list.in_mlen_msb; + g_ccm_job_list.in[2].size = sizeof(g_ccm_job_list.in_mlen_msb); + g_ccm_job_list.in[2].attributes = 12; + + /* AAD */ + g_ccm_job_list.in[3].p_buffer = ((uint8_t *)g_ble_phy_enc_buf); + g_ccm_job_list.in[3].size = 1; + g_ccm_job_list.in[3].attributes = 13; + + /* Encrypted message */ + g_ccm_job_list.in[4].p_buffer = ((uint8_t *)g_ble_phy_enc_buf) + 3; + g_ccm_job_list.in[4].size = NRF_MAXLEN; + g_ccm_job_list.in[4].attributes = 14; + + /* Job list terminator */ + memset(&g_ccm_job_list.in[5], 0, sizeof(g_ccm_job_list.in[5])); + + /* AAD length */ + g_ccm_job_list.out[0].p_buffer = (uint8_t *) &g_ccm_job_list.out_alen; + g_ccm_job_list.out[0].size = sizeof(g_ccm_job_list.out_alen); + g_ccm_job_list.out[0].attributes = 11; + + /* Decrypted message length LSB */ + g_ccm_job_list.out[1].p_buffer = &dptr[1]; + g_ccm_job_list.out[1].size = 1; + g_ccm_job_list.out[1].attributes = 12; + + /* Decrypted message length MSB */ + g_ccm_job_list.out[2].p_buffer = &g_ccm_job_list.out_mlen_msb; + g_ccm_job_list.out[2].size = sizeof(g_ccm_job_list.out_mlen_msb); + g_ccm_job_list.out[2].attributes = 12; + + /* AAD */ + g_ccm_job_list.out[3].p_buffer = &dptr[0]; + g_ccm_job_list.out[3].size = 1; + g_ccm_job_list.out[3].attributes = 13; + + /* Decrypted message */ + g_ccm_job_list.out[4].p_buffer = &dptr[3]; + g_ccm_job_list.out[4].size = NRF_MAXLEN - 4; + g_ccm_job_list.out[4].attributes = 14; + + /* Job list terminator */ + memset(&g_ccm_job_list.out[5], 0, sizeof(g_ccm_job_list.out[5])); + + NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_enc_buf[0]; + NRF_CCM->IN.PTR = (uint32_t)g_ccm_job_list.in; + NRF_CCM->OUT.PTR = (uint32_t)g_ccm_job_list.out; + + NRF_CCM->MODE = (CCM_MODE_MACLEN_M4 << CCM_MODE_MACLEN_Pos) | CCM_MODE_MODE_Decryption | + ble_phy_get_ccm_datarate(); + + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->EVENTS_END = 0; +#else NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_enc_buf[0]; NRF_CCM->INPTR = (uint32_t)&g_ble_phy_enc_buf[0]; NRF_CCM->OUTPTR = (uint32_t)dptr; @@ -986,6 +1137,7 @@ ble_phy_rx_xcvr_setup(void) NRF_CCM->EVENTS_ERROR = 0; NRF_CCM->EVENTS_ENDCRYPT = 0; nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_KSGEN); +#endif phy_ppi_radio_address_to_ccm_crypt_enable(); } else { NRF_RADIO->PACKETPTR = (uint32_t)dptr; @@ -997,8 +1149,13 @@ ble_phy_rx_xcvr_setup(void) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) if (g_ble_phy_data.phy_privacy) { NRF_AAR->ENABLE = AAR_ENABLE_ENABLE_Enabled; +#ifdef NRF54L_SERIES + NRF_AAR->IN.PTR = (uint32_t)&g_nrf_irk_list[0]; + /* TODO(m): Find replacement for NRF_AAR->SCRATCHPTR */ +#else NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; NRF_AAR->SCRATCHPTR = (uint32_t)&g_ble_phy_data.phy_aar_scratch; +#endif NRF_AAR->EVENTS_END = 0; NRF_AAR->EVENTS_RESOLVED = 0; NRF_AAR->EVENTS_NOTRESOLVED = 0; @@ -1011,7 +1168,7 @@ ble_phy_rx_xcvr_setup(void) /* Turn off trigger TXEN on output compare match and AAR on bcmatch */ phy_ppi_timer0_compare0_to_radio_txen_disable(); - phy_ppi_radio_bcmatch_to_aar_start_disable(); + //phy_ppi_radio_bcmatch_to_aar_start_disable(); TODO(m): this was disabling CCM RADIO SUBSCRIBTION because peripherals share memory. think later what to do with it /* Reset the rx started flag. Used for the wait for response */ g_ble_phy_data.phy_rx_started = 0; @@ -1037,16 +1194,21 @@ ble_phy_rx_xcvr_setup(void) NRF_RADIO->EVENTS_ADDRESS = 0; NRF_RADIO->EVENTS_DEVMATCH = 0; NRF_RADIO->EVENTS_BCMATCH = 0; +#ifndef NRF54L_SERIES NRF_RADIO->EVENTS_RSSIEND = 0; +#endif NRF_RADIO->EVENTS_CRCOK = 0; - NRF_RADIO->SHORTS = RADIO_SHORTS_END_DISABLE_Msk | - RADIO_SHORTS_READY_START_Msk | + NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_ADDRESS_BCSTART_Msk | - RADIO_SHORTS_ADDRESS_RSSISTART_Msk | - RADIO_SHORTS_DISABLED_RSSISTOP_Msk; - + RADIO_SHORTS_ADDRESS_RSSISTART_Msk; +#ifdef NRF54L_SERIES + NRF_RADIO->SHORTS |= RADIO_SHORTS_PHYEND_DISABLE_Msk; +#else + NRF_RADIO->SHORTS |= RADIO_SHORTS_END_DISABLE_Msk | + RADIO_SHORTS_DISABLED_RSSISTOP_Msk; +#endif nrf_radio_int_enable(NRF_RADIO, RADIO_INTENSET_ADDRESS_Msk | - RADIO_INTENSET_DISABLED_Msk); + RADIO_INTENSET_DISABLED_Msk); } /** @@ -1187,7 +1349,9 @@ ble_phy_tx_end_isr(void) * it should be stopped here. */ nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP); +#ifndef NRF54L_SERIES NRF_TIMER0->TASKS_SHUTDOWN = 1; +#endif phy_ppi_wfr_disable(); phy_ppi_timer0_compare0_to_radio_txen_disable(); phy_ppi_rtc0_compare0_to_timer0_start_disable(); @@ -1240,7 +1404,9 @@ ble_phy_rx_end_isr(void) /* Set RSSI and CRC status flag in header */ ble_hdr = &g_ble_phy_data.rxhdr; +#ifndef NRF54L_SERIES assert(NRF_RADIO->EVENTS_RSSIEND != 0); +#endif ble_hdr->rxinfo.rssi = (-1 * NRF_RADIO->RSSISAMPLE); dptr = (uint8_t *)&g_ble_phy_rx_buf[0]; @@ -1453,7 +1619,11 @@ ble_phy_rx_start_isr(void) * octets for extended header. */ adva_offset = (dptr[3] & 0x0f) == 0x07 ? 2 : 0; +#ifdef NRF54L_SERIES + NRF_AAR->IN.PTR = (uint32_t)(dptr + 3 + adva_offset); +#else NRF_AAR->ADDRPTR = (uint32_t)(dptr + 3 + adva_offset); +#endif /* Trigger AAR after last bit of AdvA is received */ NRF_RADIO->EVENTS_BCMATCH = 0; @@ -1597,9 +1767,11 @@ ble_phy_init(void) g_ble_phy_data.tifs = BLE_LL_IFS; #endif +#ifndef NRF54L_SERIES /* Toggle peripheral power to reset (just in case) */ nrf_radio_power_set(NRF_RADIO, false); nrf_radio_power_set(NRF_RADIO, true); +#endif #ifdef NRF53_SERIES /* Errata 158: load trim values after toggling power */ @@ -1636,8 +1808,13 @@ ble_phy_init(void) RADIO_PCNF1_WHITEEN_Msk; /* Enable radio fast ramp-up */ +#ifdef NRF54L_SERIES + NRF_RADIO->TIMING = (RADIO_TIMING_RU_Fast << RADIO_TIMING_RU_Pos) & + RADIO_TIMING_RU_Msk; +#else NRF_RADIO->MODECNF0 |= (RADIO_MODECNF0_RU_Fast << RADIO_MODECNF0_RU_Pos) & RADIO_MODECNF0_RU_Msk; +#endif /* Set logical address 1 for TX and RX */ NRF_RADIO->TXADDRESS = 0; @@ -1654,9 +1831,11 @@ ble_phy_init(void) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) nrf_ccm_int_disable(NRF_CCM, 0xffffffff); - NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; NRF_CCM->EVENTS_ERROR = 0; +#ifndef NRF54L_SERIES + NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; memset(g_nrf_encrypt_scratchpad, 0, sizeof(g_nrf_encrypt_scratchpad)); +#endif #if PHY_USE_HEADERMASK_WORKAROUND NVIC_SetVector(CCM_AAR_IRQn, (uint32_t)ble_phy_ccm_isr); @@ -1667,20 +1846,32 @@ ble_phy_init(void) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) g_ble_phy_data.phy_aar_scratch = 0; +#ifdef NRF54L_SERIES + NRF_AAR->IN.PTR = (uint32_t)&g_nrf_irk_list[0]; +#else NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; +#endif nrf_aar_int_disable(NRF_AAR, 0xffffffff); NRF_AAR->EVENTS_END = 0; NRF_AAR->EVENTS_RESOLVED = 0; NRF_AAR->EVENTS_NOTRESOLVED = 0; +#ifdef NRF54L_SERIES + NRF_AAR->MAXRESOLVED = 0; +#else NRF_AAR->NIRK = 0; +#endif #endif /* TIMER0 setup for PHY when using RTC */ nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP); +#ifndef NRF54L_SERIES NRF_TIMER0->TASKS_SHUTDOWN = 1; + NRF_TIMER0->PRESCALER = 4; /* gives us 1 MHz */ +#else + NRF_TIMER0->PRESCALER = 5; /* gives us 1 MHz */ +#endif NRF_TIMER0->BITMODE = 3; /* 32-bit timer */ NRF_TIMER0->MODE = 0; /* Timer mode */ - NRF_TIMER0->PRESCALER = 4; /* gives us 1 MHz */ phy_ppi_init(); @@ -1741,7 +1932,7 @@ ble_phy_rx(void) */ nrf_wait_disabled(); if ((NRF_RADIO->STATE != RADIO_STATE_STATE_Disabled) && - ((NRF_RADIO->STATE & 0x07) != RADIO_STATE_STATE_RxIdle)) { + ((NRF_RADIO->STATE & 0x07) != RADIO_STATE_STATE_RxIdle)) { ble_phy_disable(); STATS_INC(ble_phy_stats, radio_state_errs); return BLE_PHY_ERR_RADIO_STATE; @@ -1764,11 +1955,19 @@ ble_phy_rx(void) void ble_phy_encrypt_enable(const uint8_t *key) { - memcpy(g_nrf_ccm_data.key, key, 16); - g_ble_phy_data.phy_encrypted = 1; NRF_AAR->ENABLE = AAR_ENABLE_ENABLE_Disabled; NRF_CCM->ENABLE = CCM_ENABLE_ENABLE_Enabled; -#ifdef NRF5340_XXAA + g_ble_phy_data.phy_encrypted = 1; + +#ifdef NRF54L_SERIES + NRF_CCM->KEY.VALUE[0] = get_be32(&key[12]); + NRF_CCM->KEY.VALUE[1] = get_be32(&key[8]); + NRF_CCM->KEY.VALUE[2] = get_be32(&key[4]); + NRF_CCM->KEY.VALUE[3] = get_be32(&key[0]); +#else + memcpy(g_nrf_ccm_data.key, key, 16); +#endif +#if defined(NRF5340_XXAA) || defined(NRF54L_SERIES) NRF_CCM->HEADERMASK = BLE_LL_PDU_HEADERMASK_DATA; #endif #if PHY_USE_HEADERMASK_WORKAROUND @@ -1779,7 +1978,7 @@ ble_phy_encrypt_enable(const uint8_t *key) void ble_phy_encrypt_header_mask_set(uint8_t mask) { -#ifdef NRF5340_XXAA +#if defined(NRF5340_XXAA) || defined(NRF54L_SERIES) NRF_CCM->HEADERMASK = mask; #endif #if PHY_USE_HEADERMASK_WORKAROUND @@ -1790,14 +1989,24 @@ ble_phy_encrypt_header_mask_set(uint8_t mask) void ble_phy_encrypt_iv_set(const uint8_t *iv) { +#ifdef NRF54L_SERIES + NRF_CCM->NONCE.VALUE[1] = get_be32(iv); + NRF_CCM->NONCE.VALUE[0] = get_be32(iv + 4); +#else memcpy(g_nrf_ccm_data.iv, iv, 8); +#endif } void ble_phy_encrypt_counter_set(uint64_t counter, uint8_t dir_bit) { +#ifdef NRF54L_SERIES + NRF_CCM->NONCE.VALUE[3] = ((uint8_t *)&counter)[0]; + NRF_CCM->NONCE.VALUE[2] = get_be32(&((uint8_t *)&counter)[1]) | ((!!dir_bit) << 7); +#else g_nrf_ccm_data.pkt_counter = counter; g_nrf_ccm_data.dir_bit = dir_bit; +#endif } void @@ -1958,6 +2167,12 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) if (g_ble_phy_data.phy_encrypted) { dptr = (uint8_t *)&g_ble_phy_enc_buf[0]; pktptr = (uint8_t *)&g_ble_phy_tx_buf[0]; +#ifdef NRF54L_SERIES + NRF_CCM->IN.PTR = (uint32_t)&g_ccm_job_list.in; + NRF_CCM->OUT.PTR = (uint32_t)&g_ccm_job_list.out; + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->MODE = CCM_MODE_MODE_Encryption | (CCM_MODE_MACLEN_M4 << CCM_MODE_MACLEN_Pos) | ble_phy_get_ccm_datarate(); +#else NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; NRF_CCM->INPTR = (uint32_t)dptr; NRF_CCM->OUTPTR = (uint32_t)pktptr; @@ -1965,9 +2180,14 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) NRF_CCM->EVENTS_ERROR = 0; NRF_CCM->MODE = CCM_MODE_LENGTH_Msk | ble_phy_get_ccm_datarate(); NRF_CCM->CNFPTR = (uint32_t)&g_nrf_ccm_data; +#endif } else { #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) +#ifdef NRF54L_SERIES + NRF_AAR->IN.PTR = (uint32_t)&g_nrf_irk_list[0]; +#else NRF_AAR->IRKPTR = (uint32_t)&g_nrf_irk_list[0]; +#endif #endif dptr = (uint8_t *)&g_ble_phy_tx_buf[0]; pktptr = dptr; @@ -1997,7 +2217,65 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) NRF_CCM->INTENSET = CCM_INTENSET_ENDKSGEN_Msk; } #endif +#ifdef NRF54L_SERIES + /* AAD length */ + g_ccm_job_list.in_alen = 1; + g_ccm_job_list.in[0].p_buffer = (uint8_t *) &g_ccm_job_list.in_alen; + g_ccm_job_list.in[0].size = sizeof(g_ccm_job_list.in_alen); + g_ccm_job_list.in[0].attributes = 11; + + /* Unencrypted message length */ + g_ccm_job_list.in_mlen = payload_len; + g_ccm_job_list.in[1].p_buffer = (uint8_t *) &g_ccm_job_list.in_mlen; + g_ccm_job_list.in[1].size = sizeof(g_ccm_job_list.in_mlen); + g_ccm_job_list.in[1].attributes = 12; + + /* AAD */ + g_ccm_job_list.in[2].p_buffer = (uint8_t *) &dptr[0]; + g_ccm_job_list.in[2].size = 1; + g_ccm_job_list.in[2].attributes = 13; + + /* Unencrypted message */ + g_ccm_job_list.in[3].p_buffer = (uint8_t *) &dptr[3]; + g_ccm_job_list.in[3].size = payload_len; + g_ccm_job_list.in[3].attributes = 14; + + /* Job list terminator */ + memset(&g_ccm_job_list.in[4], 0, sizeof(g_ccm_job_list.in[4])); + + /* AAD length */ + g_ccm_job_list.out[0].p_buffer = (uint8_t *) &g_ccm_job_list.out_alen; + g_ccm_job_list.out[0].size = sizeof(g_ccm_job_list.out_alen); + g_ccm_job_list.out[0].attributes = 11; + + /* Encrypted message length LSB */ + g_ccm_job_list.out[1].p_buffer = (uint8_t *) &pktptr[1]; + g_ccm_job_list.out[1].size = 1; + g_ccm_job_list.out[1].attributes = 12; + + /* Encrypted message length MSB */ + g_ccm_job_list.out[2].p_buffer = (uint8_t *) &g_ccm_job_list.out_mlen_msb; + g_ccm_job_list.out[2].size = sizeof(g_ccm_job_list.out_mlen_msb); + g_ccm_job_list.out[2].attributes = 12; + + /* AAD */ + g_ccm_job_list.out[3].p_buffer = (uint8_t *) &pktptr[0]; + g_ccm_job_list.out[3].size = 1; + g_ccm_job_list.out[3].attributes = 13; + + /* Encrypted message */ + g_ccm_job_list.out[4].p_buffer = (uint8_t *) &pktptr[3]; + g_ccm_job_list.out[4].size = payload_len + 4; + g_ccm_job_list.out[4].attributes = 14; + + /* Job list terminator */ + memset(&g_ccm_job_list.out[5], 0, sizeof(g_ccm_job_list.out[5])); + + /* Start encryption */ + nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_START); +#else nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_KSGEN); +#endif } #endif @@ -2005,11 +2283,17 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) /* Clear the ready, end and disabled events */ NRF_RADIO->EVENTS_READY = 0; + NRF_RADIO->EVENTS_ADDRESS = 0; NRF_RADIO->EVENTS_END = 0; NRF_RADIO->EVENTS_DISABLED = 0; /* Enable shortcuts for transmit start/end. */ - shortcuts = RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_READY_START_Msk; + shortcuts = RADIO_SHORTS_READY_START_Msk; +#ifdef NRF54L_SERIES + shortcuts |= RADIO_SHORTS_PHYEND_DISABLE_Msk; +#else + shortcuts |= RADIO_SHORTS_END_DISABLE_Msk; +#endif NRF_RADIO->SHORTS = shortcuts; nrf_radio_int_enable(NRF_RADIO, RADIO_INTENSET_DISABLED_Msk); @@ -2157,7 +2441,12 @@ ble_phy_setchan(uint8_t chan, uint32_t access_addr, uint32_t crcinit) /* Set the frequency and the data whitening initial value */ g_ble_phy_data.phy_chan = chan; NRF_RADIO->FREQUENCY = g_ble_phy_chan_freq[chan]; + +#ifdef NRF54L_SERIES + NRF_RADIO->DATAWHITE = RADIO_DATAWHITE_ResetValue | chan; +#else NRF_RADIO->DATAWHITEIV = chan; +#endif return 0; } @@ -2175,7 +2464,9 @@ static void ble_phy_stop_usec_timer(void) { nrf_timer_task_trigger(NRF_TIMER0, NRF_TIMER_TASK_STOP); +#ifndef NRF54L_SERIES NRF_TIMER0->TASKS_SHUTDOWN = 1; +#endif nrf_rtc_event_disable(NRF_RTC0, RTC_EVTENSET_COMPARE0_Msk); } @@ -2295,7 +2586,12 @@ ble_phy_max_data_pdu_pyld(void) void ble_phy_resolv_list_enable(void) { +#ifdef NRF54L_SERIES + /* TODO: Is this the right replacement? */ + NRF_AAR->MAXRESOLVED = (uint32_t)g_nrf_num_irks; +#else NRF_AAR->NIRK = (uint32_t)g_nrf_num_irks; +#endif g_ble_phy_data.phy_privacy = 1; } @@ -2348,6 +2644,9 @@ ble_phy_rfclk_enable(void) #ifdef NRF53_SERIES nrf5340_net_clock_hfxo_request(); #endif +#ifdef NRF54L_SERIES + nrf54l_clock_hfxo_request(); +#endif #else nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTART); #endif @@ -2363,6 +2662,9 @@ ble_phy_rfclk_disable(void) #ifdef NRF53_SERIES nrf5340_net_clock_hfxo_release(); #endif +#ifdef NRF54L_SERIES + nrf54l_clock_hfxo_release(); +#endif #else nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP); #endif diff --git a/nimble/drivers/nrf5x/src/ble_phy_cs.c b/nimble/drivers/nrf5x/src/ble_phy_cs.c new file mode 100644 index 0000000000..5b41397bb8 --- /dev/null +++ b/nimble/drivers/nrf5x/src/ble_phy_cs.c @@ -0,0 +1,839 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include "syscfg/syscfg.h" +#include "os/os.h" +#include "os/os_cputime.h" +#include "nimble/ble.h" +#include "controller/ble_phy.h" +#include "controller/ble_ll.h" +#include "nrfx.h" +#include "phy_priv.h" +#include +#include +#include + +#if MYNEWT_VAL(BLE_PHY_CHANNEL_SOUNDING) + +/* To disable all radio interrupts */ +#define NRF_RADIO_IRQ_MASK_ALL (RADIO_INTENSET00_READY_Msk | \ + RADIO_INTENSET00_ADDRESS_Msk | \ + RADIO_INTENSET00_PAYLOAD_Msk | \ + RADIO_INTENSET00_END_Msk | \ + RADIO_INTENSET00_PHYEND_Msk | \ + RADIO_INTENSET00_DISABLED_Msk | \ + RADIO_INTENSET00_DEVMATCH_Msk | \ + RADIO_INTENSET00_DEVMISS_Msk | \ + RADIO_INTENSET00_BCMATCH_Msk | \ + RADIO_INTENSET00_CRCOK_Msk | \ + RADIO_INTENSET00_CRCERROR_Msk) + +/* BLE PHY defaults */ +#define NRF_LFLEN_BITS (8) +#define NRF_S0LEN (1) +#define NRF_S1LEN_BITS (0) +#define NRF_MAXLEN (255) +#define NRF_BALEN (3) /* For base address of 3 bytes */ +#define NRF_PCNF0 (NRF_LFLEN_BITS << RADIO_PCNF0_LFLEN_Pos) | \ + (RADIO_PCNF0_S1INCL_Include << RADIO_PCNF0_S1INCL_Pos) | \ + (NRF_S0LEN << RADIO_PCNF0_S0LEN_Pos) | \ + (NRF_S1LEN_BITS << RADIO_PCNF0_S1LEN_Pos) + +/* Various radio timings */ +/* Radio ramp-up times in usecs (fast mode) */ +#define BLE_PHY_T_TXENFAST (40) +#define BLE_PHY_T_RXENFAST (40) + +#define CSTONE_EXCLUSION_PERIOD_US (10) // XXXX change to 1us + +#define TONE_RESULT_COUNT_MAX (5) + +#define BLE_PHY_CS_ROLE_INITIATOR (0) +#define BLE_PHY_CS_ROLE_REFLECTOR (1) + +static uint32_t g_ble_phy_tx_buf[(BLE_PHY_MAX_PDU_LEN + 3) / 4]; +static uint32_t g_ble_phy_rx_buf[(BLE_PHY_MAX_PDU_LEN + 3) / 4]; + +struct ble_phy_cs_obj +{ + uint32_t prev_radio_isr_handler; + uint32_t step_anchor_ticks; + uint8_t phy_state; + uint8_t phy_mode; + uint8_t role; + uint8_t report_sync_results; + uint8_t report_tone_results; + struct ble_phy_cs_transmission *prev_transm; + struct ble_phy_cs_sync_results sync_results; + struct ble_phy_cs_tone_results tone_results[TONE_RESULT_COUNT_MAX]; + struct ble_phy_cs_subevent_results subevent_results; +}; + +static struct ble_phy_cs_obj g_ble_phy_cs; + +/* delay between EVENTS_READY and start of tx */ +const uint16_t txdelay[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 1, /* ~1.6us */ + [BLE_PHY_MODE_2M] = 5, +}; +/* delay between EVENTS_ADDRESS and txd access address */ +const uint16_t txaddrdelay_ns[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 3200, /* ~3.2us */ + [BLE_PHY_MODE_2M] = 5000, +}; +/* delay between rxd access address (w/ TERM1 for coded) and EVENTS_ADDRESS */ +const uint16_t rxaddrdelay_ns[BLE_PHY_NUM_MODE] = { + [BLE_PHY_MODE_1M] = 9200, /* ~9.2us */ + [BLE_PHY_MODE_2M] = 2000, +}; + +#if MYNEWT_VAL(BLE_PHY_CS_USE_PCT_CORRECTION) + +#define LUT_SIZE 256 +#define FULL_LUT_SIZE (4 * LUT_SIZE) + +/* LUT used for correction of the circuit delays. Must be calibrated per device. + * The values (-32768, 32767) corresponds to <-2π, 2π). + */ +static int16_t pct_angle_correction_lut[80] = MYNEWT_VAL(BLE_PHY_CS_PCT_CORRECTION_PER_CHANNEL); + +/* sin approximation for <0, π/2> angles */ +static const int16_t sin0_pi2_table[LUT_SIZE] = { + 0, 202, 404, 605, 807, 1009, 1211, 1412, + 1614, 1816, 2017, 2219, 2420, 2621, 2822, 3023, + 3224, 3425, 3626, 3826, 4027, 4227, 4427, 4627, + 4827, 5026, 5226, 5425, 5624, 5822, 6021, 6219, + 6417, 6615, 6813, 7010, 7207, 7404, 7600, 7796, + 7992, 8188, 8383, 8578, 8773, 8967, 9161, 9355, + 9548, 9741, 9933, 10126, 10317, 10509, 10700, 10890, + 11080, 11270, 11459, 11648, 11837, 12025, 12212, 12399, + 12586, 12772, 12958, 13143, 13328, 13512, 13695, 13878, + 14061, 14243, 14425, 14606, 14786, 14966, 15145, 15324, + 15502, 15679, 15856, 16033, 16208, 16383, 16558, 16732, + 16905, 17078, 17250, 17421, 17592, 17761, 17931, 18099, + 18267, 18434, 18601, 18767, 18932, 19096, 19260, 19423, + 19585, 19747, 19907, 20067, 20226, 20385, 20542, 20699, + 20855, 21011, 21165, 21319, 21472, 21624, 21775, 21925, + 22075, 22224, 22372, 22519, 22665, 22810, 22955, 23098, + 23241, 23383, 23524, 23664, 23803, 23941, 24079, 24215, + 24351, 24485, 24619, 24752, 24883, 25014, 25144, 25273, + 25401, 25528, 25654, 25779, 25903, 26026, 26149, 26270, + 26390, 26509, 26627, 26744, 26860, 26976, 27090, 27203, + 27315, 27426, 27536, 27644, 27752, 27859, 27965, 28069, + 28173, 28276, 28377, 28477, 28577, 28675, 28772, 28868, + 28963, 29057, 29150, 29241, 29332, 29421, 29510, 29597, + 29683, 29768, 29851, 29934, 30016, 30096, 30175, 30253, + 30330, 30406, 30481, 30554, 30627, 30698, 30768, 30837, + 30904, 30971, 31036, 31100, 31163, 31225, 31286, 31345, + 31403, 31460, 31516, 31571, 31624, 31676, 31728, 31777, + 31826, 31873, 31920, 31965, 32008, 32051, 32092, 32132, + 32171, 32209, 32246, 32281, 32315, 32348, 32379, 32410, + 32439, 32467, 32493, 32519, 32543, 32566, 32587, 32608, + 32627, 32645, 32662, 32678, 32692, 32705, 32717, 32727, + 32737, 32745, 32751, 32757, 32761, 32765, 32766, 32767, +}; + +static inline int16_t +sin_rad(int32_t angle) +{ + uint32_t index; + int16_t sign = 1; + + angle %= FULL_LUT_SIZE; + + if (angle < 0) angle += FULL_LUT_SIZE; + + if (angle < LUT_SIZE) { + /* 0..π/2, already a good angle */ + } else if (angle < 2 * LUT_SIZE) { + /* π/2..π */ + angle = 2 * LUT_SIZE - angle; + } else if (angle < 3 * LUT_SIZE) { + /* π..3π/2 */ + angle -= 2 * LUT_SIZE; + sign = -1; + } else { + /* 3π/2..2π */ + angle = FULL_LUT_SIZE - angle; + sign = -1; + } + + index = ((uint32_t)angle * (LUT_SIZE - 1)) / LUT_SIZE; + + return sign * sin0_pi2_table[index]; +} + +static inline int16_t +cos_rad(int32_t angle) +{ + /* cos(x) = sin(x + π/2) */ + return sin_rad(angle + LUT_SIZE); +} + +static inline void +ble_phy_cs_pct_correct_ciruit_delay(int16_t *PCT_I16, int16_t *PCT_Q16, uint8_t channel) +{ + int16_t I16, Q16; + int32_t I_corr, Q_corr; + int32_t angle; + int16_t cos_phi, sin_phi; + + I16 = *PCT_I16; + Q16 = *PCT_Q16; + + /* Angle is Q1.31 */ + angle = pct_angle_correction_lut[channel]; + + /* angle_lut_idx = (angle/2^15) * FULL_LUT_SIZE + * Q1.31 * Q0.11 = Q1.42, so we have to >> 11 (out of 15) before + * multiplication to get max precision Q1.31. After dividing by + * remaining >> 4 we end up with Q1.17. + */ + angle = ((angle >> 11) * FULL_LUT_SIZE) >> 4; + + /* Because of the modulo 2e10 the result is Q1.10 */ + angle = angle % FULL_LUT_SIZE; + if (angle < 0) angle += FULL_LUT_SIZE; + + /* Read sin/cos from LUT */ + cos_phi = cos_rad(angle); + sin_phi = sin_rad(angle); + + /* PCT correction. I16, Q16, cos_phi and sin_phi are formatted in Q1.15. + * The multiplication results in Q1.30 format, so it should be scaled + * back in the last step. + */ + I_corr = (int32_t)I16 * cos_phi + (int32_t)Q16 * sin_phi; + Q_corr = -(int32_t)I16 * sin_phi + (int32_t)Q16 * cos_phi; + + *PCT_I16 = (int16_t)(I_corr >> 15); + *PCT_Q16 = (int16_t)(Q_corr >> 15); +} +#endif /* BLE_PHY_CS_USE_PCT_CORRECTION */ + +static inline void +ble_phy_cs_rtt_set(uint8_t *rtt_sequence, uint8_t rtt_sequence_len) +{ + /* TODO: Filling only RTT.SEGMENTxx does not work, waiting for the documentation to be updated */ + memcpy(&((uint8_t *)&g_ble_phy_tx_buf)[1], rtt_sequence, rtt_sequence_len); + memcpy(&((uint8_t *)&g_ble_phy_rx_buf)[1], rtt_sequence, rtt_sequence_len); + + NRF_RADIO->RTT.SEGMENT01 = get_le32(&rtt_sequence[0]); + NRF_RADIO->RTT.SEGMENT23 = get_le32(&rtt_sequence[4]); + NRF_RADIO->RTT.SEGMENT45 = get_le32(&rtt_sequence[8]); + NRF_RADIO->RTT.SEGMENT67 = get_le32(&rtt_sequence[12]); + NRF_RADIO->RTT.CONFIG = + ((rtt_sequence_len >> 1) << RADIO_RTT_CONFIG_NUMSEGMENTS_Pos) | + (RADIO_RTT_CONFIG_EN_Enabled << RADIO_RTT_CONFIG_EN_Pos) | + ((g_ble_phy_cs.role == BLE_PHY_CS_ROLE_INITIATOR) ? + RADIO_RTT_CONFIG_ROLE_Initiator << RADIO_RTT_CONFIG_ROLE_Pos : + RADIO_RTT_CONFIG_ROLE_Reflector << RADIO_RTT_CONFIG_ROLE_Pos); + + NRF_RADIO->PCNF1 &= ~(RADIO_PCNF1_MAXLEN_Msk | RADIO_PCNF1_STATLEN_Msk); + NRF_RADIO->PCNF1 |= (rtt_sequence_len << RADIO_PCNF1_MAXLEN_Pos) | + (rtt_sequence_len << RADIO_PCNF1_STATLEN_Pos); +} + +static inline int16_t +ble_phy_cs_ffoest_get(void) +{ + return ((int16_t)(NRF_RADIO->CSTONES.FFOEST << 4)) >> 4; +} + +static int +ble_phy_cs_radio_timer_start(uint32_t cputime, uint8_t rem_us, bool tx) +{ + uint32_t next_cc; + uint32_t cur_cc; + uint32_t cntr; + uint32_t delta; + uint32_t anchor; + int radio_rem; + int rem_us_corr; + + /* Calculate rem_us for radio and FEM enable. The result may be a negative + * value, but we'll adjust later. + */ + anchor = rem_us; + if (tx) { + radio_rem = anchor - BLE_PHY_T_TXENFAST - txdelay[g_ble_phy_cs.phy_mode]; + } else { + radio_rem = anchor - BLE_PHY_T_RXENFAST; + } + + /* We need to adjust rem_us values, so they are >=1 for TIMER10 compare + * event to be triggered. + */ + if (radio_rem <= -30) { + /* rem_us is -60..-30 */ + cputime -= 2; + rem_us_corr = 61; + } else { + /* rem_us is -29..0 */ + cputime -= 1; + rem_us_corr = 30; + } + + /* + * 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_timer_task_trigger(NRF_TIMER10, NRF_TIMER_TASK_CLEAR); + g_ble_phy_cs.step_anchor_ticks = anchor + rem_us_corr; + + /* Set RTC compare to start TIMER0 */ + NRF_RTC0->EVENTS_COMPARE[0] = 0; + nrf_rtc_cc_set(NRF_RTC0, 0, next_cc); + nrf_rtc_event_enable(NRF_RTC0, RTC_EVTENSET_COMPARE0_Msk); + + phy_ppi_rtc0_compare0_to_timer0_start_enable(); + + return 0; +} + +static inline int +ble_phy_cs_channel_set(uint8_t chan) +{ + assert(!(chan <= 1 || (23 <= chan && chan <= 25) || 77 <= chan)); + + /* Check for valid channel range */ + if (chan <= 1 || (23 <= chan && chan <= 25) || 77 <= chan) { + return BLE_PHY_ERR_INV_PARAM; + } + + if (NRF_RADIO->FREQUENCY != 2 + chan) { + NRF_RADIO->FREQUENCY = 2 + chan; + } + + return 0; +} + +static inline void +ble_phy_cs_tune_set(void) +{ + NRF_RADIO->CSTONES.FFOIN = RADIO_CSTONES_FFOIN_ResetValue; + NRF_RADIO->CSTONES.FFOSOURCE = RADIO_CSTONES_FFOSOURCE_ResetValue; + NRF_RADIO->CSTONES.PHASESHIFT = 0; + RADIO_FREQFINETUNE = MYNEWT_VAL(BLE_PHY_CS_FREQFINETUNE) & 0x1FFFUL; +} + +static inline void +ble_phy_cs_tone_configure(uint32_t duration_usecs, uint8_t tone_mode) +{ + uint8_t numsamples; + + assert(duration_usecs <= 80); + numsamples = (duration_usecs - 2 * CSTONE_EXCLUSION_PERIOD_US) * 160 / 80; + NRF_RADIO->CSTONES.NUMSAMPLES = numsamples; + NRF_RADIO->CSTONES.NUMSAMPLESCOEFF = (1 << 20) / numsamples; + + if (tone_mode == BLE_PHY_CS_TONE_MODE_PM) { + NRF_RADIO->CSTONES.DOWNSAMPLE = 1; + NRF_RADIO->CSTONES.MODE = RADIO_CSTONES_MODE_TPM_Msk | RADIO_CSTONES_MODE_TFM_Msk; + } else { + NRF_RADIO->CSTONES.MODE = RADIO_CSTONES_MODE_TFM_Msk; + } +} + +static void +ble_phy_cs_disable(void) +{ +#ifdef NRF54L_SERIES + nrf_timer_task_trigger(NRF_TIMER10, NRF_TIMER_TASK_STOP); +#endif + phy_ppi_timer0_compare3_to_radio_disable_disable(); + phy_ppi_timer0_compare2_to_radio_start_disable(); + phy_ppi_timer0_compare4_to_radio_cstonesstart_disable(); + phy_ppi_cs_mode_disable(); + + nrf_timer_task_trigger(NRF_TIMER10, NRF_TIMER_TASK_STOP); + phy_ppi_timer0_compare0_to_radio_txen_disable(); + phy_ppi_timer0_compare1_to_radio_rxen_disable(); + phy_ppi_rtc0_compare0_to_timer0_start_disable(); + phy_ppi_timer00_radio_address_to_capture_disable(); + ble_phy_disable(); + + NRF_RADIO->CSTONES.MODE = 0; + NRF_RADIO->RTT.CONFIG = 0; + + /* Configure back the registers */ + NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_1Mbit; + NRF_RADIO->CRCCNF = (RADIO_CRCCNF_SKIPADDR_Skip << RADIO_CRCCNF_SKIPADDR_Pos) | RADIO_CRCCNF_LEN_Three; + NRF_RADIO->PCNF0 = NRF_PCNF0; + NRF_RADIO->PCNF1 = NRF_MAXLEN | + (RADIO_PCNF1_ENDIAN_Little << RADIO_PCNF1_ENDIAN_Pos) | + (NRF_BALEN << RADIO_PCNF1_BALEN_Pos) | + RADIO_PCNF1_WHITEEN_Msk; + NRF_RADIO->RXADDRESSES = (1 << 0); + + if (g_ble_phy_cs.prev_radio_isr_handler != 0) { + NVIC_SetVector(RADIO_IRQn, (uint32_t)g_ble_phy_cs.prev_radio_isr_handler); + } +} + +static void +ble_phy_cs_subevent_end(int status) +{ + ble_phy_cs_disable(); + g_ble_phy_cs.subevent_results.status = status; + ble_ll_cs_subevent_end(&g_ble_phy_cs.subevent_results); +} + +static inline uint32_t +ble_phy_cs_calc_tone_chain_offset(struct ble_phy_cs_transmission *first_tone) +{ + struct ble_phy_cs_transmission *t; + uint32_t offset_us = 0; + + t = first_tone; + while (t && t->mode == BLE_PHY_CS_TRANSM_MODE_TONE) { + offset_us += t->duration_usecs; + offset_us += t->end_tifs; + t = t->next; + } + + return offset_us; +} + +static inline void +ble_phy_cs_radio_events_clear(void) +{ + NRF_RADIO->EVENTS_PHYEND = 0; + NRF_RADIO->EVENTS_READY = 0; + NRF_RADIO->EVENTS_TXREADY = 0; + NRF_RADIO->EVENTS_RXREADY = 0; + NRF_RADIO->EVENTS_ADDRESS = 0; + NRF_RADIO->EVENTS_END = 0; + NRF_RADIO->EVENTS_DISABLED = 0; + RADIO_EVENTS_CSTONESEND = 0; + NRF_TIMER10->EVENTS_COMPARE[3] = 0; +} + +static int +ble_phy_cs_schedule_step(uint32_t anchor_us, struct ble_phy_cs_transmission *next_transm, + uint8_t role) +{ + struct ble_phy_cs_transmission *t = next_transm; + struct ble_phy_cs_transmission *next; + uint32_t start_us; + uint32_t offset_us = 0; + uint32_t end_us = anchor_us; + uint32_t total_duration; + uint8_t current_phase = 0xFF; + + g_ble_phy_cs.report_sync_results = 0; + g_ble_phy_cs.report_tone_results = 0; + NRF_RADIO->SHORTS = 0; + NRF_RADIO->CSTONES.MODE = 0; + NRF_RADIO->RTT.CONFIG = 0; + NRF_RADIO->PCNF1 &= ~(RADIO_PCNF1_MAXLEN_Msk | RADIO_PCNF1_STATLEN_Msk); + nrf_radio_int_enable(NRF_RADIO, RADIO_INTENSET_DISABLED_Msk); + ble_phy_cs_channel_set(t->channel); + nrf_timer_cc_set(NRF_TIMER10, 0, 0xffffffff); + nrf_timer_cc_set(NRF_TIMER10, 1, 0xffffffff); + nrf_timer_cc_set(NRF_TIMER10, 2, 0xffffffff); + nrf_timer_cc_set(NRF_TIMER10, 3, 0xffffffff); + nrf_timer_cc_set(NRF_TIMER10, 4, 0xffffffff); + nrf_timer_cc_set(NRF_TIMER10, 5, 0xffffffff); + nrf_timer_cc_set(NRF_TIMER10, 6, 0xffffffff); + + if (t->is_tx) { + NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_tx_buf[0]; + } else { + NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_rx_buf[0]; + } + + while (t) { + start_us = anchor_us + offset_us; + + /* Detect if TX or RX phase */ + if (t->is_tx != current_phase) { + if (t->is_tx == 1) { + /* Schedule TXEN */ + NRF_TIMER10->EVENTS_COMPARE[0] = 0; + nrf_timer_cc_set(NRF_TIMER10, 0, start_us - BLE_PHY_T_TXENFAST - txdelay[g_ble_phy_cs.phy_mode]); + phy_ppi_timer0_compare0_to_radio_txen_enable(); + } else { + /* Schedule RXEN */ + NRF_TIMER10->EVENTS_COMPARE[1] = 0; + nrf_timer_cc_set(NRF_TIMER10, 1, start_us - BLE_PHY_T_RXENFAST); + phy_ppi_timer0_compare1_to_radio_rxen_enable(); + } + + current_phase = t->is_tx; + } + + /* Schedule CS_SYNC TX or RX */ + if (t->mode == BLE_PHY_CS_TRANSM_MODE_SYNC) { + phy_ppi_timer00_radio_address_to_capture_enable(); + nrf_radio_int_enable(NRF_RADIO, RADIO_INTENSET_ADDRESS_Msk); + g_ble_phy_cs.report_sync_results = 1; + if (t->is_tx) { + if (t->rtt_sequence) { + ble_phy_cs_rtt_set(t->rtt_sequence, t->rtt_sequence_len); + } + /* TX SYNC */ + NRF_RADIO->BASE0 = (t->aa << 8); + NRF_RADIO->PREFIX0 = (NRF_RADIO->PREFIX0 & 0xFFFFFF00) | (t->aa >> 24); + /* Set the CS trailer */ + if (t->aa & (1 << 31)) { + /* Filling RX buffer too, because if we missed a CS_SYNC RX, + * there will be no EVENTS_ADDRESS interrupt, + * therefore no time to update the NRF_RADIO->PACKETPTR. + */ + g_ble_phy_tx_buf[0] &= 0xFFFFFFF0; + g_ble_phy_tx_buf[0] |= 0b1010; + g_ble_phy_rx_buf[0] &= 0xFFFFFFF0; + g_ble_phy_rx_buf[0] |= 0b1010; + } else { + g_ble_phy_tx_buf[0] &= 0xFFFFFFF0; + g_ble_phy_tx_buf[0] |= 0b00000101; + g_ble_phy_rx_buf[0] &= 0xFFFFFFF0; + g_ble_phy_rx_buf[0] |= 0b00000101; + } + /* Schedule RADIO START for TX */ + nrf_timer_cc_set(NRF_TIMER10, 2, start_us); + phy_ppi_timer0_compare2_to_radio_start_enable(); + } else { + /* RX SYNC */ + NRF_RADIO->BASE1 = (t->aa << 8); + NRF_RADIO->PREFIX0 = (NRF_RADIO->PREFIX0 & 0xFFFF00FF) | (((t->aa >> 24) & 0xFF) << 8); + /* Schedule RADIO START for RX */ + nrf_timer_cc_set(NRF_TIMER10, 6, start_us); + phy_ppi_timer0_compare2_to_radio_start_enable(); + } + + end_us = start_us + t->duration_usecs; + offset_us += t->duration_usecs + t->end_tifs; + g_ble_phy_cs.prev_transm = t; + t = t->next; + } else if (t->mode == BLE_PHY_CS_TRANSM_MODE_TONE) { + NRF_RADIO->RTT.CONFIG = (RADIO_RTT_CONFIG_EN_Enabled << RADIO_RTT_CONFIG_EN_Pos) | + ((g_ble_phy_cs.role == BLE_PHY_CS_ROLE_INITIATOR) ? + RADIO_RTT_CONFIG_ROLE_Initiator << RADIO_RTT_CONFIG_ROLE_Pos : + RADIO_RTT_CONFIG_ROLE_Reflector << RADIO_RTT_CONFIG_ROLE_Pos); + g_ble_phy_cs.report_tone_results = 1; + total_duration = t->duration_usecs; + next = t->next; + if (next && next->is_tx == t->is_tx && next->mode == t->mode) { + total_duration += t->end_tifs + next->duration_usecs; + offset_us += next->end_tifs; + g_ble_phy_cs.prev_transm = next; + next = next->next; + } else { + g_ble_phy_cs.prev_transm = t; + } + offset_us += total_duration + t->end_tifs; + end_us = start_us + total_duration; + + if (t->is_tx) { + if (t->tone_mode == BLE_PHY_CS_TONE_MODE_PM) { + NRF_RADIO->CSTONES.MODE = RADIO_CSTONES_MODE_TPM_Msk | RADIO_CSTONES_MODE_TFM_Msk; + } else { + NRF_RADIO->CSTONES.MODE = RADIO_CSTONES_MODE_TFM_Msk; + } + } else { + ble_phy_cs_tone_configure(t->duration_usecs, t->tone_mode); + /* Schedule CSTONESSTART for RX */ + nrf_timer_cc_set(NRF_TIMER10, 4, start_us + CSTONE_EXCLUSION_PERIOD_US); + phy_ppi_timer0_compare4_to_radio_cstonesstart_enable(); + } + + t = next; + } + + /* Schedule radio disable if the end of the step */ + if (t == NULL || (t->is_tx != current_phase && + ((g_ble_phy_cs.role == BLE_PHY_CS_ROLE_INITIATOR && current_phase == 0) || + (g_ble_phy_cs.role == BLE_PHY_CS_ROLE_REFLECTOR && current_phase == 1)))) { + nrf_timer_cc_set(NRF_TIMER10, 3, end_us + 20); + phy_ppi_timer0_compare3_to_radio_disable_enable(); + g_ble_phy_cs.step_anchor_ticks = end_us + g_ble_phy_cs.prev_transm->end_tifs; + break; + } + } + + return 0; +} + +static uint32_t +ticks00_to_ns(uint32_t ticks) { + return (uint32_t)(((uint64_t)ticks * 1000000000ULL) / 128000000ULL); +} + +static void +ble_phy_cs_isr(void) +{ + int rc = 0; + struct ble_phy_cs_transmission *prev_transm = g_ble_phy_cs.prev_transm; + struct ble_phy_cs_transmission *next_transm = NULL; + struct ble_phy_cs_tone_results *tone_result = NULL; + struct ble_phy_cs_sync_results *sync_result = NULL; + uint32_t irq00_en; + uint32_t pct16_reg; + uint32_t tod_ticks00, toa_ticks00; + int16_t I16, Q16; + int16_t measured_freq_offset; + uint8_t last_half_step; + uint8_t i; + + os_trace_isr_enter(); + + irq00_en = NRF_RADIO->INTENSET00; + + if ((irq00_en & RADIO_INTENSET00_ADDRESS_Msk) && NRF_RADIO->EVENTS_ADDRESS) { + NRF_RADIO->INTENCLR00 = RADIO_INTENCLR00_ADDRESS_Msk; + + if (NRF_RADIO->EVENTS_DISABLED == 0) { + if (g_ble_phy_cs.role == BLE_PHY_CS_ROLE_INITIATOR) { + NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_rx_buf[0]; + } else { + NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_tx_buf[0]; + } + + os_trace_isr_exit(); + + return; + } + } + + /* Disable PPI */ + phy_ppi_timer0_compare3_to_radio_disable_disable(); + phy_ppi_rtc0_compare0_to_timer0_start_disable(); + phy_ppi_timer0_compare2_to_radio_start_disable(); + phy_ppi_timer0_compare0_to_radio_txen_disable(); + phy_ppi_timer0_compare1_to_radio_rxen_disable(); + phy_ppi_timer0_compare4_to_radio_cstonesstart_disable(); + + /* Make sure all interrupts are disabled */ + NRF_RADIO->INTENCLR00 = NRF_RADIO_IRQ_MASK_ALL; + RADIO_INTENCLR01 = RADIO_INTENCLR01_CSTONESEND_Msk; + NRF_TIMER10->INTENCLR = TIMER_INTENSET_COMPARE3_Msk; + NRF_RADIO->SHORTS = 0; + + BLE_LL_ASSERT((irq00_en & RADIO_INTENSET00_DISABLED_Msk) && NRF_RADIO->EVENTS_DISABLED); + + assert(prev_transm != NULL); + + next_transm = prev_transm->next; + + if(g_ble_phy_cs.report_sync_results) { + if (NRF_RADIO->EVENTS_ADDRESS == 0) { + /* Out of sync */ + ble_phy_cs_subevent_end(BLE_PHY_CS_STATUS_SYNC_LOST); + return; + } + + sync_result = &g_ble_phy_cs.sync_results; + + if (g_ble_phy_cs.role == BLE_PHY_CS_ROLE_INITIATOR) { + tod_ticks00 = NRF_TIMER00->CC[0]; + toa_ticks00 = NRF_TIMER00->CC[1]; + } else { + toa_ticks00 = NRF_TIMER00->CC[0]; + tod_ticks00 = NRF_TIMER00->CC[1]; + } + sync_result->time_of_departure_ns = ticks00_to_ns(tod_ticks00) + + txaddrdelay_ns[g_ble_phy_cs.phy_mode]; + sync_result->time_of_arrival_ns = ticks00_to_ns(toa_ticks00) - + rxaddrdelay_ns[g_ble_phy_cs.phy_mode]; + } + + ble_phy_cs_radio_events_clear(); + + if(g_ble_phy_cs.report_tone_results) { + pct16_reg = NRF_RADIO->CSTONES.PCT16; + I16 = (int16_t)(pct16_reg & 0xFFFF); + Q16 = (int16_t)((pct16_reg >> 16) & 0xFFFF); + + measured_freq_offset = ble_phy_cs_ffoest_get(); +#if MYNEWT_VAL(BLE_PHY_CS_USE_PCT_CORRECTION) + ble_phy_cs_pct_correct_ciruit_delay(&I16, &Q16, prev_transm->channel); +#endif + + tone_result = &g_ble_phy_cs.tone_results[0]; + tone_result->I16 = I16; + tone_result->Q16 = Q16; + tone_result->measured_freq_offset = measured_freq_offset; + } + + /* The step has been completed. */ + rc = ble_ll_cs_step_end(prev_transm, tone_result, sync_result); + if (rc) { + ble_phy_cs_subevent_end(BLE_PHY_CS_STATUS_SYNC_LOST); + } + + if (next_transm) { + ble_phy_cs_schedule_step(g_ble_phy_cs.step_anchor_ticks, next_transm, g_ble_phy_cs.role); + } else { + ble_phy_cs_subevent_end(BLE_PHY_CS_STATUS_COMPLETE); + } + + os_trace_isr_exit(); +} + +int +ble_phy_cs_subevent_start(struct ble_phy_cs_transmission *transm, + uint32_t cputime, uint8_t rem_usecs, uint8_t phy_mode) +{ + int rc; + + /* Disable all PPI */ + phy_ppi_timer0_compare3_to_radio_disable_disable(); + phy_ppi_radio_bcmatch_to_aar_start_disable(); + phy_ppi_radio_address_to_ccm_crypt_disable(); + phy_ppi_timer0_compare2_to_radio_start_disable(); + phy_ppi_timer0_compare0_to_radio_txen_disable(); + phy_ppi_timer0_compare0_to_radio_rxen_disable(); + phy_ppi_timer0_compare1_to_radio_rxen_disable(); + phy_ppi_timer0_compare4_to_radio_cstonesstart_disable(); + + g_ble_phy_cs.phy_mode = phy_mode; + g_ble_phy_cs.prev_transm = NULL; + + g_ble_phy_cs.role = transm->is_tx ? BLE_PHY_CS_ROLE_INITIATOR : BLE_PHY_CS_ROLE_REFLECTOR; + + phy_ppi_cs_mode_enable(); + + /* CS SYNC packet has no PDU or CRC */ + NRF_RADIO->CRCCNF = 0; + /* CS_SYNC needs only PAYLOAD field, so do not transmit S0, LENGTH and S1 fields. + * Set the preambule length to 8 bits for LE 1M and 16 bits for LE 2M. + * Set the S1 length to 4 bits to fit a CS trailer. + */ + if (phy_mode == BLE_PHY_MODE_1M) { + NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_1Mbit; + NRF_RADIO->PCNF0 = (RADIO_PCNF0_PLEN_8bit << RADIO_PCNF0_PLEN_Pos) + | (4 << RADIO_PCNF0_S1LEN_Pos); + } else if (phy_mode == BLE_PHY_MODE_2M) { + NRF_RADIO->MODE = RADIO_MODE_MODE_Ble_2Mbit; + NRF_RADIO->PCNF0 = (RADIO_PCNF0_PLEN_16bit << RADIO_PCNF0_PLEN_Pos) + | (4 << RADIO_PCNF0_S1LEN_Pos); + } else { + assert(0); + } + + /* Disable whitening */ + NRF_RADIO->PCNF1 = (NRF_BALEN << RADIO_PCNF1_BALEN_Pos) | + (RADIO_PCNF1_ENDIAN_Little << RADIO_PCNF1_ENDIAN_Pos); + NRF_RADIO->RXADDRESSES = (1 << 1); + + NRF_RADIO->RTT.CONFIG = (RADIO_RTT_CONFIG_EN_Enabled << RADIO_RTT_CONFIG_EN_Pos) | + ((g_ble_phy_cs.role == BLE_PHY_CS_ROLE_INITIATOR) ? + RADIO_RTT_CONFIG_ROLE_Initiator << RADIO_RTT_CONFIG_ROLE_Pos : + RADIO_RTT_CONFIG_ROLE_Reflector << RADIO_RTT_CONFIG_ROLE_Pos); + + NRF_RADIO->SHORTS = 0; + + ble_phy_cs_tune_set(); + + /* Make sure all interrupts are disabled */ + nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_IRQ_MASK_ALL); + + /* Set interrupt handler for Channel Sounding api */ + g_ble_phy_cs.prev_radio_isr_handler = NVIC_GetVector(RADIO_IRQn); + NVIC_SetVector(RADIO_IRQn, (uint32_t)ble_phy_cs_isr); + + /* Clear events */ + ble_phy_cs_radio_events_clear(); + + nrf_timer_task_trigger(NRF_TIMER10, NRF_TIMER_TASK_STOP); + nrf_timer_task_trigger(NRF_TIMER10, NRF_TIMER_TASK_CLEAR); +#if 1 + nrf_timer_task_trigger(NRF_TIMER00, NRF_TIMER_TASK_STOP); + nrf_timer_task_trigger(NRF_TIMER00, NRF_TIMER_TASK_CLEAR); + NRF_TIMER00->BITMODE = 3; /* 32-bit timer */ + NRF_TIMER00->MODE = 0; /* Timer mode */ + NRF_TIMER00->PRESCALER = 0; /* gives us 128 MHz */ + nrf_timer_task_trigger(NRF_TIMER00, NRF_TIMER_TASK_START); +#endif + + rc = ble_phy_cs_radio_timer_start(cputime, rem_usecs, transm->is_tx); + if (rc) { + ble_phy_cs_disable(); + return 1; + } + + rc = ble_phy_cs_schedule_step(g_ble_phy_cs.step_anchor_ticks, transm, g_ble_phy_cs.role); + if (rc) { + ble_phy_cs_disable(); + return 1; + } + + return 0; +} + +#if MYNEWT_VAL(BLE_PHY_CS_TEST_MODE_CONTINUES_TONE) +void +ble_phy_cs_tx_continuous_tone(void) +{ + int rc = 0; + + os_trace_isr_enter(); + + ble_phy_rfclk_enable(); + + phy_ppi_cs_mode_enable(); + ble_phy_cs_radio_events_clear(); + + /* Make sure all interrupts are disabled */ + nrf_radio_int_disable(NRF_RADIO, NRF_RADIO_IRQ_MASK_ALL); + + NRF_RADIO->SHORTS = 0; + ble_phy_cs_rtt_set(NULL, 0); + ble_phy_cs_channel_set(MYNEWT_VAL(BLE_PHY_CS_TEST_MODE_CONTINUES_TONE_CHANNEL)); + ble_phy_cs_tone_configure(80, BLE_PHY_CS_TONE_MODE_FM); + ble_phy_cs_tune_set(); + + NRF_RADIO->TASKS_TXEN = 1; + + while (1); + + os_trace_isr_exit(); + + return; +} +#endif +#endif diff --git a/nimble/drivers/nrf5x/src/nrf54l/phy.c b/nimble/drivers/nrf5x/src/nrf54l/phy.c new file mode 100644 index 0000000000..744aa263c3 --- /dev/null +++ b/nimble/drivers/nrf5x/src/nrf54l/phy.c @@ -0,0 +1,252 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include "phy_priv.h" +#include + +#if PHY_USE_DEBUG +void +phy_debug_init(void) +{ +#if PHY_USE_DEBUG_1 + nrf_gpio_cfg_output(MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN)); + nrf_gpiote_task_configure(NRF_GPIOTE20, PHY_GPIOTE_DEBUG_1, + MYNEWT_VAL(BLE_PHY_DBG_TIME_TXRXEN_READY_PIN), + NRF_GPIOTE_POLARITY_NONE, + NRF_GPIOTE_INITIAL_VALUE_LOW); + nrf_gpiote_task_enable(NRF_GPIOTE20, PHY_GPIOTE_DEBUG_1); + + PPIB_RADIO_PERI_0(TIMER0_EVENTS_COMPARE_0, GPIOTE20_TASKS_SET_0); + NRF_GPIOTE20->SUBSCRIBE_SET[PHY_GPIOTE_DEBUG_1] = DPPI_CH_SUB(GPIOTE20_TASKS_SET_0); + + NRF_RADIO->PUBLISH_READY = DPPI_CH_PUB(RADIO_EVENTS_READY); + PPIB_RADIO_PERI_1(RADIO_EVENTS_READY, GPIOTE20_TASKS_CLR_0); + NRF_GPIOTE20->SUBSCRIBE_CLR[PHY_GPIOTE_DEBUG_1] = DPPI_CH_SUB(GPIOTE20_TASKS_CLR_0); +#endif + +#if PHY_USE_DEBUG_2 + nrf_gpio_cfg_output(MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN)); + nrf_gpiote_task_configure(NRF_GPIOTE20, PHY_GPIOTE_DEBUG_2, + MYNEWT_VAL(BLE_PHY_DBG_TIME_ADDRESS_END_PIN), + NRF_GPIOTE_POLARITY_NONE, + NRF_GPIOTE_INITIAL_VALUE_LOW); + nrf_gpiote_task_enable(NRF_GPIOTE20, PHY_GPIOTE_DEBUG_2); + + PPIB_RADIO_PERI_2(RADIO_EVENTS_ADDRESS, GPIOTE20_TASKS_SET_1); + NRF_GPIOTE20->SUBSCRIBE_SET[PHY_GPIOTE_DEBUG_2] = DPPI_CH_SUB(GPIOTE20_TASKS_SET_1); + + PPIB_RADIO_PERI_3(RADIO_EVENTS_END, GPIOTE20_TASKS_CLR_1); + NRF_GPIOTE20->SUBSCRIBE_CLR[PHY_GPIOTE_DEBUG_2] = DPPI_CH_SUB(GPIOTE20_TASKS_CLR_1); +#endif + +#if PHY_USE_DEBUG_3 + nrf_gpio_cfg_output(MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN)); + nrf_gpiote_task_configure(NRF_GPIOTE20, PHY_GPIOTE_DEBUG_3, + MYNEWT_VAL(BLE_PHY_DBG_TIME_WFR_PIN), + NRF_GPIOTE_POLARITY_NONE, + NRF_GPIOTE_INITIAL_VALUE_LOW); + nrf_gpiote_task_enable(NRF_GPIOTE20, PHY_GPIOTE_DEBUG_3); + + NRF_RADIO->PUBLISH_RXREADY = DPPI_CH_PUB(RADIO_EVENTS_RXREADY); + PPIB_RADIO_PERI_4(RADIO_EVENTS_RXREADY, GPIOTE20_TASKS_SET_2); + NRF_GPIOTE20->SUBSCRIBE_SET[PHY_GPIOTE_DEBUG_3] = DPPI_CH_SUB(GPIOTE20_TASKS_SET_2); + + NRF_RADIO->PUBLISH_DISABLED = DPPI_CH_PUB(RADIO_EVENTS_DISABLED); + PPIB_RADIO_PERI_5(RADIO_EVENTS_DISABLED, GPIOTE20_TASKS_CLR_2); + NRF_GPIOTE20->SUBSCRIBE_CLR[PHY_GPIOTE_DEBUG_3] = DPPI_CH_SUB(GPIOTE20_TASKS_CLR_2); +#endif +} +#endif /* PHY_USE_DEBUG */ + +void +phy_ppi_init(void) +{ + NRF_RADIO->PUBLISH_ADDRESS = DPPI_CH_PUB(RADIO_EVENTS_ADDRESS); + NRF_RADIO->PUBLISH_END = DPPI_CH_PUB(RADIO_EVENTS_END); + NRF_RADIO->PUBLISH_PAYLOAD = DPPI_CH_PUB(RADIO_EVENTS_PAYLOAD_RADIO); + NRF_RADIO->PUBLISH_BCMATCH = DPPI_CH_PUB(RADIO_EVENTS_BCMATCH); + NRF_RTC0->PUBLISH_COMPARE[0] = DPPI_CH_PUB(RTC0_EVENTS_COMPARE_0); + + NRF_TIMER0->PUBLISH_COMPARE[0] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_0); + NRF_TIMER0->PUBLISH_COMPARE[3] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_3); + NRF_TIMER0->SUBSCRIBE_CAPTURE[1] = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS); + NRF_TIMER0->SUBSCRIBE_CAPTURE[2] = DPPI_CH_SUB(RADIO_EVENTS_END); + + PPIB_RADIO_MCU_0(RADIO_EVENTS_PAYLOAD_RADIO, DPPIC00_RADIO_EVENTS_PAYLOAD_CCM); + + /* Enable channels we publish on */ + NRF_DPPIC10->CHENSET = DPPI_CH_ENABLE_ALL; +#if MYNEWT_VAL(BLE_PHY_CHANNEL_SOUNDING) + RADIO_PUBLISH_CSTONESEND = DPPI_CH_PUB(RADIO_EVENTS_CSTONES_END); + NRF_DPPIC10->CHENSET = DPPI_CH_MASK(TIMER0_EVENTS_COMPARE_1) | + DPPI_CH_MASK(TIMER0_EVENTS_COMPARE_2) | + DPPI_CH_MASK(TIMER0_EVENTS_COMPARE_4) | + DPPI_CH_MASK(RADIO_EVENTS_CSTONES_END); +#endif +} + +void +phy_txpower_set(int8_t dbm) +{ + uint16_t val; + + switch (dbm) { + case 8: + val = RADIO_TXPOWER_TXPOWER_Pos8dBm; + break; + case 7: + val = RADIO_TXPOWER_TXPOWER_Pos7dBm; + break; + case 6: + val = RADIO_TXPOWER_TXPOWER_Pos6dBm; + break; + case 5: + val = RADIO_TXPOWER_TXPOWER_Pos5dBm; + break; + case 4: + val = RADIO_TXPOWER_TXPOWER_Pos4dBm; + break; + case 3: + val = RADIO_TXPOWER_TXPOWER_Pos3dBm; + break; + case 2: + val = RADIO_TXPOWER_TXPOWER_Pos2dBm; + break; + case 1: + val = RADIO_TXPOWER_TXPOWER_Pos1dBm; + break; + case 0: + val = RADIO_TXPOWER_TXPOWER_0dBm; + break; + case -1: + val = RADIO_TXPOWER_TXPOWER_Neg1dBm; + break; + case -2: + val = RADIO_TXPOWER_TXPOWER_Neg2dBm; + break; + case -3: + val = RADIO_TXPOWER_TXPOWER_Neg3dBm; + break; + case -4: + val = RADIO_TXPOWER_TXPOWER_Neg4dBm; + break; + case -5: + val = RADIO_TXPOWER_TXPOWER_Neg5dBm; + break; + case -6: + val = RADIO_TXPOWER_TXPOWER_Neg6dBm; + break; + case -7: + val = RADIO_TXPOWER_TXPOWER_Neg7dBm; + break; + case -8: + val = RADIO_TXPOWER_TXPOWER_Neg8dBm; + break; + case -9: + val = RADIO_TXPOWER_TXPOWER_Neg9dBm; + break; + case -10: + val = RADIO_TXPOWER_TXPOWER_Neg10dBm; + break; + case -12: + val = RADIO_TXPOWER_TXPOWER_Neg12dBm; + break; + case -14: + val = RADIO_TXPOWER_TXPOWER_Neg14dBm; + break; + case -16: + val = RADIO_TXPOWER_TXPOWER_Neg16dBm; + break; + case -18: + val = RADIO_TXPOWER_TXPOWER_Neg18dBm; + break; + case -20: + val = RADIO_TXPOWER_TXPOWER_Neg20dBm; + break; + case -22: + val = RADIO_TXPOWER_TXPOWER_Neg22dBm; + break; + case -28: + val = RADIO_TXPOWER_TXPOWER_Neg28dBm; + break; + case -40: + val = RADIO_TXPOWER_TXPOWER_Neg40dBm; + break; + case -46: + val = RADIO_TXPOWER_TXPOWER_Neg46dBm; + break; + default: + val = RADIO_TXPOWER_TXPOWER_0dBm; + } + + NRF_RADIO->TXPOWER = val; +} + +int8_t +phy_txpower_round(int8_t dbm) +{ + if (dbm >= (int8_t)8) { + return (int8_t)8; + } + + if (dbm >= (int8_t)-10) { + return (int8_t)dbm; + } + + if (dbm >= (int8_t)-12) { + return (int8_t)-12; + } + + if (dbm >= (int8_t)-14) { + return (int8_t)-14; + } + + if (dbm >= (int8_t)-16) { + return (int8_t)-16; + } + + if (dbm >= (int8_t)-18) { + return (int8_t)-18; + } + + if (dbm >= (int8_t)-20) { + return (int8_t)-20; + } + + if (dbm >= (int8_t)-22) { + return (int8_t)-22; + } + + if (dbm >= (int8_t)-28) { + return (int8_t)-28; + } + + if (dbm >= (int8_t)-40) { + return (int8_t)-40; + } + + return (int8_t)-46; +} diff --git a/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h b/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h new file mode 100644 index 0000000000..8f0f43ce97 --- /dev/null +++ b/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h @@ -0,0 +1,349 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_PHY_PPI_ +#define H_PHY_PPI_ + +#define DPPI_CH_PUB(_ch) (((DPPI_CH_ ## _ch) & 0xff) | (1 << 31)) +#define DPPI_CH_SUB(_ch) (((DPPI_CH_ ## _ch) & 0xff) | (1 << 31)) +#define DPPI_CH_UNSUB(_ch) (((DPPI_CH_ ## _ch) & 0xff) | (0 << 31)) +#define DPPI_CH_MASK(_ch) (1 << (DPPI_CH_ ## _ch)) + +/* DPPIC00 [0:7] */ +#define DPPI_CH_DPPIC00_RADIO_EVENTS_PAYLOAD_CCM 0 +#if MYNEWT_VAL(BLE_PHY_CHANNEL_SOUNDING) +#define DPPI_CH_DPPIC00_RADIO_EVENTS_ADDRESS 1 +#define DPPI_CH_DPPIC00_RADIO_EVENTS_ADDRESS_2 2 +#endif + +/* DPPIC10 [0:23] */ +#define DPPI_CH_TIMER0_EVENTS_COMPARE_0 0 +#define DPPI_CH_TIMER0_EVENTS_COMPARE_3 1 +#define DPPI_CH_RADIO_EVENTS_END 2 +#define DPPI_CH_RADIO_EVENTS_BCMATCH 3 +#define DPPI_CH_RADIO_EVENTS_ADDRESS 4 +#define DPPI_CH_RTC0_EVENTS_COMPARE_0 5 +#define DPPI_CH_TIMER0_EVENTS_COMPARE_2 6 +#define DPPI_CH_RADIO_EVENTS_DISABLED 7 +#define DPPI_CH_RADIO_EVENTS_READY 8 +#define DPPI_CH_RADIO_EVENTS_RXREADY 9 +#define DPPI_CH_RADIO_EVENTS_PAYLOAD_RADIO 10 +#if MYNEWT_VAL(BLE_PHY_CHANNEL_SOUNDING) +#define DPPI_CH_TIMER0_EVENTS_COMPARE_1 11 +#define DPPI_CH_TIMER0_EVENTS_COMPARE_4 12 +#define DPPI_CH_RADIO_EVENTS_CSTONES_END 13 +#endif + +/* DPPIC20 [0:15] */ +#define DPPI_CH_GPIOTE20_TASKS_SET_0 0 +#define DPPI_CH_GPIOTE20_TASKS_CLR_0 1 +#define DPPI_CH_GPIOTE20_TASKS_SET_1 2 +#define DPPI_CH_GPIOTE20_TASKS_CLR_1 3 +#define DPPI_CH_GPIOTE20_TASKS_SET_2 4 +#define DPPI_CH_GPIOTE20_TASKS_CLR_2 5 + +#define DPPI_CH_ENABLE_ALL (DPPIC_CHEN_CH0_Msk | DPPIC_CHEN_CH1_Msk | \ + DPPIC_CHEN_CH2_Msk | DPPIC_CHEN_CH3_Msk | \ + DPPIC_CHEN_CH4_Msk | DPPIC_CHEN_CH5_Msk | \ + DPPIC_CHEN_CH10_Msk) + +#define DPPI_CH_MASK_FEM (DPPI_CH_MASK(TIMER0_EVENTS_COMPARE_2) | \ + DPPI_CH_MASK(RADIO_EVENTS_DISABLED)) + +/* nrfx not updated yet */ +#define RADIO_TASKS_CSTONESSTART (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x0A0)) +#define RADIO_TASKS_AUXDATADMASTART (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x038)) +#define RADIO_TASKS_AUXDATADMASTOP (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x03C)) +#define RADIO_SUBSCRIBE_CSTONESSTART (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x1A0)) +#define RADIO_EVENTS_AUXDATADMAEND (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x2C0)) +#define RADIO_EVENTS_CSTONESEND (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x2C8)) +#define RADIO_PUBLISH_CSTONESEND (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x3C8)) +#define RADIO_TASKS_PLLEN (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x06C)) +#define RADIO_SUBSCRIBE_PLLEN (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x16C)) +#define RADIO_EVENTS_PLLREADY (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x2B0)) +#define RADIO_PUBLISH_PLLREADY (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x3B0)) +#define RADIO_INTENSET01 (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x48C)) +#define RADIO_INTENCLR01 (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x494)) +#define RADIO_FREQFINETUNE (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x804)) +#define RADIO_AUXDATA_CNF (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x548)) +#define RADIO_AUXDATADMA_ENABLE (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x550)) +#define RADIO_AUXDATADMA_PTR (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x554)) +#define RADIO_AUXDATADMA_MAXCNT (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x558)) +#define RADIO_AUXDATADMA_AMOUNT (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0x55C)) +#define RADIO_DBCCORR (*(volatile uint32_t*)((uint8_t*)NRF_RADIO + 0xB40)) +#define RADIO_DBCCORR_ResetValue (0x1FFFFF90UL) +#define RADIO_AUXDATA_CNF_ACQMODE_Rtt (0x07UL) +#define RADIO_AUXDATADMA_ENABLE_ENABLE_Enabled (0x1UL) +#define RADIO_INTENSET01_CSTONESEND_Msk (1 << 18UL) +#define RADIO_INTENCLR01_CSTONESEND_Msk (1 << 18UL) + +/* Create PPIB links between RADIO and PERI power domain. */ +#define PPIB_RADIO_PERI(_ch, _src, _dst) \ + NRF_PPIB11->SUBSCRIBE_SEND[_ch] = DPPI_CH_SUB(_src); \ + NRF_PPIB21->PUBLISH_RECEIVE[_ch] = DPPI_CH_PUB(_dst); \ + NRF_DPPIC10->CHENSET |= 1 << DPPI_CH_ ## _src; \ + NRF_DPPIC20->CHENSET |= 1 << DPPI_CH_ ## _dst; + +#define PPIB_RADIO_PERI_0(_src, _dst) PPIB_RADIO_PERI(0, _src, _dst) +#define PPIB_RADIO_PERI_1(_src, _dst) PPIB_RADIO_PERI(1, _src, _dst) +#define PPIB_RADIO_PERI_2(_src, _dst) PPIB_RADIO_PERI(2, _src, _dst) +#define PPIB_RADIO_PERI_3(_src, _dst) PPIB_RADIO_PERI(3, _src, _dst) +#define PPIB_RADIO_PERI_4(_src, _dst) PPIB_RADIO_PERI(4, _src, _dst) +#define PPIB_RADIO_PERI_5(_src, _dst) PPIB_RADIO_PERI(5, _src, _dst) +#define PPIB_RADIO_PERI_6(_src, _dst) PPIB_RADIO_PERI(6, _src, _dst) + +/* Create PPIB link from RADIO to MCU power domain. */ +#define PPIB_RADIO_MCU(_ch, _src, _dst) \ + NRF_PPIB10->SUBSCRIBE_SEND[_ch] = DPPI_CH_SUB(_src); \ + NRF_PPIB00->PUBLISH_RECEIVE[_ch] = DPPI_CH_PUB(_dst); \ + NRF_DPPIC10->CHENSET |= 1 << DPPI_CH_ ## _src; \ + NRF_DPPIC00->CHENSET |= 1 << DPPI_CH_ ## _dst; + +#define PPIB_RADIO_MCU_0(_src, _dst) PPIB_RADIO_MCU(0, _src, _dst) + +static inline void +phy_ppi_rtc0_compare0_to_timer0_start_enable(void) +{ + NRF_TIMER0->SUBSCRIBE_START = DPPI_CH_SUB(RTC0_EVENTS_COMPARE_0); +} + +static inline void +phy_ppi_rtc0_compare0_to_timer0_start_disable(void) +{ + NRF_TIMER0->SUBSCRIBE_START = DPPI_CH_UNSUB(RTC0_EVENTS_COMPARE_0); + NRF_TIMER0->SUBSCRIBE_CAPTURE[3] = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); + NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_3); +} + +static inline void +phy_ppi_timer0_compare0_to_radio_txen_enable(void) +{ + NRF_RADIO->SUBSCRIBE_TXEN = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_0); +} + +static inline void +phy_ppi_timer0_compare0_to_radio_txen_disable(void) +{ + NRF_RADIO->SUBSCRIBE_TXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0); +} + +static inline void +phy_ppi_timer0_compare0_to_radio_rxen_enable(void) +{ + NRF_RADIO->SUBSCRIBE_RXEN = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_0); +} + +static inline void +phy_ppi_timer0_compare0_to_radio_rxen_disable(void) +{ + NRF_RADIO->SUBSCRIBE_RXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0); +} + +static inline void +phy_ppi_radio_address_to_ccm_crypt_enable(void) +{ + NRF_CCM->SUBSCRIBE_START = DPPI_CH_SUB(DPPIC00_RADIO_EVENTS_PAYLOAD_CCM); +} + +static inline void +phy_ppi_radio_address_to_ccm_crypt_disable(void) +{ + NRF_CCM->SUBSCRIBE_START = DPPI_CH_UNSUB(DPPIC00_RADIO_EVENTS_PAYLOAD_CCM); +} + +static inline void +phy_ppi_radio_bcmatch_to_aar_start_enable(void) +{ + NRF_AAR->SUBSCRIBE_START = DPPI_CH_SUB(RADIO_EVENTS_BCMATCH); +} + +static inline void +phy_ppi_radio_bcmatch_to_aar_start_disable(void) +{ + NRF_AAR->SUBSCRIBE_START = DPPI_CH_UNSUB(RADIO_EVENTS_BCMATCH); +} + +static inline void +phy_ppi_wfr_enable(void) +{ + NRF_TIMER0->SUBSCRIBE_CAPTURE[3] = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS); + NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_3); +} + +static inline void +phy_ppi_wfr_disable(void) +{ + NRF_TIMER0->SUBSCRIBE_CAPTURE[3] = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); + NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_3); +} + +#if MYNEWT_VAL(BLE_PHY_CHANNEL_SOUNDING) +static inline void +phy_ppi_timer0_compare1_to_radio_rxen_enable(void) +{ + NRF_RADIO->SUBSCRIBE_RXEN = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_1); +} + +static inline void +phy_ppi_timer0_compare1_to_radio_rxen_disable(void) +{ + NRF_RADIO->SUBSCRIBE_RXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_1); +} + +static inline void +phy_ppi_timer0_compare2_to_radio_start_enable(void) +{ + NRF_RADIO->SUBSCRIBE_START = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_2); +} + +static inline void +phy_ppi_timer0_compare2_to_radio_start_disable(void) +{ + NRF_RADIO->SUBSCRIBE_START = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_2); +} + +static inline void +phy_ppi_timer0_compare3_to_radio_disable_enable(void) +{ + NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_3); +} + +static inline void +phy_ppi_timer0_compare3_to_radio_disable_disable(void) +{ + NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_3); +} + +static inline void +phy_ppi_timer0_compare4_to_radio_cstonesstart_enable(void) +{ + RADIO_SUBSCRIBE_CSTONESSTART = DPPI_CH_SUB(TIMER0_EVENTS_COMPARE_4); +} + +static inline void +phy_ppi_timer0_compare4_to_radio_cstonesstart_disable(void) +{ + RADIO_SUBSCRIBE_CSTONESSTART = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_4); +} + +static inline void +phy_ppi_timer00_radio_address_to_capture_enable(void) +{ + /* Capture ADDRESS event time of CS_SYNC TX and RX */ + NRF_DPPIC00->CHG[0] = (1 << DPPI_CH_DPPIC00_RADIO_EVENTS_ADDRESS); + + NRF_PPIB10->SUBSCRIBE_SEND[1] = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS); + NRF_PPIB10->SUBSCRIBE_SEND[2] = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS); + NRF_PPIB00->PUBLISH_RECEIVE[1] = DPPI_CH_PUB(DPPIC00_RADIO_EVENTS_ADDRESS); + NRF_PPIB00->PUBLISH_RECEIVE[2] = DPPI_CH_PUB(DPPIC00_RADIO_EVENTS_ADDRESS_2); + + NRF_DPPIC10->CHENSET = 1 << DPPI_CH_RADIO_EVENTS_ADDRESS; + NRF_DPPIC00->CHENSET = (1 << DPPI_CH_DPPIC00_RADIO_EVENTS_ADDRESS) | + (1 << DPPI_CH_DPPIC00_RADIO_EVENTS_ADDRESS_2); + + NRF_TIMER00->SUBSCRIBE_CAPTURE[0] = DPPI_CH_SUB(DPPIC00_RADIO_EVENTS_ADDRESS); + NRF_TIMER00->SUBSCRIBE_CAPTURE[1] = DPPI_CH_SUB(DPPIC00_RADIO_EVENTS_ADDRESS_2); + + NRF_DPPIC00->SUBSCRIBE_CHG[0].DIS = (1 << DPPI_CH_DPPIC00_RADIO_EVENTS_ADDRESS) | DPPIC_SUBSCRIBE_CHG_DIS_EN_Msk; + NRF_DPPIC00->TASKS_CHG[0].EN = 1; +} + +static inline void +phy_ppi_timer00_radio_address_to_capture_disable(void) +{ + NRF_TIMER00->SUBSCRIBE_CAPTURE[0] = DPPI_CH_UNSUB(DPPIC00_RADIO_EVENTS_ADDRESS); + NRF_TIMER00->SUBSCRIBE_CAPTURE[1] = DPPI_CH_UNSUB(DPPIC00_RADIO_EVENTS_ADDRESS); + NRF_PPIB10->SUBSCRIBE_SEND[1] = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); + NRF_PPIB10->SUBSCRIBE_SEND[2] = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); + NRF_PPIB00->PUBLISH_RECEIVE[1] = DPPI_CH_UNSUB(DPPIC00_RADIO_EVENTS_ADDRESS); + NRF_PPIB00->PUBLISH_RECEIVE[2] = DPPI_CH_UNSUB(DPPIC00_RADIO_EVENTS_ADDRESS_2); + NRF_DPPIC00->CHG[0] = 0; + NRF_DPPIC00->CHENCLR = (1 << DPPI_CH_DPPIC00_RADIO_EVENTS_ADDRESS) | + (1 << DPPI_CH_DPPIC00_RADIO_EVENTS_ADDRESS_2); +} + +static inline void +phy_ppi_cs_mode_enable(void) +{ + NRF_TIMER0->SUBSCRIBE_CAPTURE[1] = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); + NRF_TIMER0->SUBSCRIBE_CAPTURE[2] = DPPI_CH_UNSUB(RADIO_EVENTS_END); + NRF_TIMER0->SUBSCRIBE_CAPTURE[6] = DPPI_CH_SUB(RADIO_EVENTS_CSTONES_END); + + /* CC[0] and CC[3] already published */ + NRF_TIMER0->PUBLISH_COMPARE[1] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_1); + NRF_TIMER0->PUBLISH_COMPARE[2] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_2); + NRF_TIMER0->PUBLISH_COMPARE[6] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_2); + NRF_TIMER0->PUBLISH_COMPARE[4] = DPPI_CH_PUB(TIMER0_EVENTS_COMPARE_4); + NRF_TIMER0->SUBSCRIBE_CAPTURE[5] = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS); + + PPIB_RADIO_PERI_6(TIMER0_EVENTS_COMPARE_1, GPIOTE20_TASKS_SET_0); +} + +static inline void +phy_ppi_cs_mode_disable(void) +{ + NRF_TIMER0->PUBLISH_COMPARE[1] = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_1); + NRF_TIMER0->PUBLISH_COMPARE[2] = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_2); + NRF_TIMER0->PUBLISH_COMPARE[6] = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_2); + NRF_TIMER0->PUBLISH_COMPARE[4] = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_4); + + NRF_TIMER0->SUBSCRIBE_CAPTURE[5] = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); + NRF_TIMER0->SUBSCRIBE_CAPTURE[6] = DPPI_CH_UNSUB(RADIO_EVENTS_CSTONES_END); + NRF_TIMER0->SUBSCRIBE_CAPTURE[1] = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS); + NRF_TIMER0->SUBSCRIBE_CAPTURE[2] = DPPI_CH_SUB(RADIO_EVENTS_END); + + NRF_PPIB11->SUBSCRIBE_SEND[6] = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_1); + NRF_PPIB21->PUBLISH_RECEIVE[6] = DPPI_CH_UNSUB(GPIOTE20_TASKS_SET_0); +} +#endif + +static inline void +phy_ppi_fem_disable(void) +{ +#if PHY_USE_FEM_SINGLE_GPIO + NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM] = + DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_3); +#else +#if PHY_USE_FEM_PA + NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM_PA] = + DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_2); +#endif +#if PHY_USE_FEM_LNA + NRF_GPIOTE->SUBSCRIBE_SET[PHY_GPIOTE_FEM_LNA] = + DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_2); +#endif +#endif +} + +static inline void +phy_ppi_disable(void) +{ + NRF_TIMER0->SUBSCRIBE_START = DPPI_CH_UNSUB(RTC0_EVENTS_COMPARE_0); + NRF_TIMER0->SUBSCRIBE_CAPTURE[3] = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); + NRF_RADIO->SUBSCRIBE_DISABLE = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_3); + NRF_RADIO->SUBSCRIBE_TXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0); + NRF_RADIO->SUBSCRIBE_RXEN = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0); + NRF_RADIO->SUBSCRIBE_START = DPPI_CH_UNSUB(TIMER0_EVENTS_COMPARE_0); + NRF_AAR->SUBSCRIBE_START = DPPI_CH_UNSUB(RADIO_EVENTS_BCMATCH); + NRF_CCM->SUBSCRIBE_START = DPPI_CH_UNSUB(DPPIC00_RADIO_EVENTS_PAYLOAD_CCM); + + phy_ppi_fem_disable(); +} + +#endif /* H_PHY_PPI_ */ diff --git a/nimble/drivers/nrf5x/src/phy_priv.h b/nimble/drivers/nrf5x/src/phy_priv.h index db0664da2b..83fe963e8d 100644 --- a/nimble/drivers/nrf5x/src/phy_priv.h +++ b/nimble/drivers/nrf5x/src/phy_priv.h @@ -53,6 +53,7 @@ #define PHY_GPIOTE_FEM_LNA (PHY_GPIOTE_FEM_PA - PHY_USE_FEM_LNA) #endif +#ifndef NRF54L_SERIES static inline void phy_gpiote_configure(int idx, int pin) { @@ -61,6 +62,7 @@ phy_gpiote_configure(int idx, int pin) NRF_GPIOTE_INITIAL_VALUE_LOW); nrf_gpiote_task_enable(NRF_GPIOTE, idx); } +#endif #if PHY_USE_DEBUG void phy_debug_init(void); @@ -92,5 +94,26 @@ int8_t phy_txpower_round(int8_t dbm); #ifdef NRF53_SERIES #include "nrf53/phy_ppi.h" #endif +#ifdef NRF54L_SERIES +#include +#define NRF_TIMER0 NRF_TIMER10 +#define TIMER0_IRQn TIMER10_IRQn +#define NRF_DPPIC NRF_DPPIC10 +#define NRF_RTC0 NRF_RTC10 +#define NRF_AAR NRF_AAR00 +#define NRF_CCM NRF_CCM00 +#define NRF_AAR NRF_AAR00 +#define NRF_GPIOTE NRF_GPIOTE20 +#define RADIO_IRQn RADIO_0_IRQn +#define RADIO_INTENCLR_ADDRESS_Msk RADIO_INTENCLR00_ADDRESS_Msk +#define RADIO_INTENSET_DISABLED_Msk RADIO_INTENSET00_DISABLED_Msk +#define RADIO_INTENCLR_DISABLED_Msk RADIO_INTENCLR00_DISABLED_Msk +#define RADIO_INTENSET_END_Msk RADIO_INTENSET00_END_Msk +#define RADIO_INTENCLR_END_Msk RADIO_INTENCLR00_END_Msk +#define RADIO_INTENCLR_PHYEND_Msk RADIO_INTENCLR00_PHYEND_Msk +#define RADIO_INTENSET_ADDRESS_Msk RADIO_INTENSET00_ADDRESS_Msk +#define RADIO_INTENSET_READY_Msk RADIO_INTENSET00_READY_Msk +#include "nrf54l/phy_ppi.h" +#endif #endif /* H_PHY_PRIV_ */ diff --git a/nimble/drivers/nrf5x/syscfg.yml b/nimble/drivers/nrf5x/syscfg.yml index f5a227305c..86ae13b3ef 100644 --- a/nimble/drivers/nrf5x/syscfg.yml +++ b/nimble/drivers/nrf5x/syscfg.yml @@ -89,6 +89,40 @@ syscfg.defs: custom UICR instead of FICR register. value: 0 + BLE_PHY_CHANNEL_SOUNDING: + description: > + Enable support for Channel Sounding feature. + value: MYNEWT_VAL(BLE_CHANNEL_SOUNDING) + state: experimental + BLE_PHY_CS_FREQFINETUNE: + description: > + Fine tuning of the RF frequency. + value: 0 + BLE_PHY_CS_USE_PCT_CORRECTION: + description: Apply the PCT corrections. + value: 0 + BLE_PHY_CS_PCT_CORRECTION_PER_CHANNEL: + description: > + The list of PCT angle corrections per channel. + Channels 0, 1, 23, 24, 25, 77, 78, and the bit 79 (non-channel) are RFU, + its values will be ignored. + value: "(int16_t[80]){0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}" + BLE_PHY_CS_TEST_MODE_CONTINUES_TONE: + description: > + Configure the device to transmit a continuous tone on one channel. + value: 0 + BLE_PHY_CS_TEST_MODE_CONTINUES_TONE_CHANNEL: + description: > + The channel number on which the continuous tone will be transmitted. + value: 0 + syscfg.restrictions: # code supports turn on times up to 90us due to some optimizations, but it # should be enough for most (all?) cases