Skip to content

Commit

Permalink
Backport ARMmbed#12019: rework cypress lptimer hal
Browse files Browse the repository at this point in the history
Changed set_match api to use an absolute ticks rather than delayed tick to match api name.
Added api set_delay to delay by a specific amount of ticks. Removed unused set_time api.
Simplified the logic for computing interrupts match value for cascading counters.
Fixed an issue when incorrect base time would be read when trying to set match values.
  • Loading branch information
ShuopengDeng authored and Kyle Kearney committed Apr 2, 2020
1 parent 978db96 commit 880af5d
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 112 deletions.
7 changes: 2 additions & 5 deletions targets/TARGET_Cypress/TARGET_PSOC6/cy_lp_ticker_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,8 @@ uint32_t lp_ticker_read(void)

void lp_ticker_set_interrupt(timestamp_t timestamp)
{
uint32_t delay;
delay = (uint32_t)timestamp - cyhal_lptimer_read(&cy_lptimer0);

if (CY_RSLT_SUCCESS != cyhal_lptimer_set_match(&cy_lptimer0, delay)) {
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_lptimer_set_time");
if (CY_RSLT_SUCCESS != cyhal_lptimer_set_match(&cy_lptimer0, (uint32_t)timestamp)) {
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_lptimer_set_match");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
* \ingroup group_hal
* \{
* High level interface for interacting with the Cypress LPTIMER.
*
* This can be used to measure timing between events, or to perform
* some action the ability after a set interval. It continues to operate
* in some low power modes; see the device datasheet for details.
*/

#pragma once
Expand All @@ -43,6 +47,10 @@
extern "C" {
#endif


/** Failed to configure power management callback */
#define CYHAL_LPTIMER_RSLT_ERR_PM_CALLBACK (CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_WDT, 0))

/** LPTIMER interrupt triggers */
typedef enum {
CYHAL_LPTIMER_COMPARE_MATCH,
Expand Down Expand Up @@ -81,32 +89,37 @@ void cyhal_lptimer_free(cyhal_lptimer_t *obj);
*/
cy_rslt_t cyhal_lptimer_reload(cyhal_lptimer_t *obj);

/** Set timeframe between interrupts
/** Deprecated. Call cyhal_lptimer_set_match instead. */
#define cyhal_lptimer_set_time cyhal_lptimer_set_match

/** Update the match/compare value
*
* Configures the LPTIMER in free-running mode. Generates an interrupt on match.
* This function is for initial configuration. For quick updates to the match
* value, use cyhal_lptimer_set_time().
* Update the match value of an already configured LPTIMER set up
* to generate an interrupt on match. Note that this function does not
* reinitialize the counter or the associated peripheral initialization
* sequence.
*
* @param[in] obj The LPTIMER object
* @param[in] time The time in ticks to be set
* @param[in] value The tick value to match
*
* @return The status of the set_time request
* @return The status of the set_match request
*/
cy_rslt_t cyhal_lptimer_set_time(cyhal_lptimer_t *obj, uint32_t time);
cy_rslt_t cyhal_lptimer_set_match(cyhal_lptimer_t *obj, uint32_t value);

/** Update the match/compare value
*
* Update the match value of an already configured LPTIMER set up
* to generate an interrupt on match. Note that this function does not
* reinitialize the counter or the associated peripheral initialization
* to generate an interrupt on match delay from the current counter value.
* Note that this function does not reinitialize the counter or the
* associated peripheral initialization
* sequence.
*
* @param[in] obj The LPTIMER object
* @param[in] value The match value in ticks
* @param[in] delay The ticks to wait
*
* @return The status of the set_match request
*/
cy_rslt_t cyhal_lptimer_set_match(cyhal_lptimer_t *obj, uint32_t value);
cy_rslt_t cyhal_lptimer_set_delay(cyhal_lptimer_t *obj, uint32_t delay);

/** Read the current tick
*
Expand Down
170 changes: 74 additions & 96 deletions targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/src/cyhal_lptimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
*******************************************************************************/

#include "cmsis_compiler.h"
#include "cy_wdt.h"
#include "cy_mcwdt.h"
#include "cy_syslib.h"
#include "cy_sysint.h"
#include "cyhal_lptimer.h"
Expand All @@ -52,16 +52,11 @@ static MCWDT_STRUCT_Type * const CYHAL_LPTIMER_BASE_ADDRESSES[] = {
#endif
};

#if !defined (CY_CFG_SYSCLK_CLKLF_FREQ_HZ)
#define CY_CFG_SYSCLK_CLKLF_FREQ_HZ 32768UL /* Default to 32K ILO */
#endif /* CY_CFG_SYSCLK_CLKLF_FREQ_HZ */

#define CY_MCWDT_COUNTER0_MAX_TICKS (0xffffUL)
#define CY_MCWDT_COUNTER1_MAX_TICKS (0xffffUL)
#define CY_MCWDT_COUNTER2_MAX_TICKS (0xffffffffUL)
#define CY_MCWDT_MAX_DELAY_TICKS (0xfff0ffffUL) /* ~36hours, Not set to 0xffffffff to avoid C0 and C1 both overflowing */
#define CY_MCWDT_LPTIMER_CTRL (CY_MCWDT_CTR0 | CY_MCWDT_CTR1 | CY_MCWDT_CTR2)

#define CY_MCWDT_MIN_DELAY 3 /* minimum amount of lfclk cycles of that LPTIMER can delay for. */

#define CY_DEFAULT_MCWDT_PRIORITY 3

static const uint16_t CY_MCWDT_RESET_TIME_US = 62;
Expand Down Expand Up @@ -95,8 +90,8 @@ cy_rslt_t cyhal_lptimer_init(cyhal_lptimer_t *obj)
obj->base = CYHAL_LPTIMER_BASE_ADDRESSES[obj->resource.block_num];

const cy_stc_mcwdt_config_t cfg = {
.c0Match = CY_MCWDT_COUNTER0_MAX_TICKS,
.c1Match = CY_MCWDT_COUNTER1_MAX_TICKS,
.c0Match = 0xFFFF,
.c1Match = 0xFFFF,
.c0Mode = CY_MCWDT_MODE_INT,
.c1Mode = CY_MCWDT_MODE_INT,
.c2Mode = CY_MCWDT_MODE_NONE,
Expand All @@ -107,25 +102,28 @@ cy_rslt_t cyhal_lptimer_init(cyhal_lptimer_t *obj)
.c1c2Cascade = false
};
rslt = (cy_rslt_t) Cy_MCWDT_Init(obj->base, &cfg);
}

if (CY_RSLT_SUCCESS == rslt)
{
obj->callback_data.callback = NULL;
obj->callback_data.callback_arg = NULL;
cyhal_lptimer_config_structs[obj->resource.block_num] = obj;
}

if (CY_RSLT_SUCCESS == rslt)
{
IRQn_Type irqn = (IRQn_Type) (srss_interrupt_mcwdt_0_IRQn + obj->resource.block_num);
cy_stc_sysint_t irqCfg = { irqn, CY_DEFAULT_MCWDT_PRIORITY };
rslt = (cy_rslt_t) Cy_SysInt_Init(&irqCfg, &cyhal_lptimer_irq_handler);
if (CY_RSLT_SUCCESS == rslt)
{
obj->callback_data.callback = NULL;
obj->callback_data.callback_arg = NULL;
cyhal_lptimer_config_structs[obj->resource.block_num] = obj;

IRQn_Type irqn = (IRQn_Type) (srss_interrupt_mcwdt_0_IRQn + obj->resource.block_num);
cy_stc_sysint_t irqCfg = { irqn, CY_DEFAULT_MCWDT_PRIORITY };
rslt = (cy_rslt_t) Cy_SysInt_Init(&irqCfg, &cyhal_lptimer_irq_handler);

if (CY_RSLT_SUCCESS == rslt)
{
NVIC_EnableIRQ(irqn);
Cy_MCWDT_Enable(obj->base, CY_MCWDT_LPTIMER_CTRL, CY_MCWDT_RESET_TIME_US);
}
NVIC_EnableIRQ(irqn);
Cy_MCWDT_Enable(obj->base, CY_MCWDT_LPTIMER_CTRL, CY_MCWDT_RESET_TIME_US);
}
}


if (CY_RSLT_SUCCESS != rslt)
{
cyhal_lptimer_free(obj);
Expand Down Expand Up @@ -154,96 +152,74 @@ void cyhal_lptimer_free(cyhal_lptimer_t *obj)

cy_rslt_t cyhal_lptimer_reload(cyhal_lptimer_t *obj)
{
Cy_MCWDT_ResetCounters(obj->base, (CY_MCWDT_CTR0 | CY_MCWDT_CTR1), CY_MCWDT_RESET_TIME_US);
Cy_MCWDT_ResetCounters(obj->base, CY_MCWDT_CTR2, CY_MCWDT_RESET_TIME_US);
return CY_RSLT_SUCCESS;
}

cy_rslt_t cyhal_lptimer_set_time(cyhal_lptimer_t *obj, uint32_t ticks)
cy_rslt_t cyhal_lptimer_set_match(cyhal_lptimer_t *obj, uint32_t ticks)
{
return cyhal_lptimer_set_match(obj, ticks);
return cyhal_lptimer_set_delay(obj, ticks - cyhal_lptimer_read(obj));
}

cy_rslt_t cyhal_lptimer_set_match(cyhal_lptimer_t *obj, uint32_t ticks)
cy_rslt_t cyhal_lptimer_set_delay(cyhal_lptimer_t *obj, uint32_t delay)
{
uint16_t c0_match_ticks;
uint16_t c1_match_ticks;
uint32_t mcwdt_interrupt_mask;
uint16_t c0_current_ticks = Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER0);
uint16_t c1_current_ticks = Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER1);

Cy_MCWDT_ClearInterrupt(obj->base, (CY_MCWDT_CTR0 | CY_MCWDT_CTR1));

/* Use MCWDT C0,C1 and C2 to implement a 32bit free running counter
C2 alone can not be used as it does not support interrupt on match feature
C2 is used to keep track of time, while C0 and C1 are used to set interrupts
To set an interrupt:
1. delay = diff between timestamp(time in future) vs current value of C2
2. if delay > 2seconds (Max time that can be counted by C0)
Yes
- use both C0 and C1
- Increment C0 by delay % (CY_MCWDT_COUNTER0_MAX_TICKS + 1)
- Increment C1 by delay / (CY_MCWDT_COUNTER1_MAX_TICKS + 1)
- Special case : In case delay is multiple of (CY_MCWDT_COUNTER0_MAX_TICKS + 1), then
delay % (CY_MCWDT_COUNTER0_MAX_TICKS + 1) will be 0, in this case
- Increment C0 by c0_current_ticks -1
- Increment C1 by (delay / (CY_MCWDT_COUNTER1_MAX_TICKS + 1)) -1
No
- Use only C0
*/
if (ticks > CY_MCWDT_COUNTER0_MAX_TICKS)
/**
* 16 bit C0/C1 are cascaded to generated a 32 bit counter.
* Counter0 continues counting after reaching its match value
* Interrupt is generated on Counter1 match.
*
* Supposed T=C0=C1=0, and we need to trigger an interrupt at T=0x28000.
* We set C0_match to 0x8000 and C1 match to 1.
* At T = 0x8000, C0_value matches C0_match so C1 get incremented. C1/C0=0x18000.
* At T = 0x18000, C0_value matches C0_match again so C1 get incremented from 1 to 2.
* When C1 get incremented from 1 to 2 theinterrupt is generated.
* At T = 0x18000, C1/C0 = 0x28000.
*/

if (delay <= CY_MCWDT_MIN_DELAY)
{
uint16_t c0_increment;
uint16_t c1_increment;

if (ticks > CY_MCWDT_MAX_DELAY_TICKS)
{
ticks = CY_MCWDT_MAX_DELAY_TICKS;
}

c0_increment = ticks % (CY_MCWDT_COUNTER0_MAX_TICKS + 1);
c0_match_ticks = (c0_current_ticks + c0_increment) % (CY_MCWDT_COUNTER0_MAX_TICKS + 1);
c1_increment = (ticks) / (CY_MCWDT_COUNTER0_MAX_TICKS + 1);
c1_match_ticks = (c1_current_ticks + c1_increment) % (CY_MCWDT_COUNTER1_MAX_TICKS + 1);

/* Special case - ticks is multiple of (CY_MCWDT_COUNTER0_MAX_TICKS + 1) */
if (c0_increment == 0)
{
c0_match_ticks = c0_current_ticks - 1;
c1_match_ticks = c1_match_ticks -1;
}

mcwdt_interrupt_mask = CY_MCWDT_CTR1;
delay = CY_MCWDT_MIN_DELAY;
}
else
if (delay > CY_MCWDT_MAX_DELAY_TICKS)
{
c0_match_ticks = c0_current_ticks + (uint16_t)ticks;
c1_match_ticks = CY_MCWDT_COUNTER1_MAX_TICKS;
delay = CY_MCWDT_MAX_DELAY_TICKS;
}

/* MCWDT has internal delay of about 1.5 LF clock ticks, so this is the minimum
* that we can schedule.
*/
if (ticks < 3)
{
/* Cheating a bit here. */
c0_match_ticks = c0_current_ticks + 3;
}
uint16_t c0_increment = (uint16_t)delay;
uint16_t c1_increment = (uint16_t)(delay >> 16);

mcwdt_interrupt_mask = CY_MCWDT_CTR0;
}
Cy_MCWDT_ClearInterrupt(obj->base, CY_MCWDT_CTR1);

uint16_t c0_old_match = Cy_MCWDT_GetMatch(obj->base, CY_MCWDT_COUNTER0);

uint32_t critical_section = cyhal_system_critical_section_enter();

if(c1_match_ticks == 0)
/* Cascading from C0 match into C1 is queued and can take 1 full LF clk cycle.
* There are 3 cases:
* Case 1: if c0 = match0 then the cascade into C1 will happen 1 cycle from now. The value c1_current_ticks is 1 lower than expected.
* Case 2: if c0 = match0 -1 then cascade may or not happen before new match value would occur. Match occurs on rising clock edge.
* Synching match value occurs on falling edge. Wait until c0 = match0 to ensure cascade occurs.
* Case 3: everything works as expected.
*/
uint16_t c0_current_ticks;
while ((c0_current_ticks = (Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER0))) == c0_old_match) {}

uint16_t c1_current_ticks = Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER1);
if (c0_current_ticks == c0_old_match + 1)
{
c1_match_ticks = 1;
c1_current_ticks++;
}

if(c0_match_ticks == 0)
if (Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER0) != c0_current_ticks)
{
c0_match_ticks = 1;
// Just in the very unlikely case that an increment occurred while previous instruction was running.
c1_current_ticks = Cy_MCWDT_GetCount(obj->base, CY_MCWDT_COUNTER1);
}
Cy_MCWDT_SetMatch(obj->base, CY_MCWDT_COUNTER0, c0_current_ticks + c0_increment, CY_MCWDT_SETMATCH_NOWAIT_TIME_US);
Cy_MCWDT_SetMatch(obj->base, CY_MCWDT_COUNTER1, c1_current_ticks + c1_increment, CY_MCWDT_SETMATCH_NOWAIT_TIME_US);

cyhal_system_critical_section_exit(critical_section);

Cy_MCWDT_SetMatch(obj->base, CY_MCWDT_COUNTER0, c0_match_ticks, CY_MCWDT_SETMATCH_NOWAIT_TIME_US);
Cy_MCWDT_SetMatch(obj->base, CY_MCWDT_COUNTER1, c1_match_ticks, CY_MCWDT_SETMATCH_NOWAIT_TIME_US);
Cy_MCWDT_SetInterruptMask(obj->base, mcwdt_interrupt_mask);
Cy_MCWDT_SetInterruptMask(obj->base, CY_MCWDT_CTR1);

return CY_RSLT_SUCCESS;
}
Expand All @@ -265,7 +241,9 @@ void cyhal_lptimer_register_callback(cyhal_lptimer_t *obj, cyhal_lptimer_event_c

void cyhal_lptimer_enable_event(cyhal_lptimer_t *obj, cyhal_lptimer_event_t event, uint8_t intrPriority, bool enable)
{
Cy_MCWDT_SetInterruptMask(obj->base, enable ? CY_MCWDT_CTR0 : 0);
CY_ASSERT(event == CYHAL_LPTIMER_COMPARE_MATCH);
Cy_MCWDT_ClearInterrupt(obj->base, CY_MCWDT_CTR1);
Cy_MCWDT_SetInterruptMask(obj->base, enable ? CY_MCWDT_CTR1 : 0);

IRQn_Type irqn = (IRQn_Type)(srss_interrupt_mcwdt_0_IRQn + obj->resource.block_num);
NVIC_SetPriority(irqn, intrPriority);
Expand Down

0 comments on commit 880af5d

Please sign in to comment.