Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework CMAC sleep calculations #3049

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions hw/drivers/ipc_cmac/include/ipc_cmac/shm.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ extern "C" {

#define CMAC_SHM_CB_MAGIC 0xc3ac

#define CMAC_SHM_CB_PENDING_OP_LP_CLK 0x0001
#define CMAC_SHM_CB_PENDING_OP_SLEEP_UPDATE 0x0001
#define CMAC_SHM_CB_PENDING_OP_RF_CAL 0x0002

#define CMAC_SHM_VECT_MAGIC 0xc3ac0001
Expand All @@ -59,7 +59,7 @@ struct cmac_shm_ctrl {
uint16_t magic;
uint16_t pending_ops;
uint16_t lp_clock_freq;
uint16_t xtal32m_settle_us;
uint16_t wakeup_lpclk_ticks;
};

struct cmac_shm_mbox {
Expand Down
2 changes: 2 additions & 0 deletions hw/drivers/ipc_cmac/include/ipc_cmac/shm_hs.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ extern volatile struct cmac_shm_debugdata *g_cmac_shm_debugdata;

void cmac_host_init(void);
void cmac_host_signal2cmac(void);

void cmac_host_req_sleep_update(void);
void cmac_host_rf_calibrate(void);

#ifdef __cplusplus
Expand Down
56 changes: 39 additions & 17 deletions hw/drivers/ipc_cmac/src/shm_hs.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <ipc_cmac/rand.h>
#include "trng/trng.h"
#include "console/console.h"
#include "mcu/da1469x_sleep.h"

extern char _binary_cmac_img_bin_start[];
extern char _binary_cmac_img_bin_end;
Expand Down Expand Up @@ -196,20 +197,10 @@ cmac_host_rand_chk_fill(void)
}
}

static void
cmac_host_lpclk_cb(uint32_t freq)
static bool
shm_synced(void)
{
/* No need to wakeup CMAC if LP clock frequency did not change */
if (g_cmac_shm_ctrl->lp_clock_freq == freq) {
return;
}

cmac_shm_lock();
g_cmac_shm_ctrl->lp_clock_freq = freq;
g_cmac_shm_ctrl->pending_ops |= CMAC_SHM_CB_PENDING_OP_LP_CLK;
cmac_shm_unlock();

cmac_host_signal2cmac();
return g_cmac_shm_ctrl && (g_cmac_shm_ctrl->magic == CMAC_SHM_CB_MAGIC);
}

static void
Expand All @@ -234,8 +225,8 @@ shm_configure(void)
struct cmac_shm_trim *trim;
uint32_t *trim_data;

g_cmac_shm_ctrl->xtal32m_settle_us =
MYNEWT_VAL(MCU_CLOCK_XTAL32M_SETTLE_TIME_US);
g_cmac_shm_ctrl->lp_clock_freq = 0;
g_cmac_shm_ctrl->wakeup_lpclk_ticks = 0;

trim = (struct cmac_shm_trim *)g_cmac_shm_trim;
trim_data = trim->data;
Expand Down Expand Up @@ -415,7 +406,7 @@ cmac_start(void)
/* Release CMAC from reset and sync */
CRG_TOP->CLK_RADIO_REG &= ~CRG_TOP_CLK_RADIO_REG_CMAC_SYNCH_RESET_Msk;

while (g_cmac_shm_ctrl->magic != CMAC_SHM_CB_MAGIC) {
while (!shm_synced()) {
/* Wait for CMAC to initialize */
}
NVIC_EnableIRQ(CMAC2SYS_IRQn);
Expand Down Expand Up @@ -443,7 +434,7 @@ cmac_host_init(void)

cmac_start();

da1469x_lpclk_register_cmac_cb(cmac_host_lpclk_cb);
cmac_host_req_sleep_update();

#if MYNEWT_VAL(CMAC_DEBUG_HOST_PRINT_ENABLE) && MYNEWT_VAL(CMAC_DEBUG_DATA_ENABLE)
/* Trim values are calculated on RF init, so are valid after synced with CMAC */
Expand All @@ -462,9 +453,40 @@ cmac_host_signal2cmac(void)
da1469x_pdc_set(g_cmac_host_pdc_sys2cmac);
}

