The Global Counter Module
The global timer is a 64-bit incrementing counter that is clocked by PERIPHCLK, which is set to 333.333MHz on the Blackboard. Once started, the timer will count up for 3ns x 2^64, or 55 billion seconds, or about 1750 years before reaching the end of its count range. The timer has an user-settable 64-bit comparator that can assert an interrupt (ID #27) when the timer value matches the programmed compare value. The timer is accessed and controlled through 7 registers starting at address 0xF8F00200. You can refer to the Blackboard Programmers Reference, the ARM DDI 0407G document, and this background document for more information on the timer and its registers.
Global Counter Registers
The first two registers are the current counter value, and they can be read or written using two individual 32-bit accesses (STRD/LDRD can’t be used). These registers can be written (to define a new counter value) by clearing the timer enable bit in the global counter, then writing the lower 32 bits and the upper 32 bits, and then setting the timer enable bit. These registers can be read as normal, but keep in mind the lower 32-bit register is being updated at 333MHz, and it might change (and cause the upper 32-bit value to increment) mid-read. Therefore, a typical read algorithm might be:
- Read the upper 32-bit timer counter register
- Read the lower 32-bit timer counter register
- Read the upper 32-bit timer counter register again. If the value is different to the 32-bit upper value read previously, go back to step 2. Otherwise the 64-bit timer counter value is correct.
The control resister includes an 8-bit prescaler and 4 control bits. The prescaler value can change the clock period that is used for measuring the time between events generated by different counters (see ARM DDI 0407G section 4 for more info). The A bit (auto increment) causes the value stored in the Auto Increment Value register to be added to the Comparator Values Register each time the counter reaches the compare value. This is useful for automatically setting periodic compares. The I bit (IRQ enable) can be set to create an interrupt (ID #27) when the counter reaches the compare value (note that to generate an interrupt, the LSB of the Interrupt Status Register must also be set). The C bit (compare enable) can be set to enable the counter to compare its value to the compare value, and the T bit (timer enable) can be set to enable the counter to increment. See the ARM DDI 0407G document for more information.
Interrupt Status Control Register
Only one bit (the LSB) is used in this register. If the LSB is set and the IRQ enable bit is set in the control register, interrupt #27 is generated when the counter and compare values match. Writing a ‘1’ to this register will clear the event flag so that a new interrupt can be taken at the next compator match.
Comparator Values Regsiter
Two 32-bit registers set the compare-to value, and they must be accessed as two separate 32-bit accesses (so STRD and LDRD cannot be used). To write these registers, you should clear the compare enable bit in the control register, write the upper and lower registers, and then set the compare bit (and the interrupt enable bit if desired).
This 32-bit register provides the increment value that is used to increment the compare value after the counter reaches the compare value, if the auto-increment bit is set in the control register.
Setting up the global counter to generate 1ms interrupts
To set up the global timer to create a 1ms interrupt, you must set up the counter appropriately, and then enable interrupt #27 in the GIC. Setting up the counter requires the compare and auto-increment values to be programmed, the four control bits to be set apporpriately, and interrupts to be enabled.
To enable the GIC, you can generally follow the nine-step procedure in the “configuring the GIC” background document, but modified for interrupt ID#27 as shown in the following steps.
Step 1: Add the statments
*((uint32_t*) ICDIPTR_BASEADDR+0x18/4) = 0x00000000; // remove processors *((uint32_t*) ICDICER_BASEDDR) = 0x08000000; // clear interrupt ID 27
Step 2: No change
Step 3: Add the statement
*((uint32_t*) ICDIPR_BASEADDR+0x18/4) = 0x90000000; // Set priority to 9 for ID 27
Step 4: Add the statement
*((uint32_t*) ICDIPTR_BASEADDR+0x18/4) = 0x01000000; // Enable interrupt #27 for CPU0
Step 5: Add the statement
*((uint32_t*) ICDICFR_BASEADDR+0x4/4) = 0x7DC00000; // ID 27 = edge sensitive only
Step 6: Add the statement
*((uint32_t*) ICDISER_BASEADDR) = 0x08000000; // Enable ID 27
Step 7: No change
Step 8: No change
Step 9: No change