How does memory-mapped I/O addressing work?
I'm trying to understand a sample supplied I2S: Anyone got it running? .
Configuring Clocks:
#define BCM2708_PERI_BASE 0x20000000
#define CLOCK_BASE (BCM2708_PERI_BASE + 0x101000) /* Clocks */
It first maps the code like so...
clk_map = (unsigned char *)mmap(
(caddr_t)clk_mem,
MAP_BLOCK_SIZE,
PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_FIXED,
mem_fd,
CLOCK_BASE
);
Then it does something...
// Always use volatile pointer!
clk = (volatile unsigned *)clk_map;
And when it's referenced there's these odd addtions of 0x26 & 0x27, what's that about?
printf("Disabling I2S clock\n");
*(clk+0x26) = 0x5A000000;
*(clk+0x27) = 0x5A000000;
usleep(10);
printf("Confiure I2S clock\n");
*(clk+0x26) = 0x5A000001;
*(clk+0x27) = 0x5A000000 | 3<<12 | 1<<9; // divider: 3.125==0b11.001
usleep(10);
printf("Enabling I2S clock\n");
*(clk+0x26) = 0x5A000011;
Looking at the datasheet, I can see where they've got some of these values, like the base address, but I'm struggling to understand the others. Where is that CLOCK_BASE
determined and what's going on?
-
1This is probably best suited for StackOverflow. Although it relates to the RPi You'll be more likely to get answers to programming questions there.Jivings– Jivings2012年06月22日 07:59:55 +00:00Commented Jun 22, 2012 at 7:59
-
4Maybe, but I feel it's a more a general Pi programming related question combining the interpretation of the datasheet and the Pi hardware. let see if it gets some good info.Dog Ears– Dog Ears2012年06月22日 08:05:49 +00:00Commented Jun 22, 2012 at 8:05
-
Okay. Let's see how it goes :)Jivings– Jivings2012年06月22日 08:09:12 +00:00Commented Jun 22, 2012 at 8:09
-
1I don't think this would do too well on Stack Overflow - it's pretty specialist and would likely get more expert attention here.Flexo - Save the data dump– Flexo - Save the data dump2012年06月23日 15:32:02 +00:00Commented Jun 23, 2012 at 15:32
2 Answers 2
On a computer you write to a specified 'memory address'. This address is recognised by the system as a hardware address, and the appropriate hardware receives or sends the appropriate value.
Most hardware systems have many different registers that can be set or read. Some might have a few, some might have many. These registers will be grouped into a continuous range. A base pointer points to the first in the range, and you write to, for example, the second port with base_pointer+1. You don't have to, you could write direct to a pointer, but using an offset makes things easier to work with.
The Raspberry Pi recognises a massive range of hardware registers at the address 0x20000000. A range of registers that control clock systems are accessed from BCM2708_PERI_BASE + 0x101000. The registers that control the I2S clock are the 38th and 39th register in that block, written to using BCM2708_PERI_BASE + 0x101000 + 0x26 and 0x27
You can't just change the clock values though, you have to disable the clock, change the values and restart it.
If this answer is too basic, my apologies. In which case your question is really hardcore, good luck. You might find this link helpful
Update: Why use mmap and not write directly to memory?
When a program is running the memory addresses it thinks it has aren't real addresses, they are mapped to real addresses by the memory manager. This stops one program from being able to affect another. Two processes can read and write to their own address 1234 perfectly happily, and the memory manager will keep the two locations completely separate.
Hardware ports, however, are at absolute physical addresses. But you cant write to them directly because the memory manager will take your address and map it to your personal memory area.
On Linux /dev/mem is a 'character device file that is an image of the main memory of the computer'
If you open this like a file then you can read and write to it like a file. In the supplied sample mem_fd is a file handle that resulted from opening /dev/mem
Another system that can make life much easier is the ability to map a file to memory and write to it like memory. So if you have a file in which you want to read or write different specific bits then instead of moving the file pointer backwards and forwards, you can map it to a location in memory, and then write to it directly as if it was memory.
So in this sample the code is creating a handle to physical memory, as though it were a file on disk, and then asking the system to treat it as though it was memory. A bit convoluted, but necessary in order to get round the virtual memory manager and write to an actual physical address. The value 0x20000000, it seems, is a bit of a red herring. The code is proposing this address as a hint, the system does not have to map /dev/mem here, though it probably does. Normally the value null would be passed, and the system would map the file handle to whatever address it thought best.
Now the physical memory is mapped to the processes virtual memory, and reads and writes go where you expect.
References:
http://www.kernel.org/doc/man-pages/online/pages/man2/mmap.2.html
http://www.raspberrypi.org/phpBB3/viewtopic.php?f=44&t=8496&p=104359
-
I still have a couple of questions: Why do they mmap? Why not just access the memory directly?Alex Chamberlain– Alex Chamberlain2012年06月25日 13:09:56 +00:00Commented Jun 25, 2012 at 13:09
-
@AlexChamberlain Because the code runs on linux, so you can't access memory directly as each process gets their own virtual memory space. However one can open and mmap /dev/mem to gain direct access to the physical memorynos– nos2014年01月07日 15:08:36 +00:00Commented Jan 7, 2014 at 15:08
@AlexChamberlain this is due to the OS structure. You can go without mmap
but the paging is declared, hence no direct access. In kernel mode you can go without mmap
, by, for example inserting your driver as kernel module with no need for mmap
. Also, in simplest OS case, where no page table memory is used you can access without mmap
either, by ie. direct physical address access.