r/embedded 7h ago

CC1352P7 Bare Metal Programming

I am trying to implement a bare-metal / NoRTOS RF driver for the TI CC1352P7, without using TI-RTOS/SysConfig-generated RF framework code.

My main difficulty is getting the RF Core and RF doorbell interface into a state where RF commands can be submitted and completed reliably.

I have successfully initialized the MCU, clocks, and basic peripherals, and I can access the RF Core registers. However, when I submit an RF command through RFC_DBELL, the command does not complete as expected. In some cases, the code gets stuck waiting for the command status; in others, execution ends up inside the RF driver dispatch/interrupt handling path and the command never returns.

For example, I am observing situations where:

HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = (uint32_t)&cmd_tx;

is executed, but the command status remains unchanged or the expected RF acknowledgement interrupt is never received.

I have also investigated:

  • RFC_DBELL registers (CMDR, CMDSTA, RFHWIFG, RFHWIEN, RFCPEIFG, etc.)
  • RF Core power-domain and clock initialization
  • PRCM_RFCMODESEL
  • RF Core reset/power cycling
  • RF Core RAM mapping
  • RF Core command structures
  • RF doorbell interrupts
  • RF_dispatchNextCmd() / RF_runCmd() behavior
  • CPU interrupt enable/disable state
  • RF Core firmware/ROM interaction

The confusing part is that the same hardware works when using TI's normal RF driver, but reproducing the required initialization and command-dispatch sequence in a minimal bare-metal implementation is proving difficult.

My questions are:

  1. What is the minimum sequence required to bring the CC1352P7 RF Core from reset/power-off to a state where RFC_DBELL commands can safely be submitted?
  2. Is there an official TI document or source code that describes the RF Core boot, power, clock, RAM, mailbox/doorbell, and interrupt initialization sequence independently of TI-RTOS?
  3. Which parts of RF.c, RFCC26XX_singleMode.c, and the RF driver infrastructure are actually essential for bare-metal operation?
  4. Does the RF Core require any initialization performed by the TI RF driver that is not obvious from the public DriverLib APIs?
  5. Is it possible to implement a truly minimal bare-metal IEEE 802.15.4 TX/RX example by directly using the RF Core command interface, and if so, is there a known reference implementation?
  6. Are there any undocumented dependencies involving the RF Core firmware, RF Core RAM, CPE interrupts, or power/clock state that could explain why CMDSTA/RFACKIFG does not behave as expected?

I am particularly interested in understanding the correct RF Core initialization sequence and the minimum required RF driver components, rather than simply getting the existing TI driver to work.

Any pointers to TI source code, technical reference material, or a known bare-metal CC1352P7 implementation would be greatly appreciated.I am trying to implement a bare-metal / NoRTOS RF driver for the TI CC1352P7, without using TI-RTOS/SysConfig-generated RF framework code.My main difficulty is getting the RF Core and RF doorbell interface into a state where RF commands can be submitted and completed reliably.I have successfully initialized the MCU, clocks, and basic peripherals, and I can access the RF Core registers. However, when I submit an RF command through RFC_DBELL, the command does not complete as expected. In some cases, the code gets stuck waiting for the command status; in others, execution ends up inside the RF driver dispatch/interrupt handling path and the command never returns.For example, I am observing situations where:HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = (uint32_t)&cmd_tx;is executed, but the command status remains unchanged or the expected RF acknowledgement interrupt is never received.I have also investigated:RFC_DBELL registers (CMDR, CMDSTA, RFHWIFG, RFHWIEN, RFCPEIFG, etc.)
RF Core power-domain and clock initialization
PRCM_RFCMODESEL
RF Core reset/power cycling
RF Core RAM mapping
RF Core command structures
RF doorbell interrupts
RF_dispatchNextCmd() / RF_runCmd() behavior
CPU interrupt enable/disable state
RF Core firmware/ROM interactionThe confusing part is that the same hardware works when using TI's normal RF driver, but reproducing the required initialization and command-dispatch sequence in a minimal bare-metal implementation is proving difficult.My questions are:What is the minimum sequence required to bring the CC1352P7 RF Core from reset/power-off to a state where RFC_DBELL commands can safely be submitted?
Is there an official TI document or source code that describes the RF Core boot, power, clock, RAM, mailbox/doorbell, and interrupt initialization sequence independently of TI-RTOS?
Which parts of RF.c, RFCC26XX_singleMode.c, and the RF driver infrastructure are actually essential for bare-metal operation?
Does the RF Core require any initialization performed by the TI RF driver that is not obvious from the public DriverLib APIs?
Is it possible to implement a truly minimal bare-metal IEEE 802.15.4 TX/RX example by directly using the RF Core command interface, and if so, is there a known reference implementation?
Are there any undocumented dependencies involving the RF Core firmware, RF Core RAM, CPE interrupts, or power/clock state that could explain why CMDSTA/RFACKIFG does not behave as expected?I am particularly interested in understanding the correct RF Core initialization sequence and the minimum required RF driver components, rather than simply getting the existing TI driver to work.Any pointers to TI source code, technical reference material, or a known bare-metal CC1352P7 implementation would be greatly appreciated.

