When working with Virtual Platforms that are running operating systems it's sometimes useful to be able to access a memory or peripheral from a normal user space program. This can help determine if the hardware is connected properly and it can help understand the state of the registers in a peripheral. Fortunately, this is quite easy to do. If you would like to try the ideas described here please refer to the Virtual Platform series of articles I wrote last year. This was a 5 part series around the ARM Integrator platform (Part 1 Part 2 Part 3 Part 4 Part 5) and can be used for additional study.
Accessing memory or a peripheral using a physical address requires a little extra programming to turn the physical address into a virtual address that can be accessed from a user space program. Let's say that a Virtual Platform model has a memory located at address 0x20000000 in the memory map. As usual, I assume the operating system is embedded Linux. To access the memory after the system boots, a small C program can be used. For bare metal applications something like
unsigned long *mem = (unsigned long *) 0x20000000;
unsigned long data;
data = *mem;
would work fine. Many hardware diagnostics are written like this to test the hardware behavior. If this code is tried on embedded Linux things will not go very well. In fact, the program will crash. What is needed is to map the physical address of the memory into the programs virtual memory space using mmap(). Here is a small C program that will perform the desired behavior, it's a small memory test just to demonstrate the concept:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
main()
{
int i, j;
int words = 10;
int devmem;
off_t PageOffset, PageAddress;
unsigned long *hw_addr = (unsigned long *) 0x20000000;
unsigned long read_data;
devmem = open("/dev/mem", O_RDWR | O_SYNC);
PageOffset = (off_t) hw_addr % getpagesize();
PageAddress = (off_t) (hw_addr - PageOffset);
hw_addr = (unsigned long *) mmap(0, 4, PROT_READ|PROT_WRITE, MAP_SHARED, devmem, PageAddress);
for (i = 0, j = 0; i < words; i++, j+=4)
{
printf("Writing to hardware model\n");
*(hw_addr+j) = 0xabcd0000+j;
read_data = *(hw_addr+j);
printf("Read from hardware %x\n",read_data);
}
}
This program can be cross compiled for the ARM Integrator platform using:
% arm-none-linux-gnueabi-gcc -o mem_access mem_access.c
Follow the steps in Part 4 of my previous series to put the program into the file system, boot Linux, and run the program.
Another useful capability is to use the same program to read the registers from a programmable peripheral. The write/read pattern is not useful, but reading the peripheral's registers can be done the same way. For the Integrator platform the address can be changed to 0x16000000 and all or some part of the UART 0 registers can be read and printed.
Although this is very simple concept, finding clear descriptions of how to apply system calls like open(), getpagesize(), and mmap() to accomplish the task takes a little digging. This approach is very useful when assembling new Virtual Platforms to provide a sanity check that the memory map is configured correctly and the peripherals can be accessed by software. If you are a Virtual Platform creator I'm sure it will come in handy to help trace system behavior.
Jason Andrews
联系客服