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 9, 2023
1 parent c8596c3 commit 82e5794
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 87 deletions.
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
4 changes: 4 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,10 @@ 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_req_sleep_update_param(uint16_t lpclk_freq,
uint16_t wakeup_lpclk_ticks);
void cmac_host_rf_calibrate(void);

#ifdef __cplusplus
Expand Down
63 changes: 46 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,47 @@ 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;

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

cmac_host_req_sleep_update_param(lpclk_freq, wakeup_lpclk_ticks);
}

void
cmac_host_req_sleep_update_param(uint16_t lpclk_freq,
uint16_t wakeup_lpclk_ticks)
{
if (!shm_synced()) {
return;
}

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
28 changes: 17 additions & 11 deletions hw/mcu/dialog/da1469x/src/da1469x_lpclk.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,10 @@
#include "hal/hal_timer.h"
#include "os/os_cputime.h"
#include "da1469x_priv.h"
#include "ipc_cmac/shm.h"

bool g_mcu_lpclk_available;

static da1469x_lpclk_cb *g_da1469x_lpclk_cmac_cb;

#if MYNEWT_VAL_CHOICE(MCU_LPCLK_SOURCE, XTAL32K)
static void
da1469x_lpclk_settle_tmr_cb(void *arg)
Expand All @@ -45,22 +44,29 @@ da1469x_lpclk_settle_tmr_cb(void *arg)
static void
da1469x_lpclk_notify(void)
{
if (!g_da1469x_lpclk_cmac_cb || !g_mcu_lpclk_available) {
if (!g_mcu_lpclk_available) {
return;
}

#if MYNEWT_VAL_CHOICE(MCU_LPCLK_SOURCE, XTAL32K)
g_da1469x_lpclk_cmac_cb(32768);
#elif MYNEWT_VAL_CHOICE(MCU_LPCLK_SOURCE, RCX)
g_da1469x_lpclk_cmac_cb(da1469x_clock_lp_rcx_freq_get());
#if MYNEWT_PKG_apache_mynewt_core__hw_drivers_ipc_cmac
cmac_host_req_sleep_update();
#endif
}

void
da1469x_lpclk_register_cmac_cb(da1469x_lpclk_cb *cb)
uint16_t
da1469x_lpclk_freq_get(void)
{
g_da1469x_lpclk_cmac_cb = cb;
da1469x_lpclk_notify();
if (!g_mcu_lpclk_available) {
return 0;
}

#if MYNEWT_VAL_CHOICE(MCU_LPCLK_SOURCE, XTAL32K)
return 32768;
#elif MYNEWT_VAL_CHOICE(MCU_LPCLK_SOURCE, RCX)
return da1469x_clock_lp_rcx_freq_get();
#else
return 0;
#endif
}

void
Expand Down

0 comments on commit 82e5794

Please sign in to comment.