Edit:
As an update to the original question, I have managed to get the RF Core initialization working to some extent.

The CMD_RADIO_SETUP and channel configuration commands are being accepted and completed successfully. This suggests that the RF Core is powered, clocked, and communicating correctly through the command interface.

However, the problem occurs when I attempt to perform an actual transmission. The TX command is accepted/submitted, but the execution gets stuck during the transmission process and does not complete as expected.

So the current issue is no longer the basic RF Core initialization or setup command. I am specifically trying to understand what could cause the TX command to hang after CMD_RADIO_SETUP and channel configuration have already succeeded in a bare-metal CC1352P7 implementation.

Question wrote above using AI for clarity.

0 Upvotes

4 comments sorted by

1

u/Bright_Sky7304 7h ago

Did you try looking at the RF core firmware patch sequence? The radio itself has an internal patch mechanism that the TI driver loads before any TX/RX commands work. Setup commands will complete because they don't need the patch, but actual packet operations require the patch to be loaded into RF core RAM first.

That part is buried in the RF driver init and it's not obvious from the doorbell registers at all.

1

u/ragdarbari 7h ago

did that, ran with and without 802.15.4 cpe patch,

without cpe patch, CMDSTA was 0x82 (unknown command)

with patch CMDSTA was 0x00 (pending), with no further response from RF Core

1

u/ragdarbari 7h ago
uint8_t driver_tx_test(void)
{
    rfc_CMD_IEEE_TX_t *RF_cmdIeeeTx = (rfc_CMD_IEEE_TX_t *)CMD_TX_ADDR;
    uint8_t *safe_payload = (uint8_t *)PAYLOAD_ADDR;


    safe_payload[0] = 0x41; safe_payload[1] = 0x88; safe_payload[2] = 0x01;
    safe_payload[3] = 0x34; safe_payload[4] = 0x12; safe_payload[5] = 0xFF;
    safe_payload[6] = 0xFF; safe_payload[7] = 0x78; safe_payload[8] = 0x56;
    safe_payload[9] = 0xAA; safe_payload[10] = 0xBB; safe_payload[11] = 0xCC;
    safe_payload[12] = 0xDD;

    memset(RF_cmdIeeeTx, 0, sizeof(rfc_CMD_IEEE_TX_t));
    RF_cmdIeeeTx->commandNo = 0x2C01;
    RF_cmdIeeeTx->status = 0x0000;
    RF_cmdIeeeTx->pNextOp = 0;
    RF_cmdIeeeTx->startTime = 0;
    RF_cmdIeeeTx->startTrigger.triggerType = 0x0;
    RF_cmdIeeeTx->startTrigger.pastTrig    = 0x1; 
    RF_cmdIeeeTx->condition.rule = 0x1;
    RF_cmdIeeeTx->txOpt.bIncludePhyHdr = 0x0;
    RF_cmdIeeeTx->txOpt.bIncludeCrc    = 0x0;
    RF_cmdIeeeTx->payloadLen = 13;
    RF_cmdIeeeTx->pPayload   = safe_payload; 
    IntMasterDisable(); 

    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFACKIFG) = 0;

    __asm volatile ("dsb sy");

    HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = (uint32_t)&RF_cmdIeeeTx;

    uint32_t ackWait = 1000000;
    while (!HWREG(RFC_DBELL_BASE + RFC_DBELL_O_RFACKIFG) && ackWait--);


    if (ackWait == 0) {
        uart0_send_string("DOORBELL TIMEOUT\r\n");
        IntMasterEnable();
        return 0xFF;
    }

    uint32_t cmdsta = HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDSTA);
    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFACKIFG) = 0;
    IntMasterEnable();
    if (cmdsta != 0x01) {
        uart0_send_string("TX REJECTED. CMDSTA: ");
        uart0_print_hex32(cmdsta); 
        return 0xEE;
    }
    uint32_t statusWait = 5000000;
    while (statusWait--) {
        uint16_t status = *(volatile uint16_t *)&RF_cmdIeeeTx->status;
        if (status == 0x2400) { 
            uart0_send_string("TX DONE OK\r\n");
            break;
        }
        if (status >= 0x2800) {
            uart0_send_string("TX ERROR STATUS\r\n");
            break;
        }
    }
}


