How it works...

The system timer is a peripheral device that is connected to the processor using an MMIO interface. This means it has a dedicated range of physical addresses, each of them with a specific format and purpose.

Our application works with a timer counter represented as two 32-bit values. Combined, they form a 64-bit read-only counter always incrementing when the system is running.

For the Raspberry PI 3, a physical memory address range allocated for the system timer has offset the following —0x3F003000 (it may be different depending on the Raspberry PI hardware revision). We define it as a constant.

constexpr uint32_t kTimerBase = 0x3F003000;

To access individual fields within the region, we define a SystemTimer struct:

struct SystemTimer {
uint32_t CS;
uint32_t counter_lo;
uint32_t counter_hi;
};

Now, we need to get the pointer to the timer address range and convert it to a pointer to  SystemTimer. This way, we can access the addresses of the counter by reading the SystemTimer data fields.

There is, however, a problem we need to solve. We know the offset in the physical address space, but our Linux application works within the virtual address space. We need to find a way to map physical addresses to virtual addresses. 

Linux provides access to physical memory addresses using the special /proc/mem file. Since it contains a snapshot of all physical memory, it is accessible only by root

We open it as a regular file using the open function:

int memfd = open("/dev/mem", O_RDWR | O_SYNC);

Once the file is open and we know its descriptor, we can map it into our virtual address space. We do not need to map the whole physical memory. A region related to the timer is sufficient; that is why we pass the system timer range start as an offset parameter and the size of the SystemTimer structure as the size parameter:

SystemTimer *timer = (SystemTimer*)mmap(NULL, sizeof(SystemTimer),
PROT_READ|PROT_WRITE, MAP_SHARED, memfd, kTimerBase);

Now we can access the timer fields. We read the timer counter in the loop and display its current value and its variance from the preceding value. When we run our application as root, we get the following output:

As we can see, reading from this memory address returns increasing values. The value of the difference is around 10,000 and pretty constant. Since we added a 10-millisecond delay into the counter read loop,  we can infer that the memory address is associated with the timer, not regular memory, and the timer counter granularity is 1 microsecond.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.252.201