I recently purchased some low-power Mini-ITX motherboards, including the Gigabyte GA-N3050N-D3H and Biostar N3050NH. These motherboards use SO-DIMM DDR3 RAM, commonly found in laptops.
Although these two motherboards are very similar, the Gigabyte motherboard lacks some BIOS features and settings compared to the Biostar motherboard. One of the missing features is the inability to set the RAM frequency. I want to run the RAM at a minimum frequency of 800MHz, but the Gigabyte motherboard runs it at the normal frequency of 1600MHz. This means that the Biostar motherboard can reduce idle power consumption by about 0.12W, or around 2%, with just a simple switch.
To solve this problem, I need to modify the RAM’s Serial Presence Detect (SPD) data. The SPD data is a set of configuration parameters stored in a small EEPROM chip, which the BIOS reads during boot-up. Fortunately, the SPD EEPROM communicates using SMBus, which is largely compatible with I2C on Arduino.
The SPD EEPROM
Modifying the RAM’s SPD data is not a new concept, and there are already many tools and programs available online that can perform SPD operations. However, the software and hardware compatibility of these tools and programs seem to be very unstable, failing to meet my requirements.
To avoid damaging the RAM module when connecting the wires to the SPD EEPROM, I used a DDR3 DIMM to SO-DIMM adapter. These adapters are indeed effective, but require careful setting of the DIL switch to use the RAM module’s SPD instead of the adapter’s SPD. Unfortunately, I previously carelessly modified the adapter’s SPD configuration and now I don’t know what its initial configuration was:
If you try to modify the SPD of a full-size DIMM RAM, you may need to solder the wires directly to the module.
The SPD chip on the RAM module (Kingston KVR16LS11S6/2) I’m using is an Atmel AT34C02, which can operate at a voltage range of 1.8V to 5.5V, so I don’t need to worry about damaging the chip when powering it with Arduino. The EEPROM chip has a size of 256 bytes, and some parts of it can be used to store custom data, such as bytes 150 to 175, which are “manufacturer-specific data”, and if I don’t care about XMP, I can use bytes 176 to 254.
I found that these EEPROM chips typically have a write protection feature to prevent modification of their contents. I initially thought that the RAM module manufacturer would enable this feature, but surprisingly, it was disabled! This made things much easier for me.
Using Arduino Uno, I can read the SPD, modify the necessary bytes, and write the changes back. To configure the RAM to run at 800MHz, I only need to modify a few bytes. Byte 12 is the main byte, which contains the MTB (bytes 10 and 11) multiplier of the minimum cycle time, and bytes 126 and 127 contain the CRC. By changing byte 12 from 0x0A to 0x14, I can set the maximum clock rate to 800MHz. Then, I need to recalculate the CRC using the CRC-CCITT XModem algorithm.
Output from $ dmidecode --type=17
:
$dmidecode --type=17
Memory Device
Array Handle: 0x0028
Error Information Handle: Not Provided
Total Width: 64 bits
Data Width: 64 bits
Size: 2048 MB
Form Factor: DIMM
Set: None
Locator: A1_DIMM0
Bank Locator: A1_BANK0
Type: DDR3
Type Detail: Unknown
Speed: 800 MHz <<<==========
Manufacturer: Kingston
Serial Number: 6216C9B3
Asset Tag: A1_AssetTagNum0
Part Number: 9905594-001.A00LF
Rank: 1
Configured Clock Speed: 800 MHz <<<==========
Minimum voltage: 1.350 V
Maximum voltage: 1.500 V
Configured voltage: 1.350 V
Success!
I’ve uploaded the Arduino code and other related content to a GitHub repo.
I tested the following modules:
Kingston KVR16LS11S6/2 (1600MHz) (two modules, one with a Kingston RAM chip and the other with a Nanya chip)
Kingston KVR13LS9S6/2 (1333MHz)
I noticed that the SPD configurations of the two KVR16LS11S6 1600MHz modules are slightly different; the module with the Kingston chip supports PASR (Partial Array Self-Refresh), while the Nanya module only supports ASR. The advantage of PASR is that it only refreshes part of the RAM, rather than the entire RAM, which reduces power consumption in standby mode [more info].
I also noticed that the KVR13LS9S6 1333MHz module uses the same Nanya RAM chip as the KVR16LS11S6 1600MHz module, and setting the maximum frequency to 1600MHz seems to work without any issues.
Write protection
There are typically three levels of write protection mechanisms in EEPROM chips. Please note that these levels may vary slightly between different EEPROM chips, so be sure to check the datasheet!
- Hardware Write Protection
When the WP pin is connected to VCC, all contents of the EEPROM are write-protected. This is the easiest write protection mechanism to bypass; simply disconnect the WP pin to disable write protection.
- Reversible Software Write Protection
This mechanism is a bit more difficult to bypass. Not all EEPROMs support this mode, and attempting to use this method may damage the chip! You must disconnect A2 or connect it to GND, connect A1 to VCC, and connect A0 to a 7V-10V power source, such as a 9V battery. Then, send the 0x66 command to clear the write protection setting.
- Permanent Software Write Protection
If the EEPROM is permanently write-protected, you’re out of luck. You’ll need to replace the chip.
I also discovered that the Gigabyte GA-M68M-S2P motherboard with Hypertec HYUK26412882GBOE DDR2 RAM can read and write to the SPD EEPROM using Linux Debian. I wrote some bash scripts to dump and write the SPD, which can be found in the GitHub repo.