Skip to content

Commit

Permalink
hw/mcu/dialog: Rework CMAC sleep calculations
Browse files Browse the repository at this point in the history
Sleep time calculation on CMAC assumed incorrectly that wakeup fsm uses
lpclk but in fact RC32K is used so we need to take it into account. To
make things easier to handle on CMAC side we will calculate wakeup time
on M33 side and pass it to CMAC instead of XTAL32M settle time. This way
we don't really need to care about how wakeup fsm is configured.

It's now expected that M33 will take care of RC32K calibration and will
calculate proper wakeup time based on wakeup fsm settings and XTAL32M
settle time, and then notify CMAC on any change so we can recalculate
our min sleep time.
  • Loading branch information
andrzej-kaczmarek committed Aug 1, 2023
1 parent 1e32211 commit 01fe8ae
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 70 deletions.
9 changes: 5 additions & 4 deletions hw/drivers/ipc_cmac/include/ipc_cmac/shm.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ extern "C" {

#define CMAC_SHM_CB_MAGIC 0xc3ac

#define CMAC_SHM_CB_PENDING_OP_LP_CLK 0x0001
#define CMAC_SHM_CB_PENDING_OP_RF_CAL 0x0002
#define CMAC_SHM_CB_PENDING_OP_LPCLK_UPDATE 0x0001
#define CMAC_SHM_CB_PENDING_OP_WAKEUP_UPDATE 0x0002
#define CMAC_SHM_CB_PENDING_OP_RF_CAL 0x0004

#define CMAC_SHM_VECT_MAGIC 0xc3ac0001
#define CMAC_SHM_VECT_CRASHINFO 0x00000001
Expand All @@ -58,8 +59,8 @@ struct cmac_shm_config {
struct cmac_shm_ctrl {
uint16_t magic;
uint16_t pending_ops;
uint16_t lp_clock_freq;
uint16_t xtal32m_settle_us;
uint16_t lpclk_freq;
uint16_t wakeup_time_us;
};

struct cmac_shm_mbox {
Expand Down
3 changes: 3 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,9 @@ extern volatile struct cmac_shm_debugdata *g_cmac_shm_debugdata;

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

void cmac_host_lpclk_update(uint32_t freq);
void cmac_host_wakeup_update(uint32_t wakeup_time_us);
void cmac_host_rf_calibrate(void);

#ifdef __cplusplus
Expand Down
46 changes: 33 additions & 13 deletions hw/drivers/ipc_cmac/src/shm_hs.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,17 +199,7 @@ cmac_host_rand_chk_fill(void)
static void
cmac_host_lpclk_cb(uint32_t freq)
{
/* 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();
cmac_host_lpclk_update(freq);
}

static void
Expand All @@ -234,8 +224,7 @@ 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->wakeup_time_us = 0;

trim = (struct cmac_shm_trim *)g_cmac_shm_trim;
trim_data = trim->data;
Expand Down Expand Up @@ -444,6 +433,7 @@ cmac_host_init(void)
cmac_start();

da1469x_lpclk_register_cmac_cb(cmac_host_lpclk_cb);
cmac_host_wakeup_update(da1469x_sleep_wakeup_time_us_get());

#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,6 +452,36 @@ cmac_host_signal2cmac(void)
da1469x_pdc_set(g_cmac_host_pdc_sys2cmac);
}

void
cmac_host_lpclk_update(uint32_t freq)
{
if (g_cmac_shm_ctrl->lpclk_freq == freq) {
return;
}

cmac_shm_lock();
g_cmac_shm_ctrl->lpclk_freq = freq;
g_cmac_shm_ctrl->pending_ops |= CMAC_SHM_CB_PENDING_OP_LPCLK_UPDATE;
cmac_shm_unlock();

cmac_host_signal2cmac();
}

void
cmac_host_wakeup_update(uint32_t wakeup_time_us)
{
if (g_cmac_shm_ctrl->wakeup_time_us == wakeup_time_us) {
return;
}

cmac_shm_lock();
g_cmac_shm_ctrl->wakeup_time_us = wakeup_time_us;
g_cmac_shm_ctrl->pending_ops |= CMAC_SHM_CB_PENDING_OP_WAKEUP_UPDATE;
cmac_shm_unlock();

cmac_host_signal2cmac();
}

void
cmac_host_rf_calibrate(void)
{
Expand Down
3 changes: 2 additions & 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,8 @@ 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 lpclk_freq);
bool cmac_timer_slp_is_ready(void);
#if MYNEWT_VAL(MCU_SLP_TIMER_32K_ONLY)
static inline uint32_t
Expand Down
11 changes: 9 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,13 @@ 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_LPCLK_UPDATE) {
cmac_timer_slp_update(g_cmac_shm_ctrl.lpclk_freq);
cmac_sleep_wakeup_time_update(g_cmac_shm_ctrl.wakeup_time_us);
}

if (pending_ops & CMAC_SHM_CB_PENDING_OP_WAKEUP_UPDATE) {
cmac_sleep_wakeup_time_update(g_cmac_shm_ctrl.wakeup_time_us);
}

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_time_us);

#ifdef __cplusplus
}
Expand Down
58 changes: 26 additions & 32 deletions hw/mcu/dialog/cmac/src/cmac_sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ 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_time_us_min;

static bool
cmac_sleep_is_switch_allowed(void)
static inline bool
cmac_sleep_is_slp_allowed(void)
{
return (ble_phy_xcvr_state_get() == 0) &&
!os_arch_cmac_pending_irq() &&
Expand All @@ -98,6 +98,10 @@ sub27(uint32_t x, uint32_t y)
static bool
cmac_sleep_is_deep_sleep_allowed(void)
{
if (g_mcu_sleep_time_us_min == 0) {
return false;
}

/*
* We wait for SWD attach until high part of LL Timer increases by 2 which
* is anywhere in 1-2ms range which is enough.
Expand Down Expand Up @@ -170,42 +174,31 @@ cmac_sleep_wait4xtal(void)

#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)

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

g_mcu_wakeup_usecs_min =
g_mcu_sleep_time_us_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_USEC(wakeup_time_us) + T_LPTICK(2) + T_USEC(50);
}

extern bool ble_rf_try_recalibrate(uint32_t idle_time_us);
Expand Down Expand Up @@ -234,7 +227,7 @@ cmac_sleep(void)
* happen so need to make sure we can be up and running on time.
*/

sleep_usecs = wakeup_at - cmac_timer_read32() - g_mcu_wakeup_usecs_min;
sleep_usecs = wakeup_at - cmac_timer_read32() - g_mcu_sleep_time_us_min;
if ((int32_t)sleep_usecs <= 0) {
switch_to_slp = false;
deep_sleep = false;
Expand All @@ -245,14 +238,15 @@ cmac_sleep(void)
goto skip_sleep;
}

sleep_lp_ticks = cmac_timer_usecs_to_lp_ticks(sleep_usecs);
if (sleep_lp_ticks <= 1) {
/* XXX off by one? */
sleep_lp_ticks = cmac_timer_usecs_to_lp_ticks(sleep_usecs) - 1;
if ((int32_t)sleep_lp_ticks <= 1) {
switch_to_slp = false;
deep_sleep = false;
goto do_sleep;
}

if (!cmac_sleep_is_switch_allowed()) {
if (!cmac_sleep_is_slp_allowed()) {
switch_to_slp = false;
deep_sleep = false;
} else if (!cmac_sleep_is_deep_sleep_allowed()) {
Expand Down
28 changes: 11 additions & 17 deletions hw/mcu/dialog/cmac/src/cmac_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
(CMAC_CM_EV_LATCHED_REG_EV1C_CLK_1US_X1_Msk << ((_num) - 1)))

struct cmac_timer_slp {
uint32_t freq;
uint16_t lpclk_freq;
#if !MYNEWT_VAL(MCU_SLP_TIMER_32K_ONLY)
uint32_t conv;
uint32_t tick_ns;
Expand Down Expand Up @@ -334,36 +334,30 @@ 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 lpclk_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;
if (lpclk_freq == g_cmac_timer_slp.lpclk_freq) {
return;
}

g_cmac_timer_slp.freq = lp_clock_freq;
g_cmac_timer_slp.lpclk_freq = lpclk_freq;

#if !MYNEWT_VAL(MCU_SLP_TIMER_32K_ONLY)
if (g_cmac_timer_slp.freq) {
g_cmac_timer_slp.conv = g_cmac_timer_slp.freq * 32768 / 1000000;
g_cmac_timer_slp.tick_ns = 1000000000 / g_cmac_timer_slp.freq;
if (g_cmac_timer_slp.lpclk_freq) {
g_cmac_timer_slp.conv = g_cmac_timer_slp.lpclk_freq * 32768 / 1000000;
g_cmac_timer_slp.tick_ns = 1000000000 / g_cmac_timer_slp.lpclk_freq;
}
#endif

return true;
}

bool
cmac_timer_slp_is_ready(void)
{
#if MYNEWT_VAL(MCU_SLP_TIMER_32K_ONLY)
return g_cmac_timer_slp.freq == 32768;
return g_cmac_timer_slp.lpclk_freq == 32768;
#else
return g_cmac_timer_slp.freq;
return g_cmac_timer_slp.lpclk_freq != 0;
#endif
}

Expand Down

0 comments on commit 01fe8ae

Please sign in to comment.