April 24, 2026

Allwinner H5 network driver -- RAM

Currently Kyu reports 2048M of ram for the h5, but the description of the Orange Pi PC2 says there is only 1G. I suspect this 1G is aliased in the address space, but this needs to be investigated.

The H5 datasheet shows device registers in the low 1G of the address space, and allocates the upper 3G for DDR memory.

Probe using U-Boot

U-boot provides various commands for poking at memory, including:
md 0x40000000
mw.l 0x40000000 0xdeadbeef
Actually, the 0x prefix is not required. The U-boot shell works in hex.

I use these commands to put unique values into memory at 4000000, 50000000, ... f0000000 -- and it works! I go back after setting all the values and see them all where I placed them. This should not be possible unless I have 3G of DDR memory.

I try this again, writing a value at address 40000000 and looking for it at 80000000. I write the value, then do all sorts of things, expecting at some point the dirty value in the cache will get flushed to memory. Of course there are two cache lines to worry about. The one for the address 40000000, which we want to flush, and the one for the address 80000000, which we want to invalidate. So far I have not managed to see the value I wrote to 40000000 by looking at 80000000.

The chips

I inspect my board and find a pair of Samsung K4B4G16 chips. Each chip is 512M-bytes, so the pair should be 1G as advertised. Why am I seeing more ram?

How does Kyu determine RAM size

Kyu first starts up in armv8/startup.S which calls "board_mmu_init() before it calls kyu_startup. For the H5 this is:
	ram_start = BOARD_RAM_START;

    printf ( "Probing for amount of ram\n" );
    ram_size = ram_probe ( ram_start );
    printf ( "Found %d M of ram\n", ram_size/(1024*1024) );

    mmu_initialize ( ram_start, ram_size );

Note that this uses printf() even at this early stage, which works because the serial port has been configured by U-Boot.

The routine of interest is ram_probe() in armv8/ram.c The method used is to try a descending list of possible ram sizes (2G, 1G, 512M, 256M). To test the 2G size, it writes a word to the last long in that address range and checks that it can read back the value written. It also looks at the last long in the size smaller (1G when testing 2G) to check for aliasing. If it sees the magic value there, it assumes address aliasing and moves to the next smaller size.

Write some code

I write the value deadabcd to address 4fff_fffc then scan the entire address space from 4000_0000 to ffff_fffc and here is what I see:
Found at 0000000040583f40 - deadabcd
Found at 000000004ffffffc - deadabcd
Found at 000000008ffffffc - deadabcd
Found at 00000000cffffffc - deadabcd
The first hit (at 40583f40) is without doubt the constant itself in the code I just wrote. Note that now I see 1G of ram aliased 3 times in the 3G address space. Just what we would expect.

I try the experiment again using U-Boot and these addresses and get the same crazy result where I seem to have 3G of ram. The cache may be tricking me (and my ram_probe() routine). The difference between my test and the probe is that my test scans sequentially through all of memory before trying to read the aliased locations.


Have any comments? Questions? Drop me a line!

Tom's electronics pages / tom@mmto.org