XO 4 Memory Test
The following information is only needed for debugging the timing parameters for a new DDR3 DRAM for the XO-4 laptop.
You will need a serial cable attached to the laptop's host serial port. Hold down the "rotate" key (near the screen) while booting the laptop, in order to stop the boot in CForth. You should see something like:
CForth built 2012-08-16 02:20 from commit e000ecca... Skipping OFW ok
Some versions of OFW (Q7A00 through Q7A06) automatically initialize the memory controllers before providing the prompt. You cannot change timing parameters using these versions. More details below.
CForth has a table with most of the memory configuration parameters loaded in memory, which you can modify. You can see a listing of this table using the ".table" command. The first column is the actual address in memory of that row of the table. The second column is the value being written to a configuration register. The third column is the offset, relative to the memory controller base address, of the configuration register being written.
ok .table d100dbc0 : e0001 10 d100dbc8 : 42530 20 d100dbd0 : 0 30 d100dbd8 : 911403cf 80 d100dbe0 : 64660784 84 d100dbe8 : c2004453 88 d100dbf0 : 34f4a187 8c d100dbf8 : f20c1 90 d100dc00 : 4040200 94 d100dc08 : 5501 98 d100dc10 : 0 50 d100dc18 : 0 54 d100dc20 : 20c08009 58 d100dc28 : 201 5c d100dc30 : 200000a 60 d100dc38 : 0 64 d100dc40 : 0 68 d100dc48 : 300008 240 d100dc50 : 80000000 24c d100dc58 : 31d8 23c d100dc60 : 20004055 220 d100dc68 : 1ff84a79 230 d100dc70 : ff00a70 234 d100dc78 : a7 238 d100dc80 : f0210000 248 d100dc88 : 0 300 d100dc90 : 1080 304 d100dc98 : 1 300 d100dca0 : 1080 304 d100dca8 : 2 300 d100dcb0 : 1080 304 d100dcb8 : 3 300 d100dcc0 : 1080 304 d100dcc8 : 100 380 d100dcd0 : 200 390 d100dcd8 : 101 380 d100dce0 : 200 390 d100dce8 : 102 380 d100dcf0 : 200 390 d100dcf8 : 103 380 d100dd00 : 200 390
The offset is relative to the memory controller base address which is 0xd001.0000 for memory controller 0 and 0xd000.0000 for memory controller 1. The table values are first written to memory controller 0, then its DLL is initialized, and then the process is repeated for memory controller 1. Finally, the interleave is configured by writing to the DDR_ILV_CTRL register at 0xd4282ca0 . See the Forth word "init-dram" for details (cforth/src/app/arm-xo-cl4/initdram.fth). It is usually called indirectly, by calling late-init.
After modifying the table, you can initialize the memory using the "late-init" (this calls init-dram after setting the freqs). You can then either test the memory or start OFW using the "ofw" command.
The memory may only be initialized once. late-init (init-dram) only works the first time (it doesn't even try after that). To test a different memory configuration you need to power cycle the laptop.
Some versions of OFW (Q7A00 through Q7A06) automatically initialize the memory controllers before providing the prompt. You do not need to type the late-init command with them. Typing it will have no effect. You cannot change timing parameters using these versions. Address mapping and interleave, however, may still be changed.
Testing 1Gb DDR3 Parts
One example of this is testing a particular new 1Gb DDR3 part, instead of the 2Gb parts the above table was generated for. To do this, first modify the relevant parts of the table (the first two entries):
ok d0001 d100dbc0 l! ok 42430 d100dbc8 l!
Then initialize both DRAM controllers using late-init. Don't forget to set the interleave to something smaller than the amount of memory. And then you can start OFW and boot Linux. This will need to be repeated on each boot.
ok late-init ok 2 d4282ca0 l! ok ofw
Direct mucking about
If you are just modifying an address mapping, interleave, or invalidating a chip select to disable a controller, you can directly modify the memory controller and CPU control registers even if the memory controllers have already been initialized.
You can modify memory controller registers using the mc! (write) and mc@ (read) commands in CForth:
value offset memory_controller mc! offset memory_controller mc@
These take an argument of 0 or 1 to identify the memory controller.
Single Memory Controller
Starting with Q7A07, late-init uses a variable ( #mcs ) to determine how many memory controllers to initialize.
You can configure your laptop to use a single memory controller (the default is two) by setting this variable to 1 before booting. Stop in CForth by holding down the rotate key before powering up the laptop, then on the serial console type:
ok 1 to #mcs ok late-init ofw
Boot should proceed normally, but with half as much memory as is normal.
The memory interleave register (DDR_INTERLEAVE_CONTROL) determines the boundary upon which memory controllers are interleaved in the address space. It is located at address 0xD4282CA0. It is a bit field set to zero to disable interleave (not recommended when using two controllers). Bit 0 selects interleaving on a 4KB boundary, bit 1 interleaving on a 16KB boundary, bit 2 a 64 KB boundary, bit 3 a 256 KB boundary, bit 4 a 1024KB boundary, bit 5 a 512 MB boundary, and bit 6 a 1 GB boundary.
For example, to change the system to interleave between memory controllers on a 256KB boundary, type:
ok 8 d4282ca0 l!
And to disable one of the memory controllers, type:
ok 0 d4282ca0 l! ok 0 10 0 mc!
This sets the interleave to zero, then disables memory controller 0.
SP Memory Addressing
Beware that CForth runs on the SoCs security processor (SP), using physical addressing. The SP's memory addressing is a little strange relative to OFW's physical addressing running on the main core(s), listed as DRAM address:
|DRAM address||SP address|
|0x0000.0000||0x0000.0000 (if TCM is off; memory inaccessible via SP address 0 with TCM on)|
Memory Test Commands
If using a version of CForth which doesn't automatically init the memory (i.e.: a version later than Q7A06), if you run a memory test without first initializing the memory using late-init, the processor will silently hang.
Canned Test Commands
|memtest||( )||Runs a random-pattern test (see below) and reports the result|
|memtest-start||( -- address )||Value containing the start address for memtest|
|memtest-length||( -- length )||Value containing the length for memtest|
|2000.0000 to memtest-start||Change the memtest start address|
|1000.0000 to memtest-length||Change the memtest length|
Test Primitives for Detailed Debugging
In the following commands, if an error is encountered, the returns value is the address where the error occurred. If no error is encountered the returned error address is -1 (0xFFFFFFFF).
|lfill||( address length value -- )||Fill memory range with 32-bit value|
|lcheck||( address length value -- error_address )||Check the result of lfill|
|inc-fill||(address length -- )||Fill memory range with data=address|
|inc-check||(address length -- error_address )||Check the result of inc-fill|
|random-fill||( address length -- )||Fill memory range with psuedorandom data using a pseudorandom access order|
|random-check||( address length -- error_address )||Check the result of random-fill|
lfill/lcheck are good for fast bit-level verification.
inc-fill/inc-check are good for checking addressing.
random-fill/random-check are somewhat slower. They are good for thorough checkout, but if there are problems, it can be difficult to determine the exact problem syndrome, since the test pattern is not easily recognizable.
For example, to test the lower 1 MB of memory:
ok 0 100000 random-fill ok 0 100000 random-check .