0xStubs

Computer science, IT, Photography

Measuring time in a bare-metal Zynq application

If you want to measure elapsed time in a bare-metal application on the Xilinx Zynq SoC—for example to measure how long your external accelerator takes to get a result—you will soon notice that typical methods do not work. But there is a simple and precise replacement for those methods.

A method that is often recommended is to use gettimeofday() out of sys/time.h to get the current time before and after whatever time interval we want to measure and to take the difference of it. Trying this in a bare-metal application on the Zynq SoC we get the following error:

In function `_gettimeofday_r': gettimeofdayr.c:(.text+0x20): undefined reference to `_gettimeofday'

According to the man page this method is deprecated anyway, so let’s try the recommended replacement: clock_gettime() out of time.h:

undefined reference to `clock_gettime'

So, no luck either… How about just obtaining the clock count using clock() out of time.h?

In function `_times_r': timesr.c:(.text+0x4): undefined reference to `_times'

Luckily, there is a Xilinx specific replacement for the last approach. Here is a short sample program:

#include <stdio.h>
#include "platform.h"
#include "xparameters.h"
#include "xtime_l.h"

void print(char *str);

int main() {
    XTime tStart, tEnd;

    init_platform();

    XTime_GetTime(&tStart);
    print("Hello World\n\r");
    XTime_GetTime(&tEnd);

    printf("Output took %llu clock cycles.\n", 2*(tEnd - tStart));
    printf("Output took %.2f us.\n",
           1.0 * (tEnd - tStart) / (COUNTS_PER_SECOND/1000000));

    return 0;
}

This uses the global timer in the Zynq SoC whose counter increases every two clock cycles. So it gives pretty precise results.


Updates to this post:

  • Jul 28, 2015: Fix calculation of clock cycles in example program.

7 Responses to Measuring time in a bare-metal Zynq application

  1. Sven says:

    Hello,
    I think you you did a small mistake calculating the clock cycles.

    the define “COUNTS_PER_SECOND” already includes the fact the counter only counts every two clock cycles [ #define (CPU_FREQ /2) ]. so if multiply the difference between tEnd and tStart by 2 your time calculated will be double the original value.

    regards
    Sven

    • Michael says:

      The 2 is already included in COUNTS_PER_SECOND but I don’t use that constant when calculating the number of clock cycles. I just take the raw value of the counter registers and multiply it by 2.

      When calculating the time in µs, I leave out the factor 2 because it is already included in COUNTS_PER_SECOND. Or do I miss something here?

  2. Mihai says:

    The difference tEnd – tStart always gives me 0 for whatever instructions I put between XTime_GetTime(&tStart) and XTime_GetTime(&tEnd).

    Instructions between XTime_GetTime(&tStart) and XTime_GetTime(&tEnd):
    ————————————-
    print(“Hello World\n\r”);
    for(int i=0;i< 1000 ;i++)
    {
    sum += i;
    }
    Xil_Out32(0x43c00000, 0x5);
    Xil_Out32(0x7aa00000, 0x5555);
    NumberOfPattern = Xil_In32(0x43c00004);
    ————————————-

  3. Brett says:

    I can confirm that just running this program in Vivado 2017.2 does not work, and returns a difference of 0. However maybe this is due to never initializing the hardware timer itself?

    • Michael says:

      I retried the exact same code as posted above in Vivado 2017.2 and it works perfectly here on a Digilent Zedboard:

      Hello World
      Output took 2020 clock cycles.
      Output took 3.03 us.

      I used two distinct platform definitions: one exported by Vivado after configuring the ‘ZYNQ7 processing system’ IP block and running hardware synthesis, and the pre-defined ‘zed_hw_platform’ which can be chosen in the SDK. Both worked fine. Also, it did not matter if the PL was programmed or not.

      Finally, I removed Timer 0 (TTC0) from the hardware configuration and tried again. This time the program got stuck at the call to print(), so I guess the UART needs TTC0 to work. Also, I am not able to run any program without calling ps7_init at least once. So honestly, I have no clue how to reproduce the behavior that you and Mihai observe 😕

Leave a Reply to Michael Cancel reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.