Factory‑Calibrated `xtal_trim` Overridden by Default in SDK Build? (DWM3001CDK)

Hi everyone,

I’m using the latest SDK v1.0.2 for the DWM3001CDK.

The user manual states that each board’s clock is factory‑calibrated and its xtal_trim value is written to OTP. After flashing the firmware and running LISTCAL, I see that my board’s xtal_trim is 20.

However, when I print the configurations before running the FiRa application by doing this:

static void fira_app_process_start(void)
{
    /* OK, let's start. */
    int r = uwbmac_start(uwbmac_ctx);
    assert(r == QERR_SUCCESS);
    /* Start session. */
    r = fira_helper_start_session(&fira_ctx, session_handle);
    assert(r == QERR_SUCCESS);
    started = true;
    listener_cfg(); // print configurations
}

The listener_cfg() is defined in cmd_fn.c and I guess it is for debugging:

void listener_cfg(void)
{
    int hlen, str_len;
    char *str = qmalloc(MAX_STR_SIZE);
    dwt_app_config_t *dwt_app_config = get_app_dwt_config();
    dwt_config_t *dwt_config = &dwt_app_config->dwt_config;

    str_len = sprintf(str, "JS%04X", 0x5A5A); // reserve space for length of JS object
    hlen = str_len;
    str_len += sprintf(&str[str_len], "{\"LCFG PARAM\":{\r\n");

    str_len += sprintf(&str[str_len], "\"CHAN\":%d,\r\n", deca_to_chan(dwt_config->chan));
    str_len += sprintf(&str[str_len], "\"txPreamble\":%d,\r\n", dwt_config->txPreambLength);
    str_len += sprintf(&str[str_len], "\"PAC\":%d,\r\n", deca_to_pac(dwt_config->rxPAC));
    str_len += sprintf(&str[str_len], "\"TXPCODE\":%d,\r\n", deca_to_preamble_code(dwt_config->txCode));
    str_len += sprintf(&str[str_len], "\"PCODE\":%d,\r\n", deca_to_preamble_code(dwt_config->rxCode));
    str_len += sprintf(&str[str_len], "\"SFDTYPE\":%d,\r\n", deca_to_sfd_type(dwt_config->sfdType));
    str_len += sprintf(&str[str_len], "\"DRATE\":%d,\r\n", deca_to_bitrate(dwt_config->dataRate));
    str_len += sprintf(&str[str_len], "\"PHRMODE\":%d,\r\n", deca_to_phr_mode(dwt_config->phrMode));
    str_len += sprintf(&str[str_len], "\"PHRRATE\":%d,\r\n", deca_to_phr_rate(dwt_config->phrRate));
    str_len += sprintf(&str[str_len], "\"SFDTO\":%d,\r\n", dwt_config->sfdTO);
    str_len += sprintf(&str[str_len], "\"STSMODE\":%d,\r\n", deca_to_sts_mode(dwt_config->stsMode));
    str_len += sprintf(&str[str_len], "\"STSLEN\":%d,\r\n", deca_to_sts_length(dwt_config->stsLength));
    str_len += sprintf(&str[str_len], "\"PDOAMODE\":%d,\r\n", deca_to_pdoa_mode(dwt_config->pdoaMode));
    str_len += sprintf(&str[str_len], "\"XTALTRIM\":%u}}", dwt_app_config->xtal_trim);

    /* Add formatted 4X of length, this will erase first '{' */
    sprintf(&str[2], "%04X", str_len - hlen);
    /* Restore the start bracket. */
    str[hlen] = '{';
    str_len += sprintf(&str[str_len], "\r\n");
    reporter_instance.print((char *)str, str_len);

    qfree(str);
}

Through here I saw the xtal_trim is now 46, and it is the default xtal_trim value defined in the deca_device_api.h:

#define DEFAULT_XTAL_TRIM 0x2EU

According to the comments, the default should be used only if no OTP value exists. My board definitely has an OTP value (20), so why is the firmware still falling back to the default?

Because one xtal_trim step is around 1.65 ppm, this 26‑step difference adds up to ~43 ppm of error—quite large. DS‑TWR will cancel out clock error, but this still feels like a firmware issue. Am I interpreting the behaviour correctly, or is there something I’m missing?

Thanks for any insight!

Shanmu