© Warren Gay 2018
Warren GayAdvanced Raspberry Pihttps://doi.org/10.1007/978-1-4842-3948-3_5

5. SDRAM

Warren Gay1 
(1)
St. Catharine’s, Ontario, Canada
 

The original Model B Rev 2.0 Raspberry Pi had 512 MB of SDRAM (synchronous dynamic random access memory), while the older revisions and the Model A had 256 MB. Modern Pi’s now come equipped with 1 GB of RAM except for the Raspberry Pi Zero, which has 512 MB. Contrast this to the AVR class ATmega328P, which has 2 KB of static RAM!

There isn’t much about the memory hardware that concerns the average Pi developer because the operating system provides a rich facility for managing program use. However, let’s examine some useful Raspbian Linux interfaces that inform us how that memory is utilized. You’ll also examine how to access the memory-mapped ARM peripherals directly from your Linux application.

/proc/meminfo

The pseudo file /proc/meminfo provides us with information about memory utilization. This information varies somewhat by architecture and the compile options used for that kernel. Let’s study an example that was produced by a Raspberry Pi 3 Model B+ running Raspbian Linux:
$ cat /proc/meminfo
MemTotal:         949452 kB
MemFree:          631676 kB
MemAvailable:     756320 kB
Buffers:           20532 kB
Cached:           158004 kB
SwapCached:            0 kB
Active:           170108 kB
Inactive:         107020 kB
Active(anon):      99576 kB
Inactive(anon):    13172 kB
Active(file):      70532 kB
Inactive(file):    93848 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:        102396 kB
SwapFree:         102396 kB
Dirty:                40 kB
Writeback:             0 kB
AnonPages:         98596 kB
Mapped:            79016 kB
Shmem:             14152 kB
Slab:              22444 kB
SReclaimable:      10640 kB
SUnreclaim:        11804 kB
KernelStack:        1768 kB
PageTables:         3376 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      577120 kB
Committed_AS:     809528 kB
VmallocTotal:    1114112 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
CmaTotal:           8192 kB
CmaFree:            6796 kB

All of the memory values shown have the units KB to the right of them, indicating kilo (1,024) bytes. These reported values are described in great detail online.7 But let’s summarize some values of interest.

MemTotal

The MemTotal line indicates the total amount of memory available, minus a few reserved binary regions. Note that memory allocated to the GPU is not factored into MemTotal. Some owners may choose to allocate the minimum of 16 MB to the GPU to make more memory available when not using the graphical desktop. The default is to assign 64 MB.

If this is broken down a bit further, accounting for memory allocated to the GPU, we find that there is about 32 MB of memory that is unaccounted for, outlined in Table 5-1. This will vary with the Pi model and the GPU memory assigned because of the way the GPU addresses memory.
Table 5-1

 GPU and Main Memory Breakdown

Memory

Model B

Comments

SDRAM

1,048,576 KB

Hardware-equipped memory

MemTotal

949,452 KB

/proc/meminfo

Difference

99,124 KB

Remaining memory

gpu_mem

65,536 KB

/boot/config.txt (gpu_mem=64 by default)

Difference

33,588 KB

Unaccounted for

MemFree

MemFree normally represents the sum of LowFree + HighFree memory in kilobytes on the Intel x86 platform. For ARM, this simply represents the amount of memory available to user space programs.

Buffers

This value represents temporary buffers used within the kernel for raw disk blocks, and so forth. This value should not get much larger than about 20 MB or so.8

Cached

This value represents the read file content that has been cached (page cache). This does not include the content reported for SwapCached.

SwapCached

The value shown for SwapCached represents memory that was swapped out and is now swapped back in. For efficiency, these memory pages are still represented by swap disk space, should they be needed again. The fact that the value is reported as zero is a happy sign that no swapping has occurred or is no longer pertinent.

Active

The Active memory value represents recently used memory that is not reclaimed, unless absolutely necessary.

Inactive

This value represents memory that is not active and is likely to be reclaimed when memory is needed.

Active(anon)

This value represents memory that is not backed up by a file and is active. Active memory is not reclaimed unless absolutely necessary.

Inactive(anon)

This value represents memory that is not backed up by a file and is not active. Inactive memory is eligible to be reclaimed if memory is required.

