From 2ba96b27c229e5522176e3a37a6e61bf6184188e Mon Sep 17 00:00:00 2001 From: Magdalena Kasenberg Date: Tue, 7 Jan 2025 11:17:59 +0100 Subject: [PATCH 1/5] nimble/phy: Add initial support for nRF54L15 PHY --- nimble/drivers/nrf5x/pkg.yml | 17 +- nimble/drivers/nrf5x/src/ble_hw.c | 133 +++++++++++- nimble/drivers/nrf5x/src/ble_phy.c | 182 +++++++++++++++- nimble/drivers/nrf5x/src/nrf54l/phy.c | 241 ++++++++++++++++++++++ nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h | 178 ++++++++++++++++ nimble/drivers/nrf5x/src/phy_priv.h | 22 ++ 6 files changed, 755 insertions(+), 18 deletions(-) create mode 100644 nimble/drivers/nrf5x/src/nrf54l/phy.c create mode 100644 nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h diff --git a/nimble/drivers/nrf5x/pkg.yml b/nimble/drivers/nrf5x/pkg.yml index b99e669248..354ffdc44e 100644 --- a/nimble/drivers/nrf5x/pkg.yml +++ b/nimble/drivers/nrf5x/pkg.yml @@ -30,7 +30,16 @@ 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" diff --git a/nimble/drivers/nrf5x/src/ble_hw.c b/nimble/drivers/nrf5x/src/ble_hw.c index f8cd59f098..96b1bb4133 100644 --- a/nimble/drivers/nrf5x/src/ble_hw.c +++ b/nimble/drivers/nrf5x/src/ble_hw.c @@ -36,9 +36,27 @@ #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 + +/* ECB data structure */ +struct ecb_job_entry { + uint8_t *ptr; + uint32_t attr_and_length; +}; + +static struct ecb_job_entry ecb_input_job_list[2]; +static struct ecb_job_entry ecb_output_job_list[2]; +#endif + /* Total number of resolving list elements */ #define BLE_HW_RESOLV_LIST_SIZE (16) @@ -276,6 +294,43 @@ 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); + + ecb_input_job_list[0].ptr = ecb->plain_text; + ecb_input_job_list[0].attr_and_length = (11 << 24) | (16 & 0x00ffffff); + ecb_output_job_list[0].ptr = ecb->cipher_text; + ecb_output_job_list[0].attr_and_length = (11 << 24) | (16 & 0x00ffffff); + + /* The end of a job list shall be 0 */ + ecb_input_job_list[1].ptr = 0; + ecb_input_job_list[1].attr_and_length = 0; + ecb_output_job_list[1].ptr = 0; + ecb_output_job_list[1].attr_and_length = 0; + + NRF_ECB->EVENTS_END = 0; + NRF_ECB->EVENTS_ERROR = 0; + NRF_ECB->IN.PTR = (uint32_t)ecb_input_job_list; + NRF_ECB->OUT.PTR = (uint32_t)ecb_output_job_list; + memcpy((void *)NRF_ECB->KEY.VALUE, ecb->key, sizeof(uint32_t) * 4); + + /* 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 +356,7 @@ ble_hw_encrypt_block(struct ble_encryption_block *ecb) tm_tick(); #endif } - +#endif return rc; } @@ -314,7 +369,25 @@ ble_rng_isr(void) uint8_t rnum; os_trace_isr_enter(); +#ifdef NRF54L_SERIES + /* 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 +403,7 @@ ble_rng_isr(void) rnum = (uint8_t)NRF_RNG->VALUE; (*g_ble_rng_isr_cb)(rnum); } - +#endif os_trace_isr_exit(); } @@ -345,6 +418,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 +456,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 +473,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 +507,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 +530,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 +634,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..4d5092c2c4 100644 --- a/nimble/drivers/nrf5x/src/ble_phy.c +++ b/nimble/drivers/nrf5x/src/ble_phy.c @@ -103,8 +103,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 +134,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 +236,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] = { @@ -948,9 +997,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 } @@ -976,6 +1033,19 @@ ble_phy_rx_xcvr_setup(void) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) if (g_ble_phy_data.phy_encrypted) { NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_enc_buf[0]; +#ifdef NRF54L_SERIES + NRF_CCM->IN.PTR = (uint32_t)&g_ble_phy_enc_buf[0]; + NRF_CCM->OUT.PTR = (uint32_t)dptr; + /* TODO: Find replacement for removed registers like this one + * NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[0]; + */ + NRF_CCM->MODE = CCM_MODE_MACLEN_Pos | CCM_MODE_MODE_Decryption | + ble_phy_get_ccm_datarate(); + memcpy((uint8_t *)NRF_CCM->KEY.VALUE, &g_nrf_ccm_data.key, sizeof(g_nrf_ccm_data.key)); + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->EVENTS_END = 0; + nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_START); +#else NRF_CCM->INPTR = (uint32_t)&g_ble_phy_enc_buf[0]; NRF_CCM->OUTPTR = (uint32_t)dptr; NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[0]; @@ -986,6 +1056,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 +1068,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: 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; @@ -1037,16 +1113,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 +1268,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 +1323,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]; @@ -1255,12 +1340,21 @@ ble_phy_rx_end_isr(void) ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_CRC_OK; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) if (g_ble_phy_data.phy_encrypted) { +#ifdef NRF54L_SERIES + while (NRF_CCM->EVENTS_END == 0) { +#else while (NRF_CCM->EVENTS_ENDCRYPT == 0) { +#endif /* Make sure CCM finished */ }; /* Only set MIC failure flag if frame is not zero length */ - if ((dptr[1] != 0) && (NRF_CCM->MICSTATUS == 0)) { + if ((dptr[1] != 0) && +#ifdef NRF54L_SERIES + (NRF_CCM->MACSTATUS == 0)) { +#else + (NRF_CCM->MICSTATUS == 0)) { +#endif ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_MIC_FAILURE; } @@ -1453,7 +1547,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; @@ -1490,7 +1588,11 @@ ble_phy_isr(void) os_trace_isr_enter(); /* Read irq register to determine which interrupts are enabled */ +#ifdef NRF54L_SERIES + irq_en = NRF_RADIO->INTENSET00; +#else irq_en = NRF_RADIO->INTENSET; +#endif /* * NOTE: order of checking is important! Possible, if things get delayed, @@ -1597,9 +1699,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 +1740,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,7 +1763,9 @@ ble_phy_init(void) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) nrf_ccm_int_disable(NRF_CCM, 0xffffffff); +#ifndef NRF54L_SERIES NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; +#endif NRF_CCM->EVENTS_ERROR = 0; memset(g_nrf_encrypt_scratchpad, 0, sizeof(g_nrf_encrypt_scratchpad)); @@ -1667,20 +1778,34 @@ 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; +#endif NRF_TIMER0->BITMODE = 3; /* 32-bit timer */ NRF_TIMER0->MODE = 0; /* Timer mode */ +#ifdef NRF54L_SERIES + NRF_TIMER0->PRESCALER = 5; /* gives us 1 MHz */ +#else NRF_TIMER0->PRESCALER = 4; /* gives us 1 MHz */ +#endif phy_ppi_init(); @@ -1741,7 +1866,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; @@ -1958,6 +2083,14 @@ 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)dptr; + NRF_CCM->OUT.PTR = (uint32_t)pktptr; + /* NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[0]; */ + NRF_CCM->EVENTS_ERROR = 0; + NRF_CCM->MODE = CCM_MODE_MACLEN_Pos | ble_phy_get_ccm_datarate(); + memcpy((uint8_t *)NRF_CCM->KEY.VALUE, &g_nrf_ccm_data.key, sizeof(g_nrf_ccm_data.key)); +#else NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; NRF_CCM->INPTR = (uint32_t)dptr; NRF_CCM->OUTPTR = (uint32_t)pktptr; @@ -1965,9 +2098,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 +2135,11 @@ 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 + 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 +2147,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 +2305,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 +2328,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 +2450,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; } diff --git a/nimble/drivers/nrf5x/src/nrf54l/phy.c b/nimble/drivers/nrf5x/src/nrf54l/phy.c new file mode 100644 index 0000000000..44c334bc80 --- /dev/null +++ b/nimble/drivers/nrf5x/src/nrf54l/phy.c @@ -0,0 +1,241 @@ +/* + * 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" + +#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_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); + + /* Enable channels we publish on */ + NRF_DPPIC10->CHENSET = DPPI_CH_ENABLE_ALL; +} + +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..5f9772b905 --- /dev/null +++ b/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h @@ -0,0 +1,178 @@ +/* + * 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)) + +/* 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 + +/* 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) + +#define DPPI_CH_MASK_FEM (DPPI_CH_MASK(TIMER0_EVENTS_COMPARE_2) | \ + DPPI_CH_MASK(RADIO_EVENTS_DISABLED)) + +/* 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) + +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(RADIO_EVENTS_ADDRESS); +} + +static inline void +phy_ppi_radio_address_to_ccm_crypt_disable(void) +{ + NRF_CCM->SUBSCRIBE_START = DPPI_CH_UNSUB(RADIO_EVENTS_ADDRESS); +} + +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); +} + +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(RADIO_EVENTS_ADDRESS); + + 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..ead8bf05f4 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,25 @@ int8_t phy_txpower_round(int8_t dbm); #ifdef NRF53_SERIES #include "nrf53/phy_ppi.h" #endif +#ifdef NRF54L_SERIES +#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_ */ From a0b3929ab3c70334b35f9c1b5bad7b2666273a59 Mon Sep 17 00:00:00 2001 From: Michal Gorecki Date: Tue, 21 Jan 2025 16:04:19 +0100 Subject: [PATCH 2/5] nimble/phy: Add an initial encryption for nRF54L15 --- nimble/drivers/nrf5x/src/ble_hw.c | 40 ++-- nimble/drivers/nrf5x/src/ble_phy.c | 221 ++++++++++++++++++---- nimble/drivers/nrf5x/src/nrf54l/phy.c | 3 + nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h | 22 ++- 4 files changed, 223 insertions(+), 63 deletions(-) diff --git a/nimble/drivers/nrf5x/src/ble_hw.c b/nimble/drivers/nrf5x/src/ble_hw.c index 96b1bb4133..44b566af1a 100644 --- a/nimble/drivers/nrf5x/src/ble_hw.c +++ b/nimble/drivers/nrf5x/src/ble_hw.c @@ -47,14 +47,12 @@ #define NRF_ECB NRF_ECB00 #define NRF_AAR NRF_AAR00 -/* ECB data structure */ -struct ecb_job_entry { - uint8_t *ptr; - uint32_t attr_and_length; +struct nrf_ecb_job_list { + nrf_vdma_job_t in[2]; + nrf_vdma_job_t out[2]; }; -static struct ecb_job_entry ecb_input_job_list[2]; -static struct ecb_job_entry ecb_output_job_list[2]; +static struct nrf_ecb_job_list g_ecb_job_list; #endif /* Total number of resolving list elements */ @@ -298,22 +296,26 @@ ble_hw_encrypt_block(struct ble_encryption_block *ecb) /* Stop ECB */ nrf_ecb_task_trigger(NRF_ECB, NRF_ECB_TASK_STOP); - ecb_input_job_list[0].ptr = ecb->plain_text; - ecb_input_job_list[0].attr_and_length = (11 << 24) | (16 & 0x00ffffff); - ecb_output_job_list[0].ptr = ecb->cipher_text; - ecb_output_job_list[0].attr_and_length = (11 << 24) | (16 & 0x00ffffff); + /* 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])); - /* The end of a job list shall be 0 */ - ecb_input_job_list[1].ptr = 0; - ecb_input_job_list[1].attr_and_length = 0; - ecb_output_job_list[1].ptr = 0; - ecb_output_job_list[1].attr_and_length = 0; + 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)ecb_input_job_list; - NRF_ECB->OUT.PTR = (uint32_t)ecb_output_job_list; - memcpy((void *)NRF_ECB->KEY.VALUE, ecb->key, sizeof(uint32_t) * 4); + 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); @@ -370,6 +372,8 @@ ble_rng_isr(void) 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); diff --git a/nimble/drivers/nrf5x/src/ble_phy.c b/nimble/drivers/nrf5x/src/ble_phy.c index 4d5092c2c4..7215392731 100644 --- a/nimble/drivers/nrf5x/src/ble_phy.c +++ b/nimble/drivers/nrf5x/src/ble_phy.c @@ -45,6 +45,9 @@ #ifdef NRF53_SERIES #include #endif +#ifdef NRF54L_SERIES +#include +#endif #include "mcu/cmsis_nvic.h" #include "hal/hal_gpio.h" #else @@ -68,6 +71,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 @@ -374,7 +384,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 @@ -397,6 +419,7 @@ struct nrf_ccm_data struct nrf_ccm_data g_nrf_ccm_data; #endif +#endif static void timer0_reset(void) @@ -1032,20 +1055,75 @@ ble_phy_rx_xcvr_setup(void) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) if (g_ble_phy_data.phy_encrypted) { - NRF_RADIO->PACKETPTR = (uint32_t)&g_ble_phy_enc_buf[0]; #ifdef NRF54L_SERIES - NRF_CCM->IN.PTR = (uint32_t)&g_ble_phy_enc_buf[0]; - NRF_CCM->OUT.PTR = (uint32_t)dptr; - /* TODO: Find replacement for removed registers like this one - * NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[0]; - */ - NRF_CCM->MODE = CCM_MODE_MACLEN_Pos | CCM_MODE_MODE_Decryption | + /* 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(); - memcpy((uint8_t *)NRF_CCM->KEY.VALUE, &g_nrf_ccm_data.key, sizeof(g_nrf_ccm_data.key)); + NRF_CCM->EVENTS_ERROR = 0; NRF_CCM->EVENTS_END = 0; - nrf_ccm_task_trigger(NRF_CCM, NRF_CCM_TASK_START); #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; NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[0]; @@ -1070,7 +1148,7 @@ ble_phy_rx_xcvr_setup(void) NRF_AAR->ENABLE = AAR_ENABLE_ENABLE_Enabled; #ifdef NRF54L_SERIES NRF_AAR->IN.PTR = (uint32_t)&g_nrf_irk_list[0]; - /* TODO: Find replacement for NRF_AAR->SCRATCHPTR */ + /* 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; @@ -1087,7 +1165,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; @@ -1340,21 +1418,12 @@ ble_phy_rx_end_isr(void) ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_CRC_OK; #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) if (g_ble_phy_data.phy_encrypted) { -#ifdef NRF54L_SERIES - while (NRF_CCM->EVENTS_END == 0) { -#else while (NRF_CCM->EVENTS_ENDCRYPT == 0) { -#endif /* Make sure CCM finished */ }; /* Only set MIC failure flag if frame is not zero length */ - if ((dptr[1] != 0) && -#ifdef NRF54L_SERIES - (NRF_CCM->MACSTATUS == 0)) { -#else - (NRF_CCM->MICSTATUS == 0)) { -#endif + if ((dptr[1] != 0) && (NRF_CCM->MICSTATUS == 0)) { ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_MIC_FAILURE; } @@ -1588,11 +1657,7 @@ ble_phy_isr(void) os_trace_isr_enter(); /* Read irq register to determine which interrupts are enabled */ -#ifdef NRF54L_SERIES - irq_en = NRF_RADIO->INTENSET00; -#else irq_en = NRF_RADIO->INTENSET; -#endif /* * NOTE: order of checking is important! Possible, if things get delayed, @@ -1763,11 +1828,11 @@ ble_phy_init(void) #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) nrf_ccm_int_disable(NRF_CCM, 0xffffffff); + NRF_CCM->EVENTS_ERROR = 0; #ifndef NRF54L_SERIES NRF_CCM->SHORTS = CCM_SHORTS_ENDKSGEN_CRYPT_Msk; -#endif - NRF_CCM->EVENTS_ERROR = 0; 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); @@ -1798,14 +1863,12 @@ ble_phy_init(void) 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 */ -#ifdef NRF54L_SERIES - NRF_TIMER0->PRESCALER = 5; /* gives us 1 MHz */ -#else - NRF_TIMER0->PRESCALER = 4; /* gives us 1 MHz */ -#endif phy_ppi_init(); @@ -1889,11 +1952,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 @@ -1904,7 +1975,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 @@ -1915,14 +1986,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 @@ -2084,12 +2165,10 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) 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)dptr; - NRF_CCM->OUT.PTR = (uint32_t)pktptr; - /* NRF_CCM->SCRATCHPTR = (uint32_t)&g_nrf_encrypt_scratchpad[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->EVENTS_ERROR = 0; - NRF_CCM->MODE = CCM_MODE_MACLEN_Pos | ble_phy_get_ccm_datarate(); - memcpy((uint8_t *)NRF_CCM->KEY.VALUE, &g_nrf_ccm_data.key, sizeof(g_nrf_ccm_data.key)); + 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; @@ -2136,6 +2215,60 @@ ble_phy_tx(ble_phy_tx_pducb_t pducb, void *pducb_arg, uint8_t end_trans) } #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); @@ -2508,6 +2641,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 @@ -2523,6 +2659,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/nrf54l/phy.c b/nimble/drivers/nrf5x/src/nrf54l/phy.c index 44c334bc80..68df810d82 100644 --- a/nimble/drivers/nrf5x/src/nrf54l/phy.c +++ b/nimble/drivers/nrf5x/src/nrf54l/phy.c @@ -85,6 +85,7 @@ 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); @@ -93,6 +94,8 @@ phy_ppi_init(void) 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; } diff --git a/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h b/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h index 5f9772b905..a624a95534 100644 --- a/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h +++ b/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h @@ -25,6 +25,9 @@ #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 + /* DPPIC10 [0:23] */ #define DPPI_CH_TIMER0_EVENTS_COMPARE_0 0 #define DPPI_CH_TIMER0_EVENTS_COMPARE_3 1 @@ -36,6 +39,7 @@ #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 /* DPPIC20 [0:15] */ #define DPPI_CH_GPIOTE20_TASKS_SET_0 0 @@ -47,7 +51,8 @@ #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_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)) @@ -66,6 +71,15 @@ #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) +/* 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) { @@ -107,13 +121,13 @@ phy_ppi_timer0_compare0_to_radio_rxen_disable(void) static inline void phy_ppi_radio_address_to_ccm_crypt_enable(void) { - NRF_CCM->SUBSCRIBE_START = DPPI_CH_SUB(RADIO_EVENTS_ADDRESS); + 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(RADIO_EVENTS_ADDRESS); + NRF_CCM->SUBSCRIBE_START = DPPI_CH_UNSUB(DPPIC00_RADIO_EVENTS_PAYLOAD_CCM); } static inline void @@ -170,7 +184,7 @@ phy_ppi_disable(void) 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(RADIO_EVENTS_ADDRESS); + NRF_CCM->SUBSCRIBE_START = DPPI_CH_UNSUB(DPPIC00_RADIO_EVENTS_PAYLOAD_CCM); phy_ppi_fem_disable(); } From f7212bb7570bb55cda7cae6038700d68d5e4f3a0 Mon Sep 17 00:00:00 2001 From: Magdalena Kasenberg Date: Wed, 13 May 2026 20:53:49 +0200 Subject: [PATCH 3/5] nimble/phy: nRF54L15: Add workaround to use RTC10 on nrfx 3.14.0 The RTC for nRF54L15 has been removed since nrfx 3.10.0, but at least older board revisions (like 1.0.0) can still handle RTC at the old address. --- nimble/drivers/nrf5x/src/ble_phy.c | 3 +++ nimble/drivers/nrf5x/src/nrf54l/phy.c | 1 + nimble/drivers/nrf5x/src/phy_priv.h | 1 + 3 files changed, 5 insertions(+) diff --git a/nimble/drivers/nrf5x/src/ble_phy.c b/nimble/drivers/nrf5x/src/ble_phy.c index 7215392731..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 */ @@ -47,6 +49,7 @@ #endif #ifdef NRF54L_SERIES #include +#include #endif #include "mcu/cmsis_nvic.h" #include "hal/hal_gpio.h" diff --git a/nimble/drivers/nrf5x/src/nrf54l/phy.c b/nimble/drivers/nrf5x/src/nrf54l/phy.c index 68df810d82..ae3324dac8 100644 --- a/nimble/drivers/nrf5x/src/nrf54l/phy.c +++ b/nimble/drivers/nrf5x/src/nrf54l/phy.c @@ -25,6 +25,7 @@ #include #include #include "phy_priv.h" +#include #if PHY_USE_DEBUG void diff --git a/nimble/drivers/nrf5x/src/phy_priv.h b/nimble/drivers/nrf5x/src/phy_priv.h index ead8bf05f4..83fe963e8d 100644 --- a/nimble/drivers/nrf5x/src/phy_priv.h +++ b/nimble/drivers/nrf5x/src/phy_priv.h @@ -95,6 +95,7 @@ int8_t phy_txpower_round(int8_t dbm); #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 From 81a646dfe04b82903143432dea377401c7fc15b3 Mon Sep 17 00:00:00 2001 From: Magdalena Kasenberg Date: Fri, 22 May 2026 13:26:21 +0200 Subject: [PATCH 4/5] nimble/phy: nrf5x: Add Channel Sounding to PHY --- .../controller/include/controller/ble_phy.h | 54 ++ nimble/drivers/nrf5x/pkg.yml | 3 + nimble/drivers/nrf5x/src/ble_phy_cs.c | 807 ++++++++++++++++++ nimble/drivers/nrf5x/src/nrf54l/phy.c | 7 + nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h | 157 ++++ nimble/drivers/nrf5x/syscfg.yml | 34 + 6 files changed, 1062 insertions(+) create mode 100644 nimble/drivers/nrf5x/src/ble_phy_cs.c 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 354ffdc44e..71d6dc9ab2 100644 --- a/nimble/drivers/nrf5x/pkg.yml +++ b/nimble/drivers/nrf5x/pkg.yml @@ -43,3 +43,6 @@ pkg.source_files.'MCU_TARGET=="nRF5340_NET"': pkg.source_files.'MCU_TARGET=="nRF54L15"': - "src/nrf54l/phy.c" + +pkg.source_files.'BLE_CHANNEL_SOUNDING': + - "src/ble_phy_cs.c" 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..4231d37105 --- /dev/null +++ b/nimble/drivers/nrf5x/src/ble_phy_cs.c @@ -0,0 +1,807 @@ +/* + * 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; +} +#endif diff --git a/nimble/drivers/nrf5x/src/nrf54l/phy.c b/nimble/drivers/nrf5x/src/nrf54l/phy.c index ae3324dac8..744aa263c3 100644 --- a/nimble/drivers/nrf5x/src/nrf54l/phy.c +++ b/nimble/drivers/nrf5x/src/nrf54l/phy.c @@ -99,6 +99,13 @@ phy_ppi_init(void) /* 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 diff --git a/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h b/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h index a624a95534..8f0f43ce97 100644 --- a/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h +++ b/nimble/drivers/nrf5x/src/nrf54l/phy_ppi.h @@ -27,6 +27,10 @@ /* 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 @@ -40,6 +44,11 @@ #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 @@ -57,6 +66,33 @@ #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); \ @@ -70,6 +106,7 @@ #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) \ @@ -156,6 +193,126 @@ phy_ppi_wfr_disable(void) 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) { 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 From 1df431a3ec02a0f9c1132a1626fce3dcc04e8487 Mon Sep 17 00:00:00 2001 From: Magdalena Kasenberg Date: Tue, 14 Apr 2026 13:28:57 +0200 Subject: [PATCH 5/5] nimble/phy: nrf5x: Add continues tone test mode Use BLE_PHY_CS_TEST_MODE_CONTINUES_TONE and BLE_PHY_CS_TEST_MODE_CONTINUES_TONE when building an image to put the radio into continuous tone transmission mode. Helpful for frequency calibration. --- nimble/drivers/nrf5x/pkg.yml | 3 +++ nimble/drivers/nrf5x/src/ble_phy_cs.c | 32 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/nimble/drivers/nrf5x/pkg.yml b/nimble/drivers/nrf5x/pkg.yml index 71d6dc9ab2..90893277c1 100644 --- a/nimble/drivers/nrf5x/pkg.yml +++ b/nimble/drivers/nrf5x/pkg.yml @@ -46,3 +46,6 @@ pkg.source_files.'MCU_TARGET=="nRF54L15"': 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_phy_cs.c b/nimble/drivers/nrf5x/src/ble_phy_cs.c index 4231d37105..5b41397bb8 100644 --- a/nimble/drivers/nrf5x/src/ble_phy_cs.c +++ b/nimble/drivers/nrf5x/src/ble_phy_cs.c @@ -804,4 +804,36 @@ ble_phy_cs_subevent_start(struct ble_phy_cs_transmission *transm, 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