summaryrefslogtreecommitdiff
path: root/bsp/nxp/imx/imxrt/libraries/drivers/drv_dwt.c
blob: 91bd5c2ecb9b532967f14d78fe1cc7f1a44df008 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*
 * Copyright (c) 2006-2024, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-01-01     RTT          the first version, generic i.MX RT DWT driver
 */

#include "drv_dwt.h"

#ifdef BSP_USING_DWT

#include <rtthread.h>

/* CMSIS standard variable updated by SystemCoreClockUpdate() in the SDK's
 * system_<device>.c. Declared extern here to stay device-family-agnostic. */
extern uint32_t SystemCoreClock;

/* CoreDebug and DWT register base addresses (Cortex-M, ARMv7-M / ARMv8-M) */
#define DWT_CTRL        (*(volatile rt_uint32_t *)0xE0001000UL)  /* DWT Control */
#define DWT_CYCCNT      (*(volatile rt_uint32_t *)0xE0001004UL)  /* Cycle Count */
#define CoreDebug_DEMCR (*(volatile rt_uint32_t *)0xE000EDFCUL) /* DEMCR */

/* DEMCR: enable DWT and ITM trace */
#define CoreDebug_DEMCR_TRCENA_Msk (1UL << 24)
/* DWT_CTRL: enable cycle counter */
#define DWT_CTRL_CYCCNTENA_Msk (1UL << 0)
/* DWT_CTRL: NOCYCCNT - set if the cycle counter is not implemented */
#define DWT_CTRL_NOCYCCNT_Msk (1UL << 25)

/**
 * Initialize the DWT cycle counter.
 */
rt_err_t dwt_init(void)
{
    /* Enable DWT trace by setting TRCENA in DEMCR */
    CoreDebug_DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;

    /* Check that the cycle counter is implemented on this device */
    if (DWT_CTRL & DWT_CTRL_NOCYCCNT_Msk)
    {
        return -RT_ERROR;
    }

    /* Reset then enable the cycle counter */
    DWT_CYCCNT = 0U;
    DWT_CTRL |= DWT_CTRL_CYCCNTENA_Msk;

    return RT_EOK;
}

/**
 * Read the current DWT cycle counter value.
 */
rt_uint32_t dwt_get_cycles(void)
{
    return DWT_CYCCNT;
}

/**
 * Convert a cycle count to microseconds using SystemCoreClock.
 */
rt_uint32_t dwt_cycles_to_us(rt_uint32_t cycles)
{
    return (rt_uint32_t)((rt_uint64_t)cycles * 1000000UL / SystemCoreClock);
}

/**
 * Busy-wait for the requested number of microseconds.
 */
void dwt_delay_us(rt_uint32_t us)
{
    rt_uint32_t start = DWT_CYCCNT;
    rt_uint32_t ticks = (rt_uint32_t)((rt_uint64_t)us * SystemCoreClock / 1000000UL);

    /* Unsigned subtraction handles counter wrap-around correctly */
    while ((DWT_CYCCNT - start) < ticks)
    {
        /* busy wait */
    }
}

#ifdef RT_USING_FINSH
#include <finsh.h>

/**
 * MSH command: dwt_test
 * Tests the DWT cycle counter by measuring rt_thread_mdelay(100) and
 * a 200 us busy-wait delay, then prints the results over the console.
 */
static void dwt_test(int argc, char **argv)
{
    rt_uint32_t t0, t1, elapsed_us;

    if (dwt_init() != RT_EOK)
    {
        rt_kprintf("[DWT] init FAILED: cycle counter not implemented on this device\r\n");
        return;
    }
    rt_kprintf("[DWT] init OK, cycle counter started\r\n");

    /* test 1: measure a 100 ms OS delay */
    t0 = dwt_get_cycles();
    rt_thread_mdelay(100);
    t1 = dwt_get_cycles();
    elapsed_us = dwt_cycles_to_us(t1 - t0);
    rt_kprintf("[DWT] rt_thread_mdelay(100) took %u us\r\n", elapsed_us);

    /* test 2: busy-wait 200 us and measure it back */
    t0 = dwt_get_cycles();
    dwt_delay_us(200);
    t1 = dwt_get_cycles();
    elapsed_us = dwt_cycles_to_us(t1 - t0);
    rt_kprintf("[DWT] dwt_delay_us(200) measured %u us\r\n", elapsed_us);
}
MSH_CMD_EXPORT(dwt_test, test DWT cycle counter and us delay);

#endif /* RT_USING_FINSH */

#endif /* BSP_USING_DWT */