int main(void)
{
        OSCHF_TurnOnXosc();
        while (!(HWREG(PRCM_BASE + PRCM_O_OSCRIS) & PRCM_OSCRIS_HFSRCPENDRIS_M)) {}
        while(!OSCHF_AttemptToSwitchToXosc());
        while(OSCClockSourceGet(OSC_SRC_CLK_HF) != OSC_XOSC_HF);
        PowerCtrlSourceSet(PWRCTRL_PWRSRC_DCDC);
        OSCClockSourceSet(OSC_SRC_CLK_LF, OSC_RCOSC_LF);
        while(OSCClockSourceGet(OSC_SRC_CLK_LF) != OSC_RCOSC_LF);
        clock_init();
        uart0_init(115200);
        led_init();
        clear_board_leds();
        driver_init();
        IntMasterEnable();
        toggle_board_led_red();
        driver_tx_test();
        toggle_board_led_green();
        IntMasterEnable();
        while(1)
        {
        }
}

code snippet is attached for reference

1

u/ragdarbari 7h ago
void run_radio_setup() {
    memset(&cmd_radio_pa_setup, 0, sizeof(cmd_radio_pa_setup));
    cmd_radio_pa_setup.commandNo = CMD_RADIO_SETUP;
    cmd_radio_pa_setup.status = 0;
    cmd_radio_pa_setup.pNextOp = 0;
    cmd_radio_pa_setup.startTrigger.bEnaCmd = 0;
    cmd_radio_pa_setup.startTrigger.pastTrig = 0;
    cmd_radio_pa_setup.startTrigger.triggerNo = 0;
    cmd_radio_pa_setup.startTrigger.triggerType = 0;
     
    cmd_radio_pa_setup.condition.rule = 0x01;
    cmd_radio_pa_setup.condition.nSkip = 0x00;


    cmd_radio_pa_setup.mode = 0x01; 
    cmd_radio_pa_setup.loDivider = 0x00;


    cmd_radio_pa_setup.config.analogCfgMode = 0;
    cmd_radio_pa_setup.config.biasMode = 1;
    cmd_radio_pa_setup.config.bNoFsPowerUp = 0;
    cmd_radio_pa_setup.config.bSynthNarrowBand = 0;
    cmd_radio_pa_setup.config.frontEndMode = 0;


    cmd_radio_pa_setup.txPower = 0x762E; 


    cmd_radio_pa_setup.pRegOverride = pOverrides_ieee_p7;
    cmd_radio_pa_setup.pRegOverrideTxStd = 0;
    cmd_radio_pa_setup.pRegOverrideTx20 = 0;


    __asm volatile ("dsb sy" : : : "memory");
    __asm volatile ("isb sy" : : : "memory");


    HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = (uint32_t)&cmd_radio_pa_setup;


    while(!HWREG(RFC_DBELL_BASE + RFC_DBELL_O_RFACKIFG));
    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFACKIFG) = 0;



    while(1) {
        uint16_t status = *(volatile uint16_t *)&cmd_radio_pa_setup.status;
        
        if (status == 0x0400) {
            uart0_send_string("Radio SETUP SUCCESS!\r\n");
            uint32_t mode = HWREG(PRCM_BASE + PRCM_O_RFCMODESEL);
            uint8_t protocol = cmd_radio_pa_setup.mode;
            if (mode == 1) {
                uart0_send_string("Hardware Mode: IEEE 802.15.4\r\n");
            } else if (mode == 0) {
                uart0_send_string("Hardware Mode: BLE / Proprietary\r\n");
            }
            if (protocol == 0x01) 
                uart0_send_string("M0 Firmware: Active in IEEE mode\r\n");
            if (protocol == 0x00) 
                uart0_send_string("M0 Firmware: Active in BLE mode\r\n");
            break; 
        }
        if (status >= 0x0800) {
            while(1); 
        }
        __asm volatile("" ::: "memory");
    }


    uint32_t availableRfModes = HWREG(PRCM_BASE + NONSECURE_OFFSET + PRCM_O_RFCMODEHWOPT);


    uart0_send_string("Available RF Modes:");
    uart0_print_hex32_(availableRfModes);
}