void
cmac_host_req_sleep_update(void)
{
uint16_t lpclk_freq;
uint32_t wakeup_lpclk_ticks;

if (!shm_synced()) {
return;
}

lpclk_freq = da1469x_lpclk_freq_get();
wakeup_lpclk_ticks = da1469x_sleep_wakeup_ticks_get();

if ((g_cmac_shm_ctrl->lp_clock_freq == lpclk_freq) &&
(g_cmac_shm_ctrl->wakeup_lpclk_ticks == wakeup_lpclk_ticks)) {
return;
}

cmac_shm_lock();
g_cmac_shm_ctrl->lp_clock_freq = lpclk_freq;
g_cmac_shm_ctrl->wakeup_lpclk_ticks = wakeup_lpclk_ticks;
g_cmac_shm_ctrl->pending_ops |= CMAC_SHM_CB_PENDING_OP_SLEEP_UPDATE;
cmac_shm_unlock();

cmac_host_signal2cmac();
}

void
cmac_host_rf_calibrate(void)
{
if (!shm_synced()) {
return;
}

cmac_shm_lock();
g_cmac_shm_ctrl->pending_ops |= CMAC_SHM_CB_PENDING_OP_RF_CAL;
cmac_shm_unlock();
Expand Down
2 changes: 1 addition & 1 deletion hw/mcu/dialog/cmac/include/mcu/cmac_timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ extern struct cmac_timer_ctrl g_cmac_timer_ctrl;
void cmac_timer_init(void);
void cmac_timer_slp_enable(uint32_t ticks);
void cmac_timer_slp_disable(uint32_t exp_ticks);
bool cmac_timer_slp_update(void);
void cmac_timer_slp_update(uint16_t lp_clock_freq);
bool cmac_timer_slp_is_ready(void);
#if MYNEWT_VAL(MCU_SLP_TIMER_32K_ONLY)
static inline uint32_t
Expand Down
7 changes: 5 additions & 2 deletions hw/mcu/dialog/cmac/src/cmac_isr.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
* under the License.
*/

#include <syscfg/syscfg.h>
#include <CMAC.h>
#include <mcu/cmac_pdc.h>
#include <mcu/cmac_timer.h>
#include <ipc_cmac/shm.h>
#include <ipc_cmac/mbox.h>
#include <ipc_cmac/rand.h>
Expand All @@ -40,8 +42,9 @@ SYS2CMAC_IRQHandler(void)
cmac_mbox_read();
cmac_rand_read();

if (pending_ops & CMAC_SHM_CB_PENDING_OP_LP_CLK) {
cmac_sleep_recalculate();
if (pending_ops & CMAC_SHM_CB_PENDING_OP_SLEEP_UPDATE) {
cmac_timer_slp_update(g_cmac_shm_ctrl.lp_clock_freq);
cmac_sleep_wakeup_time_update(g_cmac_shm_ctrl.wakeup_lpclk_ticks);
}

if (pending_ops & CMAC_SHM_CB_PENDING_OP_RF_CAL) {
Expand Down
2 changes: 1 addition & 1 deletion hw/mcu/dialog/cmac/src/cmac_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ extern "C" {
extern int8_t g_cmac_pdc_cmac2sys;

void cmac_sleep(void);
void cmac_sleep_recalculate(void);
void cmac_sleep_wakeup_time_update(uint16_t wakeup_lpclk_ticks);

#ifdef __cplusplus
}
Expand Down
68 changes: 27 additions & 41 deletions hw/mcu/dialog/cmac/src/cmac_sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ static uint32_t g_retained_regs_val[ ARRAY_SIZE(retained_regs) ];
static uint32_t g_mcu_wait_for_swd_start;

/* Minimum time required to go to sleep (until switch to SLP) and then wake up */
static uint32_t g_mcu_wakeup_usecs_min;
static uint32_t g_mcu_sleep_lp_ticks_min;

static bool
static inline bool
cmac_sleep_is_switch_allowed(void)
{
return (ble_phy_xcvr_state_get() == 0) &&
Expand Down Expand Up @@ -168,44 +168,34 @@ cmac_sleep_wait4xtal(void)
*(volatile uint32_t *)0x5000001c = 1;
}

#define T_USEC(_t) (_t)
#define T_LPTICK(_t) ((_t) * cmac_timer_slp_tick_us())
#define T_LPTICK_U(_t) (T_LPTICK(_t) * 15 / 10)
#define T_USEC(_t) (((_t) + cmac_timer_slp_tick_us() - 1) / \
cmac_timer_slp_tick_us())
#define T_LPTICK(_t) (_t)

static void
cmac_sleep_calculate_wakeup_time(void)
void
cmac_sleep_wakeup_time_update(uint16_t wakeup_lpclk_ticks)
{
assert(g_cmac_shm_ctrl.xtal32m_settle_us);
if (wakeup_lpclk_ticks == 0) {
g_mcu_sleep_lp_ticks_min = 0;
return;
}

g_mcu_wakeup_usecs_min =
g_mcu_sleep_lp_ticks_min =
/*
* We need ~12us to prepare for sleep before starting switch to SLP.
* We need ~15us to prepare for sleep before starting switch to SLP.
* Switch to SLP is done by switching SLP clock to LPCLK first and then
* enabling SLP. The former has to be synchronized with negative edge of
* LPCLK and the latter happens on positive edge of LPCLK so we just
* assume 2 LPCLK ticks in worst case.
*/
T_USEC(12) + T_LPTICK(2) +
T_USEC(15) + T_LPTICK(2) +
/*
* On wake up we assume fast wake up mode which has 3 phases that take
* up to 2, 2 and 3 LPCLK ticks respectively (need to add some margin
* here for worst-worst case). XTAL32M is started at 3rd phase and we
* need to wait for it to settle before switch back to LLT. This is done
* by disabling SLP and then switching SLP clock to PCLK. Both actions
* are synchronized with LPCLK negative edge so take 2 LPCLK ticks in
* worst case. Finally, LLP compensation takes around 50us.
* After wakeup (this includes XTAL32M settling) we need to switch back
* to LLT. This is done by disabling SLP and then switching SLP clock to
* PCLK. Both actions are synchronized with LPCLK negative edge so take
* 2 LPCLK ticks in worst case. Finally, LLT compensation takes ~50us.
*/
T_LPTICK_U(2) + T_LPTICK_U(2) +
max(T_LPTICK_U(3), T_USEC(g_cmac_shm_ctrl.xtal32m_settle_us)) +
T_LPTICK(2) + T_USEC(50);
}

void
cmac_sleep_recalculate(void)
{
if (cmac_timer_slp_update()) {
cmac_sleep_calculate_wakeup_time();
}
T_LPTICK(wakeup_lpclk_ticks) + T_LPTICK(2) + T_USEC(50);
}

extern bool ble_rf_try_recalibrate(uint32_t idle_time_us);
Expand All @@ -228,25 +218,21 @@ cmac_sleep(void)
cmac_pdc_ack_all();

wakeup_at = cmac_timer_next_at();
sleep_usecs = wakeup_at - cmac_timer_read32();

/*
* At this point in time we know exactly when next LLT interrupt should
* happen so need to make sure we can be up and running on time.
*/
if (ble_rf_try_recalibrate(sleep_usecs)) {
goto skip_sleep;
}

sleep_usecs = wakeup_at - cmac_timer_read32() - g_mcu_wakeup_usecs_min;
if ((int32_t)sleep_usecs <= 0) {
if (g_mcu_sleep_lp_ticks_min == 0) {
switch_to_slp = false;
deep_sleep = false;
goto do_sleep;
}

if (ble_rf_try_recalibrate(sleep_usecs)) {
goto skip_sleep;
}

sleep_lp_ticks = cmac_timer_usecs_to_lp_ticks(sleep_usecs);
if (sleep_lp_ticks <= 1) {
sleep_lp_ticks = cmac_timer_usecs_to_lp_ticks(sleep_usecs) -
g_mcu_sleep_lp_ticks_min;
if ((int32_t)sleep_lp_ticks <= 1) {
switch_to_slp = false;
deep_sleep = false;
goto do_sleep;
Expand Down
14 changes: 4 additions & 10 deletions hw/mcu/dialog/cmac/src/cmac_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -334,15 +334,11 @@ cmac_timer_slp_disable(uint32_t exp_ticks)
assert(CMAC->CM_LL_INT_STAT_REG == 0);
}

bool
cmac_timer_slp_update(void)
void
cmac_timer_slp_update(uint16_t lp_clock_freq)
{
uint32_t lp_clock_freq;

lp_clock_freq = g_cmac_shm_ctrl.lp_clock_freq;

if (lp_clock_freq == g_cmac_timer_slp.freq) {
return false;
return;
}

g_cmac_timer_slp.freq = lp_clock_freq;
Expand All @@ -353,8 +349,6 @@ cmac_timer_slp_update(void)
g_cmac_timer_slp.tick_ns = 1000000000 / g_cmac_timer_slp.freq;
}
#endif

return true;
}

bool
Expand All @@ -363,7 +357,7 @@ cmac_timer_slp_is_ready(void)
#if MYNEWT_VAL(MCU_SLP_TIMER_32K_ONLY)
return g_cmac_timer_slp.freq == 32768;
#else
return g_cmac_timer_slp.freq;
return g_cmac_timer_slp.freq != 0;
#endif
}

Expand Down
3 changes: 1 addition & 2 deletions hw/mcu/dialog/da1469x/include/mcu/da1469x_lpclk.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@
extern "C" {
#endif

typedef void (da1469x_lpclk_cb)(uint32_t freq);
uint16_t da1469x_lpclk_freq_get(void);

void da1469x_lpclk_register_cmac_cb(da1469x_lpclk_cb *cb);
/* Stable lp clock enabled (e.g. switched to XTAL after settling) */
void da1469x_lpclk_enabled(void);
/* Frequency of lp clock changed (e.g. after RCX recalibration) */
Expand Down
1 change: 1 addition & 0 deletions hw/mcu/dialog/da1469x/include/mcu/da1469x_sleep.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct da1469x_sleep_cb {
};

void da1469x_sleep_cb_register(struct da1469x_sleep_cb *cb);
uint32_t da1469x_sleep_wakeup_ticks_get(void);

#ifdef __cplusplus
}
Expand Down
11 changes: 7 additions & 4 deletions hw/mcu/dialog/da1469x/src/da1469x_clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,14 @@ da1469x_clock_sys_xtal32m_init(void)
uint32_t reg;
int xtalrdy_cnt;

/*
* Number of 256kHz clock cycles (~4.085us) assuming worst case when actual frequency is 244800.
* RC32M is in range <30.6, 32.6> so 256Khz can ba as low as 30.6MHz / 125 = 244.8kHz.
/* Number of 256kHz clock cycles for XTAL32M to settle.
* To make sure we wait no less than configured settle time, we need to
* calculate using the shortest possible clock cycle, i.e. the max possible
* frequency of RC32M (32.6 MHz).
*
* Max frequency of 256kHz clock: 32.6 MHz / 125 = 260.8 kHz -> 3.834 us
*/
xtalrdy_cnt = MYNEWT_VAL(MCU_CLOCK_XTAL32M_SETTLE_TIME_US) * 1000 / 4085;
xtalrdy_cnt = MYNEWT_VAL(MCU_CLOCK_XTAL32M_SETTLE_TIME_US) * 1000 / 3834;

reg = CRG_XTAL->XTALRDY_CTRL_REG;
reg &= ~(CRG_XTAL_XTALRDY_CTRL_REG_XTALRDY_CNT_Msk);
Expand Down
Loading