ZYNQ7000 Global Timer and Its Driver Example

Introduction to Timers

In ZYNQ embedded systems, timer resources are abundant. Each Cortex-A9 processor has its own independent 32-bit private timer and a 32-bit watchdog timer, while both CPUs share a single 64-bit Global Timer Counter (GTC).

System Block Diagram

ZYNQ7000 Global Timer and Its Driver Example

Global Timer (GTC)

The Global Timer is a 64-bit incrementing counter with an automatic increment feature. The Global Timer is memory-mapped to the same address space as the dedicated timers. The Global Timer is only accessed during a reset in a secure state. All Cortex-A9 processors can access this Global Timer. Each Cortex-A9 processor has a 64-bit comparator, which is used to generate a private interrupt when the Global Timer reaches the comparator value.

GTC Clock

  • The clock for the Global Timer GTC is half of the CPU clock frequency (CPU_6x4x), which is CPU_3x2x.

GTC Register Table

ZYNQ7000 Global Timer and Its Driver Example
ZYNQ7000 Global Timer and Its Driver Example

GTC Driver Example

  • gtc.c driver source file:
/**
 * Copyright (c) 2022-2023, HelloAlpha
 * 
 * Change Logs:
 * Date           Author       Notes
 */
#include "gt.h"

#include "stdio.h"

void GtStart(void)
{
    /* Start the Global Timer */
    GT_WR_REG(GT_CTRL_REG, AUTO_INC_BIT | IRQ_ENABLE_BIT 
                | COMP_ENABLE_BIT | TMR_ENABLE_BIT);
}

/* Global Timer initialization and interrupt configuration */
int GtIntrInit(XScuGic *GtInstancePtr, 
                uint64_t Value, void(* CallBack)(void *))
{
    int Status;

    /* Stop the Global Timer */
    GT_WR_REG(GT_CTRL_REG, 0);
    /* Clear the lower 32 bits of the counter */
    GT_WR_REG(GT_CNT_REG0, 0);
    /* Clear the upper 32 bits of the counter */
    GT_WR_REG(GT_CNT_REG1, 0);
    /* Clear the interrupt status */
    GT_WR_REG(GT_INTR_STAT_REG, 1);
    /* Load the lower 32 bits of the comparator */
    GT_WR_REG(COMP_VAL_REG0, (uint32_t)Value);
    /* Load the upper 32 bits of the comparator */
    GT_WR_REG(COMP_VAL_REG1, 0);
    /* Load the increment register value */
    GT_WR_REG(AUTO_INC_REG, (uint32_t)(Value >> 32));
    /* Bind the Global Timer interrupt service function */
    Status = XScuGic_Connect(GtInstancePtr, GT_INTR,
        (Xil_ExceptionHandler)CallBack, 0);
    if (Status != XST_SUCCESS)
    {
        return Status;
    }
    /* Map the 27th Global Timer interrupt to CPU1 */
    XScuGic_InterruptMaptoCpu(GtInstancePtr, 1, GT_INTR);
    /* Enable the Global Timer interrupt (27th) */
    XScuGic_Enable(GtInstancePtr, GT_INTR);
    return Status;
}

/* Reset the counter */
void gt_tic(void)
{
    *((volatile int*)(GT_CTRL_REG)) = 0x00;
    *((volatile int*)(GT_CNT_REG0)) = 0x00000000;
    *((volatile int*)(GT_CNT_REG1)) = 0x00000000;
    *((volatile int*)(GT_CTRL_REG)) = 0x01;
}

/** Read the counter and output the current time in ms
 *  Can be used with gt_tic as follows:
 *  {
 *      gt_tic();
 *      function_to_get_running_time();
 *      gt_toc();
 *  }
 */
double gt_toc(void)
{
    *((volatile int*)(GT_CTRL_REG)) = 0x00;
    long long cnt = *((volatile int*)(GT_CNT_REG1));
    double elapsed_time = cnt << 32;
    cnt = *((volatile int*)(GT_CNT_REG0));
    elapsed_time += cnt;
    elapsed_time /= CLK_3x2x;
    elapsed_time *= 1000;
    printf("Elapsed time is %f ms.\r\n",elapsed_time);
    return elapsed_time;
}

/* Get the current time (in seconds) */
float get_time_s(void)
{
    XTime tCur = 0;
    XTime_GetTime(&tCur);
    return (tCur / (float) COUNTS_PER_SECOND);
}
  • gtc.h driver header file:
/**
 * Copyright (c) 2022-2023, HelloAlpha
 * 
 * Change Logs:
 * Date           Author       Notes
 */
#ifndef __GTC_H__
#define __GTC_H__

#include "xtime_l.h"
#include "xscugic.h"
#include "xil_io.h"

/* Timer Registers */
#define GT_BASEADDR     GLOBAL_TMR_BASEADDR
#define GT_CNT_REG0     GT_BASEADDR + GTIMER_COUNTER_LOWER_OFFSET
#define GT_CNT_REG1     GT_BASEADDR + GTIMER_COUNTER_UPPER_OFFSET
#define GT_CTRL_REG     GT_BASEADDR + GTIMER_CONTROL_OFFSET
/* Interrupt Registers */
#define GT_INTR         XPAR_GLOBAL_TMR_INTR
/* Interrupt Status Register */
#define GT_INTR_STAT_REG    GT_BASEADDR + 0x0CU
/* Comparators */
#define COMP_VAL_REG0       GT_BASEADDR + 0x10U
#define COMP_VAL_REG1       GT_BASEADDR + 0x14U
/* Auto Increment Register */
#define AUTO_INC_REG        GT_BASEADDR + 0x18U

#define AUTO_INC_BIT        0x08
#define IRQ_ENABLE_BIT      0x04
#define COMP_ENABLE_BIT     0x02
#define TMR_ENABLE_BIT      0x01

#define CLK_3x2x    333333333

#define GT_WR_REG   Xil_Out32

void GtStart(void);
int GtIntrInit(XScuGic *GtInstancePtr, 
                uint64_t Value, void(* CallBack)(void *));
void gt_tic(void);
double gt_toc(void);
float get_time_s(void);

#endif

Reference: UG585; Summary of online resources.

The End

ZYNQ7000 Global Timer and Its Driver Example

Share, bookmark, like, and follow!

ZYNQ7000 Global Timer and Its Driver Example

Leave a Comment