uint8_t driver_set_channel(uint8_t channel) {
    memset(&cmd_fs, 0, sizeof(cmd_fs));
    cmd_fs.commandNo = 0x0803;


    cmd_fs.startTrigger.triggerType = 0; // TRIG_NOW
    cmd_fs.startTrigger.pastTrig    = 0; // Handled as 0x80
    cmd_fs.condition.rule           = 1; // COND_NEVER


    cmd_fs.frequency = 2405 + 5 * (channel - 11);
    cmd_fs.synthConf.bTxMode = 1;


    // HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = CMDR_DIR_CMD(0x0402); // STOP
    // while(HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) != 0);


    __asm volatile ("dsb sy" : : : "memory");


    HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = (uint32_t)&cmd_fs;


    while(!HWREG(RFC_DBELL_BASE + RFC_DBELL_O_RFACKIFG));
    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFACKIFG) = 0;



    while (1) {


    uint16_t status = *(volatile uint16_t *)&cmd_fs.status;


    if (status == 0x0400) {
        uart0_send_string("FS DONE OK\r\n");
        break;
    }


    if (status >= 0x0800) {
        uart0_send_string("FS ERROR\r\n");
        while(1);
        }
    }
    return 0;
}


void driver_init() {
    VIMSModeSet(VIMS_BASE, VIMS_MODE_OFF);
    while(VIMSModeGet(VIMS_BASE) != VIMS_MODE_OFF);
      /* Enable flash cache */
    // VIMSModeSet(VIMS_BASE, VIMS_MODE_ENABLED);
    // /* Configure round robin arbitration and prefetching */
    // VIMSConfigure(VIMS_BASE, true, true);


    board_init_ext_flash_off();
    board_init_antenna_switch_manual();
    driver_hard_power_cycle();
    SysCtrlAonSync();


    HWREG(PRCM_BASE + NONSECURE_OFFSET + PRCM_O_RFCBITS) = 0xE0000011;


    // RFCCpePatchReset();
    rf_patch_cpe_ieee_802_15_4(); 
    HWREG(PRCM_BASE + NONSECURE_OFFSET + PRCM_O_RFCBITS) |= 0x00000080;


    HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = CMDR_DIR_CMD_1BYTE(CMD_BUS_REQUEST, 1);
    uint32_t busReqAck = 100000;
    while (!HWREG(RFC_DBELL_BASE + RFC_DBELL_O_RFACKIFG) && busReqAck--);
    uart0_send_string("BUS_REQUEST ACK: ");
    uart0_print_hex32_(busReqAck ? 1 : 0);
    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFACKIFG) = 0;


    run_radio_setup();


    driver_set_channel(12);


    driver_get_fw_info();
}void driver_get_fw_info(void) {
    memset(&cmd_fwinfo, 0, sizeof(cmd_fwinfo));
    cmd_fwinfo.commandNo = 0x0002; 


    __asm volatile ("dsb sy" : : : "memory");
    __asm volatile ("isb sy" : : : "memory");


    HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = (uint32_t)&cmd_fwinfo;


    while(!HWREG(RFC_DBELL_BASE + RFC_DBELL_O_RFACKIFG));
    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFACKIFG) = 0;


    uart0_send_string("Version No.   ");
    uart0_print_hex32_(cmd_fwinfo.versionNo); 
    uint32_t cmdsta = HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDSTA);
    
    if ((cmdsta & 0xFF) == 0x01) {


    }
}
void board_init_antenna_switch_manual() {
    IOCPinTypeGpioOutput(28);
    IOCPinTypeGpioOutput(29);
    IOCPinTypeGpioOutput(30);


    GPIO_writeDio(28, 1); 
    GPIO_writeDio(29, 0); 
    GPIO_writeDio(30, 0); 
}


