Example using Arm Development Studio

This section of the guide includes a short example, downloadable as a zip file, to demonstrate the configuration of the System Counter and the generation of interrupts using TVAL and CVAL.

The example requires Arm Development Studio. If you do not already have a copy of Arm Development Studio, you can download an evaluation copy.

The example includes a ReadMe.txt file which lists the included files, and instructions for building and running the example.

Within main.c is the code for configuring the Timers. Going through main(), it starts with:

//
// Configure the interrupt controller
//
rd = initGIC();

// Secure Physical Timer (INTID 29)
setIntPriority(29, rd, 0);
setIntGroup(29, rd, 0);
enableInt(29);

// Non-secure EL1 Physical Timer (INTID 30)
setIntPriority(30, rd, 0);
setIntGroup(30, rd, 0);
enableInt(30, rd);

The previous code configures the GIC. The operation of the GIC is beyond the scope of this guide, but configuring the GIC is necessary to generate timer interrupts.

The function initGIC() performs top-level initialization of the interrupt controller. The following calls configure and enable the interrupt sources associated with the Secure physical timer and Non-secure EL1 physical timer. Each interrupt is configured as follows:

  • Group 0. This means the interrupt will be signaled as a FIQ.
  • Priority 0. This is the highest priority value in the GIC architecture.
  • Enabled. This allows the interrupt to be signaled to the core.

Next, the System Counter is initialized, as shown here:

//
// Configure and enable the System Counter
//
setSystemCounterBaseAddr(0x2a430000); // Address of the System Counter
initSystemCounter(SYSTEM_COUNTER_CNTCR_HDBG,
                  SYSTEM_COUNTER_CNTCR_FREQ0,
                  SYSTEM_COUNTER_CNTCR_nSCALE);

The first call sets the location of the System Counter, so that the driver functions can access its registers. This address is based on the Arm Base Platform Model. More information on this model’s memory map can be found in the Fast Models Reference Manual.

The second call writes the CNTCR register. The code selects frequency update scheme 0, disables scaling and sets the enable bit. After this point, the system count will start incrementing.

Next main() has the following:

//
// Configure timer
//

// Configure the Secure Physical Timer
// This uses the CVAL/comparator to set an absolute time for the timer to fire
current_time = getPhysicalCount();
setSEL1PhysicalCompValue(current_time + 10000);
setSEL1PhysicalTimerCtrl(CNTPS_CTL_ENABLE);

// Configure the Non-secure Physical Timer
// This uses the TVAL/timer to fire the timer in X ticks
setNSEL1PhysicalTimerValue(20000);
setNSEL1PhysicalTimerCtrl(CNTP_CTL_ENABLE);

The preceding code configures two of the timers:

  • Sets up the Secure physical timer, CNTPS, using the CVAL.
  • Sets up the Non-secure EL1 physical timer, CNTP, using TVAL.

The code in main() then waits for both interrupts to be generated before exiting.

The interrupt handler is also within main.c:

void fiqHandler(void)
{
  uin32_t ID;

  // Read the IAR to get the INTID of the interrupt taken
  ID = readIARGrp0();

  printf("FIQ: Received INTID %d\n", ID);

  switch (ID)
  {
    case 29:
      setSEL1PhysicalTimerCtrl(0); // Disable timer to clear interrupt
      printf("FIQ: Secure Physical Timer\n");
      break;
    case 30:
      setNSEL1PhysicalTimerCtrl(0); // Disable timer to clear interrupt
      printf("FIQ: Non-secure EL1 Physical Timer\n");
      break;
    case 1023:
      printf("FIQ: Interrupt was spurious\n");
      return;
    default:
      printf("FIQ: Panic, unexpected INTID\n");
  }

  // Write EOIR to deactivate interrupt
  writeEOIGrp0(ID);

  flag++;
  return;
}

The interrupt handler reads the Interrupt Acknowledge Register (IAR) of the GIC to get the ID of the interrupt that has been taken. Based on the returned value, the handler then disables the appropriate timer to clear the interrupt. An alternative approach would be to set IMASK, and mask the interrupt, or update the comparator.

Finally, the interrupt handler writes the End of Interrupt Register (EOIR) of the GIC. This updates the internal state machine of the GIC for the taken interrupt.

Previous Next