Sign in to follow this  
Followers 0
Lyon

Execution time - the easy way

7 posts in this topic

Hi,

Found on web several postings related to some available, but unused hardware on Cortex-Mx processors (unused on some platforms, available on others.

Adapted for TM4C as below:

#include "inc/hw_nvic.h"          /* for definition of NVIC_DBG_INT */ 
#include "inc/hw_memmap.h"        /* for definition of DWT_BASE */
#include "inc/hw_types.h"         /* for definition of HWREG */

#define DWT_O_CYCCNT 0x00000004
static  uint32_t     c_start, c_stop;

// declaration of an initialization function
void  EnableTiming(void);

// definition of that function
/******************************************************************************/
void EnableTiming(void){
   HWREG(NVIC_DBG_INT) |= 0x01000000;      /*enable TRCENA bit in NVIC_DBG_INT*/
   HWREG(DWT_BASE + DWT_O_CYCCNT) = 0;     /* reset the counter */
   HWREG(DWT_BASE) |= 0x01;                /* enable the counter */
   enabled = 1;
}
/******************************************************************************/

 // and then, in the code to be examined write these:
 EnableTiming();
 c_start = HWREG(DWT_BASE + DWT_O_CYCCNT); // at the beginning of the tested code
 // your code follows here
 c_stop = HWREG(DWT_BASE + DWT_O_CYCCNT);  // at the end of the tested code

 // then c_stop - c_start is the execution number of cycles for the code; 
 // just multiply with clock period to find out the execution time of the code.
 // At 80MHz, the 32 bit counter gives you a total time 2^32 x 12.5ns = ~53 seconds!
 
 // Another interesting function is this one 
 /**********************************************************/
 void TimingDelay(unsigned int tick)
 {
   unsigned int start, current;
   start = HWREG(DWT_BASE + DWT_O_CYCCNT);
   do {
	current = HWREG(DWT_BASE + DWT_O_CYCCNT);
   } while((current-start)<tick);
 }
/*********************************************************/

Enjoy!

L

bluehash, pabigot, spirilis and 3 others like this

Share this post


Link to post
Share on other sites

Excellent; thank you. For reference, here's a portable version using CMSIS, checked on Tiva and EFM32 processors. (You will need to include the CMSIS device header, e.g. TIVA.h for TM4C and em_device.h for EFM32. That's hidden in bspacm/core.h below.)

 

For reference, the necessary information is in the ARMv7-M Architecture Reference Manual; you have to register with ARM to download the PDF. See sections C1.6.5 for DEMCR and C1.8 for DWT.

 

/* BSPACM - demonstrate cycle counter interface
 *
 * Written in 2014 by Peter A. Bigot <http://pabigot.github.io/bspacm/>
 *
 * To the extent possible under law, the author(s) have dedicated all
 * copyright and related and neighboring rights to this software to
 * the public domain worldwide. This software is distributed without
 * any warranty.
 *
 * You should have received a copy of the CC0 Public Domain Dedication
 * along with this software. If not, see
 * <http://creativecommons.org/publicdomain/zero/1.0/>.
 */

#include <bspacm/core.h>
#include <stdio.h>

#define BSPACM_CORE_ENABLE_CYCCNT() do {              \
    if (! (DWT_CTRL_NOCYCCNT_Msk & DWT->CTRL)) {      \
      CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; \
      DWT->CYCCNT = 0;                                \
      DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;            \
    }                                                 \
  } while (0)

#define BSPACM_CORE_DISABLE_CYCCNT() do {              \
    if (! (DWT_CTRL_NOCYCCNT_Msk & DWT->CTRL)) {       \
      DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk;            \
      CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk; \
    }                                                  \
  } while (0)

#define BSPACM_CORE_CYCCNT() DWT->CYCCNT

#define BSPACM_CORE_DELAY_CYCLES(cycles_) do {  \
    uint32_t const delta = (cycles_);           \
    uint32_t const cc0 = DWT->CYCCNT;           \
    while ((DWT->CYCCNT - cc0) < delta) {       \
      /* spin */                                \
    }                                           \
  } while (0)

void main ()
{
  uint32_t cc0 = 0;
  BSPACM_CORE_ENABLE_INTERRUPT();

  printf("\n" __DATE__ " " __TIME__ "\n");
  printf("System clock %lu Hz\n", SystemCoreClock);
  if (DWT_CTRL_NOCYCCNT_Msk & DWT->CTRL) {
    printf("DWT does not support cycle counting\n");
    return;
  }
  BSPACM_CORE_ENABLE_CYCCNT();
  do {
    uint32_t cc1 = DWT->CYCCNT;
    uint32_t delta = cc1 - cc0;
    printf("Cycle counter %lu, delta %lu\n", cc1, delta);
    cc0 = cc1;
    BSPACM_CORE_DELAY_CYCLES(8000000U);
  } while (1);
}
Lyon and bluehash like this

Share this post


Link to post
Share on other sites

Nice code dump! Just thought I'd add a few notes:

  • In the first implementation, if you make the functions inline, the compiler can optimize out the function jump, and you can get a slightly more accurate time measurement (pushing/popping registers to go to and from functions generally takes around 12 cycles if I recall)
  • CMSIS is interesting, and I have yet to get a chance to play with it. I think the CMSIS example will be more accurate due to the #define macros (no pushing/popping registers from the stack), but some cycles will be lost due to de-referencing. 

Nice code dumps!

Share this post


Link to post
Share on other sites
  • CMSIS is interesting, and I have yet to get a chance to play with it. I think the CMSIS example will be more accurate due to the #define macros (no pushing/popping registers from the stack), but some cycles will be lost due to de-referencing. 
FWIW, DWT is a compile-time constant pointer, not a variable, so there's no double-dereference (the CYCCNT value is read directly from its register). This is true of all CMSIS pointers to mapped memory.
Mr.Cruz likes this

Share this post


Link to post
Share on other sites

Hi ,

which systemclock should I configure with ADC & timer module ( I am using both module in same code)..??

SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); freq = SysCtlClockGet(); // 80MHz
SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); freq = SysCtlClockGet(); // 50MHz
SysCtlClockSet(SYSCTL_SYSDIV_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ); freq = SysCtlClockGet(); // 40MHz

If I dont write any of above, I get :: freq = SysCtlClockGet(); // 16MHz

My requirement is lowest execution time.
Should I configure to 80MHz..?? 

If I do so will both ADC and Timers will work fine..??
Is any extra configuration needed...?



Regards,
Krishnat

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0