Active(file)

This value represents file-backed memory, which is active. Active memory is reclaimed only if absolutely required.

Inactive(file)

This value represents inactive memory that is backed by a file. Inactive memory is eligible for reclamation, when memory is required.

Unevictable

This amount reflects the total amount of memory that cannot be reclaimed. Memory that is locked, for example, cannot be reclaimed.

Mlocked

This value reports the amount of locked memory.

SwapTotal

This value reports the total amount of swap space available in kilobytes.

SwapFree

This value reports the remaining amount of swap space available in kilobytes.

Dirty

This value represents the kilobytes of memory that have been modified and are waiting to be written to disk.

Writeback

This value reports the amount of memory in kilobytes being written back to disk.

AnonPages

This represents the non-file-backed pages of memory mapped into user space.

Mapped

This value reports the files that have been mapped into memory. This may include library code.

Shmem

This parameter does not appear to be documented well. However, it represents the amount of shared memory in kilobytes.

Slab

This parameter is described as “in-kernel data structures cache.”

SReclaimable

This parameter is described as “Part of Slab that might be reclaimed, such as caches.”

SUnreclaim

This parameter is described as “Part of Slab that cannot be reclaimed [under] memory pressure.”

KernelStack

This value reports the memory used by the kernel stack(s).

PageTables

This value reports the amount of memory required by the page tables used in the kernel. Clearly, with more memory to manage, there is more memory dedicated to page tables.

NFS_Unstable

This value represents “NFS pages sent to the server, but not yet committed to stable storage.” This example data suggests that NFS is not being used.

Bounce

This reports the memory used for “block device bounce buffers.”

WritebackTmp

This parameter reports the memory used by FUSE for “temporary writeback buffers.”

CommitLimit

The documentation states:

Based on the overcommit ratio (vm.overcommit_ratio), this is the total amount of memory currently available to be allocated on the system. This limit is only adhered to if strict overcommit accounting is enabled (mode 2 in vm.overcommit_memory). The CommitLimit is calculated with the following formula:

$$ CommitLimit=left( vm. overcommit\_ ratio	imes PhysicalRAM
ight)+ swap $$

For example, a system with 1 GB of physical RAM and 7 GB of swap with a vm.overcommit_ratio of 30 would yield a CommitLimit of 7.3 GB. For more details, see the memory overcommit documentation in vm/overcommitaccounting.

The formula can be written as follows:
$$ C=left(R	imes r
ight)+S $$
The elements of this formula are described here:
  • C is the overcommit limit.

  • R is the physical RAM available (MemTotal).

  • S is the swap space available (SwapTotal).

  • r is the overcommit ratio percent (expressed as a fraction).

The overcommit ratio, r, is not reported in the /proc/meminfo data. To obtain that ratio, we consult another pseudo file. This example was taken from a Rev 2.0 Model B, but it appears to be a value common to all Pi’s:
$ cat /proc/sys/vm/overcommit_ratio
50

The value 50 is to be interpreted as r = 0.50 (50%).

Using the overcommit formula, the value for S can be computed for the swap space available:
$$ {displaystyle egin{array}{l}S=C-left(R	imes r
ight)\ {}kern1em =577120-left(949452	imes 0.50
ight)\ {}kern1em =82394kern0.125em KBend{array}} $$
The overcommit ratio is configurable by the user, by writing a value into the pseudo file. This example changes the ratio to 35%:
$ sudo -i
# echo 35 >/proc/sys/vm/overcommit_ratio
# cat /proc/sys/vm/overcommit_ratio
35

Committed_AS

This parameter is described as follows:

The amount of memory presently allocated on the system. The committed memory is a sum of all of the memory which has been allocated by processes, even if it has not been “used” by them as of yet. A process which malloc()’s 1 GB of memory, but only touches 300 MB of it will only show up as using 300 MB of memory even if it has the address space allocated for the entire 1 GB. This 1 GB is memory which has been “committed” to by the VM and can be used at any time by the allocating application. With strict overcommit enabled on the system (mode 2 in vm.overcommit_memory), allocations which would exceed the CommitLimit (detailed above) will not be permitted. This is useful if one needs to guarantee that processes will not fail due to lack of memory once that memory has been successfully allocated.