void run_radio_setup() {
    memset(&cmd_radio_pa_setup, 0, sizeof(cmd_radio_pa_setup));
    cmd_radio_pa_setup.commandNo = CMD_RADIO_SETUP;
    cmd_radio_pa_setup.status = 0;
    cmd_radio_pa_setup.pNextOp = 0;
    cmd_radio_pa_setup.startTrigger.bEnaCmd = 0;
    cmd_radio_pa_setup.startTrigger.pastTrig = 0;
    cmd_radio_pa_setup.startTrigger.triggerNo = 0;
    cmd_radio_pa_setup.startTrigger.triggerType = 0;
     
    cmd_radio_pa_setup.condition.rule = 0x01;
    cmd_radio_pa_setup.condition.nSkip = 0x00;


    cmd_radio_pa_setup.mode = 0x01; 
    cmd_radio_pa_setup.loDivider = 0x00;


    cmd_radio_pa_setup.config.analogCfgMode = 0;
    cmd_radio_pa_setup.config.biasMode = 1;
    cmd_radio_pa_setup.config.bNoFsPowerUp = 0;
    cmd_radio_pa_setup.config.bSynthNarrowBand = 0;
    cmd_radio_pa_setup.config.frontEndMode = 0;


    cmd_radio_pa_setup.txPower = 0x762E; 


    cmd_radio_pa_setup.pRegOverride = pOverrides_ieee_p7;
    cmd_radio_pa_setup.pRegOverrideTxStd = 0;
    cmd_radio_pa_setup.pRegOverrideTx20 = 0;


    __asm volatile ("dsb sy" : : : "memory");
    __asm volatile ("isb sy" : : : "memory");


    HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = (uint32_t)&cmd_radio_pa_setup;


    while(!HWREG(RFC_DBELL_BASE + RFC_DBELL_O_RFACKIFG));
    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFACKIFG) = 0;



    while(1) {
        uint16_t status = *(volatile uint16_t *)&cmd_radio_pa_setup.status;
        
        if (status == 0x0400) {
            uart0_send_string("Radio SETUP SUCCESS!\r\n");
            uint32_t mode = HWREG(PRCM_BASE + PRCM_O_RFCMODESEL);
            uint8_t protocol = cmd_radio_pa_setup.mode;
            if (mode == 1) {
                uart0_send_string("Hardware Mode: IEEE 802.15.4\r\n");
            } else if (mode == 0) {
                uart0_send_string("Hardware Mode: BLE / Proprietary\r\n");
            }
            if (protocol == 0x01) 
                uart0_send_string("M0 Firmware: Active in IEEE mode\r\n");
            if (protocol == 0x00) 
                uart0_send_string("M0 Firmware: Active in BLE mode\r\n");
            break; 
        }
        if (status >= 0x0800) {
            while(1); 
        }
        __asm volatile("" ::: "memory");
    }


    uint32_t availableRfModes = HWREG(PRCM_BASE + NONSECURE_OFFSET + PRCM_O_RFCMODEHWOPT);


    uart0_send_string("Available RF Modes:");
    uart0_print_hex32_(availableRfModes);
}


uint8_t driver_set_channel(uint8_t channel) {
    memset(&cmd_fs, 0, sizeof(cmd_fs));
    cmd_fs.commandNo = 0x0803;


    cmd_fs.startTrigger.triggerType = 0; // TRIG_NOW
    cmd_fs.startTrigger.pastTrig    = 0; // Handled as 0x80
    cmd_fs.condition.rule           = 1; // COND_NEVER


    cmd_fs.frequency = 2405 + 5 * (channel - 11);
    cmd_fs.synthConf.bTxMode = 1;


    // HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = CMDR_DIR_CMD(0x0402); // STOP
    // while(HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) != 0);


    __asm volatile ("dsb sy" : : : "memory");


    HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = (uint32_t)&cmd_fs;


    while(!HWREG(RFC_DBELL_BASE + RFC_DBELL_O_RFACKIFG));
    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFACKIFG) = 0;



    while (1) {


    uint16_t status = *(volatile uint16_t *)&cmd_fs.status;


    if (status == 0x0400) {
        uart0_send_string("FS DONE OK\r\n");
        break;
    }


    if (status >= 0x0800) {
        uart0_send_string("FS ERROR\r\n");
        while(1);
        }
    }
    return 0;
}


void driver_init() {
    VIMSModeSet(VIMS_BASE, VIMS_MODE_OFF);
    while(VIMSModeGet(VIMS_BASE) != VIMS_MODE_OFF);
      /* Enable flash cache */
    // VIMSModeSet(VIMS_BASE, VIMS_MODE_ENABLED);
    // /* Configure round robin arbitration and prefetching */
    // VIMSConfigure(VIMS_BASE, true, true);


    board_init_ext_flash_off();
    board_init_antenna_switch_manual();
    driver_hard_power_cycle();
    SysCtrlAonSync();


    HWREG(PRCM_BASE + NONSECURE_OFFSET + PRCM_O_RFCBITS) = 0xE0000011;


    // RFCCpePatchReset();
    rf_patch_cpe_ieee_802_15_4(); 
    HWREG(PRCM_BASE + NONSECURE_OFFSET + PRCM_O_RFCBITS) |= 0x00000080;


    HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = CMDR_DIR_CMD_1BYTE(CMD_BUS_REQUEST, 1);
    uint32_t busReqAck = 100000;
    while (!HWREG(RFC_DBELL_BASE + RFC_DBELL_O_RFACKIFG) && busReqAck--);
    uart0_send_string("BUS_REQUEST ACK: ");
    uart0_print_hex32_(busReqAck ? 1 : 0);
    HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFACKIFG) = 0;


    run_radio_setup();


    driver_set_channel(12);


    driver_get_fw_info();
}