VmallocTotal

This represents the total amount of allocated virtual memory address space.

VmallocUsed

This is the amount of virtual memory that is in use, reported in kilobytes.

VmallocChunk

This value reports the largest size of a vmalloc area, in kilobytes.

Physical Memory

Normally, physical memory isn’t a concern to application programmers because the operating system and its drivers provide an abstract and often portable way to access it. However, when this support is absent, direct access to a peripheral like the PWM controller is necessary.

Figure 5-1 illustrates the physical addressing used on the Raspberry Pi for the original Pi Model B (for simplicity). The SDRAM starts at physical address zero and works up to the ARM/GPU split point (Chapter 17 defines the split point). The ARM peripherals are mapped to physical memory starting at the address of 0x20000000, on the Pi Model B. This starting address is of keen interest to programmers.
../images/326071_2_En_5_Chapter/326071_2_En_5_Fig1_HTML.jpg
Figure 5-1

 Physical memory layout

With modern Pi models, we cannot assume a constant for the starting peripheral address. Later in the book, a way to determine this value at runtime will be shown.

In the region labeled Peripherals, the offsets and addresses indicated in Table 5-2 are of interest to us.
Table 5-2

 Peripheral Offsets for the Raspberry Pi Model B

Peripheral

Offset

Address

Description

C Offset Macro

Base

0x00000000

0x20000000

Starting address

BCM2708_PERI_BASE

PADS_GPIO

0x00100000

0x20100000

PADS base

PADS_GPIO_BASE

GPIO 00..27

0x0010002C

0x2010002C

GPIO 00..27 pads

PADS_GPIO_00_27

GPIO 28..45

0x00100030

0x20100030

GPIO 28..45 pads

PADS_GPIO_28_45

GPIO 46..53

0x00100034

0x20100034

GPIO 46..53 pads

PADS_GPIO_46_53

Clock

0x00101000

0x20101000

Clock registers

CLK_BASE

GPIO

0x00200000

0x20200000

GPIO registers

GPIO_BASE

GPPUD

0x00200025

0x20200025

Pull-up enable

 

GPPUDCLK0

0x00200026

0x20200026

Pull-up clock 0

 

GPPUDCLK1

0x00200027

0x20200027

Pull-up clock 1

 

PWM

0x0020C000

0x2020C000

PWM registers

PWM_BASE

Memory Mapping

To gain access to physical memory under Linux, you use the /dev/mem character device and the mmap(2) system call. The /dev/mem node is shown here:
$ ls -l /dev/mem
crw-r----- 1 root kmem 1, 1 Jun  5 20:24 /dev/mem

From the ownership information shown, it is evident that you’ll need root privileges to access it. This is sensible given that a process can cause havoc with direct access to the physical memory. Exercise caution in what the applications do with this memory access.

The mmap(2) system call API is shown here:
#include <sys/mman.h>
void ∗mmap(
  void     ∗addr,    /∗Address to use ∗/
  size_t    length,   /∗Number of bytes to access ∗/
  int       prot,     /∗Memory protection ∗/
  int       flags,    /∗Option flags ∗/
  int       fd,       /∗Opened file descriptor ∗/
  off_t     offset    /∗Starting off set ∗/
) ;
Rather than look at all the options and flags available to this somewhat complicated system call, let’s look at the ones that we use in the following code:
static char ∗map = 0;
static void
gpio_init() {
    int fd;
    char ∗map;
    fd = open("/dev/mem",O_RDWR|O_SYNC) ;   /∗Needs root access ∗/
    if ( fd < 0 ) {
        perror("Opening /dev/mem") ;
        exit(1) ;
    }
    map = (char ∗) mmap(
        NULL,                  /∗ Any address ∗/
        BLOCK_SIZE,            /∗ # of bytes ∗/
        PROT_READ|PROT_WRITE,
        MAP_SHARED,            /∗Shared ∗/
        fd,                    /∗ /dev/mem ∗/
        GPIO_BASE              /∗ Offset to GPIO ∗/
    ) ;
    if ( (long)map == −1L ) {
        perror("mmap(/dev/mem)");
        exit(1) ;
    }
    close(fd);
    ugpio = (volatile unsigned ∗)map;
}

The first thing performed in this code is to open the device driver node /dev/mem. It is opened for reading and writing (O_RDWR), and the option flag O_SYNC requests that any write(2) call to this file descriptor results in blocking the execution of the caller until it has completed.

Address

Next, the mmap(2) call is invoked. The address argument is provided with NULL (zero) so that the kernel can choose where to map it into the caller’s address space. If the application were to specify a starting address to use and the kernel was not able use it, the system call would fail. The starting address is returned and assigned to the character pointer map in the preceding listing.

Length

Argument 2 is supplied with the macro BLOCK_SIZE in this example. This is the number of bytes you would like to map into your address space. This was defined earlier in the program as 4 KB:
#define BLOCK_SIZE (4∗1024)
While the application may not need the full 4 KB of physical memory mapped, mmap(2) may insist on using a multiple of the page size. This can be verified on the command line as follows:
$ getconf PAGE_SIZE
4096
A program can determine this directly by using the sysconf(2) system call:
#include <unistd.h>
    ...
    long sz = sysconf(_SC_PAGESIZE);

Protection

The third mmap(2) argument is supplied with the flags PROT_READ and PROT_WRITE. This indicates that the application wants both read and write access to the memory-mapped region.

Flags

The flags argument is supplied with the value MAP_SHARED. This permits nonexclusive access to the underlying mapping.

File Descriptor

This argument supplies the underlying opened file to be mapped into memory. In this case, we map a region of physical ARM memory into our application by using the opened device driver node /dev/mem.

Offset

This last argument specifies the location in physical memory where we want to start our access. For the GPIO registers, it is the address 0x20200000 on the original Pi Model B.

Return Value

The return value, when successful, will be an application address that points to the physical memory region we asked for. The application programmer need not be concerned with what this address is, except to save and use it for access.

The return value is also used for indicating failure, so this should be checked and handled:
if ( (long) map == –1L )  {
    perror("mmap(/dev/mem)");
    exit(1);
}

The returned address (pointer) map is cast to a long integer and compared to -1L. This is the magic value that indicates that an error occurred. The error code is found in errno.

Volatile

The last section of this initialization code for GPIO assigns the address map to another variable, ugpio, as follows:
ugpio = (volatile unsigned ∗)map;
The value ugpio was defined earlier in the program:
static volatile unsigned ∗ugpio = 0;
There are two things noteworthy about this:
  • The data type is an unsigned int (32 bits on the Pi).

  • The pointed-to data is marked as volatile.

Since the Pi registers are 32 bits in size, it is often more convenient to access them as 32-bit words. The unsigned data type is perfect for this. But be careful with offsets in conjunction with this pointer, since they will be word rather than byte offsets.

The volatile keyword tells the compiler not to optimize access to memory through the pointer variable. Imagine code that reads a peripheral register and reads the same register again later, to see whether an event has occurred. An optimizing compiler might say to itself, “I already have this value in CPU register R, so I’ll just use that because it's faster.” But the effect of this code is that it will never see a bit change in the peripheral’s register because that data was not fetched back into the register. The volatile keyword forces the compiler to retrieve the value even though it would be faster to use the value still found in the register.

Virtual Memory

In the previous section, you looked at how to access physical memory in an application, provided that you had the access (root or setuid). The Broadcom Corporation PDF manual “BCM2835 ARM Peripherals,” page 5, also shows a virtual memory layout on the right. This should not be confused with the physical memory layout that was examined earlier. Virtual memory can be accessed through /dev/kmem driver node using mmap(2).

Summary

Some parameters such as Buffers impact the performance of Raspbian Linux on the Pi. How much memory is allocated to file data that has been read from disk, for example? Other configuration parameters determine how much SDRAM is dedicated to GPU use.

Probably the most important aspect of memory allocation is how much memory is available to developer application programs. The value of MemFree is therefore a useful metric. When exceeding physical memory limits, the swapping parameters then become measurements of interest.

Finally, access to the Raspberry Pi peripherals directly using mmap(2) was introduced. Until Raspbian Linux gains device drivers for peripherals such as PWM, the direct access technique will be necessary. Even with driver support, there are sometimes good reasons to access the peripheral registers directly.

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

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