• Overclocking Lab
  • My Arduino-based RAM SPD reader/writer (free and open source)
2020/05/13 23:27:32
Official project repository on GitHub:
Latest binaries:
Latest firmware:


Part 1 - hardware

Wire up your Arduino + DIMM according to the schematic below:

Click image to see full version. PDF can be found here
The schematic shows modules divided into sections:
  • Arduino section shows pins used on Nano V3 model. Use its 3.3V pin as a VCC source. If your Arduino doesn't have a dedicated 3.3V output, use a step-down 3.3V regulator, like LM7833. Pins labeled SA1_EN, HV_EN, and HV_FB can be assigned in "SpdReaderWriterSettings.h" file. 5V pin is required to power a 9V step-up converter (HV_SOURCE) and DDR5.
  • HV_CTL and one of 9V sources are needed to enable and detect 9V on SA0 for RSWP related operations. Use either a step-up/boost converter (HV_SOURCE), or a 9V battery (HV_SOURCE_ALT), not both!
  • SA1_CTL is used to switch pin SA1 on EEPROM between VCC and ground. This is needed because RSWP-supported EEPROMs used on DDR3 and DDR2 require different SA1 configurations to enable or disable RSWP. It is also used to prevent addressing conflicts when DDR5 is used.
Refer to your Arduino model pinout, on my Nano model SDA and SCL are A4 and A5, respectively: https://www.arduino.cc/en/reference/wire

Parts list:
  • Arduino: Nano V3 or any AVR-based model with I2C / TWI interface.
  • OK1: PC817 or any general purpose optocoupler with a maximum collector to emitter voltage above 10V and a diode current below 40mA and forward voltage below 5V.
  • R1, R2: Pull up resistor for I2C lines.
  • R3: Current limiting resistor for optocoupler diode, values between 390Ω and 560Ω work fine.
  • R4, R5: Voltage divider for HV feedback (HV_FB) on SA0. The values used will work for 5V and 3.3V Arduinos.
  • R6: Pull up resistor for SA1 pin.
  • D1: Any general purpose schottky diode.
  • C1: 6.3V, 560uF
EEPROM pins to DIMM pins guide:
DDR5:    VIN_BULK=1, HSCL=4, HSDA=5, HSA=148, PWR_EN=151, GND=6*
DDR4:    SA0=139,   SA1=140,   SA2=238,   GND=2*,   SDA=285,   SCL=141,  VDDSPD=284, WP/EVENT_n=78
DDR3:    SA0=117,   SA1=237,   SA2=119,   GND=2*,   SDA=238,   SCL=118,  VDDSPD=236
DDR2:    SA0=239,   SA1=240,   SA2=101,   GND=4*,   SDA=119,   SCL=120,  VDDSPD=238
DDR:      SA0=181,   SA1=182,   SA2=183,   GND=3*,   SDA=91,     SCL=92,    VDDSPD=184
SDRAM: SA0=165,   SA1=166,   SA2=167,   GND=1*,   SDA=82,     SCL=83,    VDDSPD=168
* There are multiple GND pins on DIMMs, the table shows just one of them for each RAM type.
DDR5 UDIMM pins locations:

DDR4 DIMM pins locations:

Obviously don't solder wires directly to DIMM contacts or motherboard solder points, get a spare slot from components resellers, like Digikey, or get a DIMM adapter, like the one pictured below. Those are cheap, and have pins labeled for easy identification.


Part 2 - Firmware

Upload firmware to your Arduino once everything is assembled. Both files are required. Make sure the values in SpdReaderWriterSettings.h match the pins names you used if your pins differ from the ones in the guide.

Part 3 - Windows GUI application

Latest application can always be found here:
The application requires .NET framework 4 or later to be installed. Supported OSs are Windows XP SP3 and up. The application is compiled for x86 architecture, so both 32 bit and 64 bit OSs are supported.
Once your device is assembled and tested, start the program. Click "Connect" toolbar button, make sure selected baud rate matches the one you programmed, select your device from the list, and click "Connect" to start working:

Click on the device identified by its serial port name and EEPROM address detected on its I2C bus to start working with it.

Part 4 - Windows Console application

The program can read and write bytes, and enable or disable write protection. The operation depends on its command line arguments. The supported arguments are:
  1. /help
  2. /find
  3. /scan <PORT>
  4. /read <PORT> <ADDRESS#> <filepath> /silent
  5. /write <PORT> <ADDRESS#> <FILEPATH> /silent
  6. /writeforce <PORT> <ADDRESS#> <FILEPATH> /silent
  7. /enablewriteprotection <PORT> <ADDRESS#>
  8. /enablewriteprotection <PORT> <ADDRESS#> <block#>
  9. /disablewriteprotection <PORT> <ADDRESS#>
  10. /enablepermanentwriteprotection <PORT> <ADDRESS#>
The parameters in CAPS are mandatory. Parameter <filepath> is optional when 
switch is used, output will be printed to console only.
You'll need to know two parameters to work your DIMM - port name and EEPROM address. To find out what port your device is using, run this command:
spdrwcli.exe /find

If everything works, you'll get a port name your device is connected to:

Found Device on Serial Port: COM3

In my case it is COM3. If the program can't find any devices, make sure the sketch is properly uploaded to your Arduino and no other application is using your Arduino's serial port - that includes Arduino IDE Serial Monitor. Close it, if you have it open.
With a DDR4 DIMM present you'll want to scan your device for EEPROM addresses on I2C bus. With SA0-SA2 connected to ground, the address will be 80, however, it is still recommended to run this test to ensure the EEPROM is detected:
spdrwcli.exe /scan COM3

In most cases, if everything is fine, you'll see a device at address 80 detected:

Found EEPROM at address: 80

When I used DDR4 slots cut out of an old motherboard with both DIMMs populated, both were detected at addresses 80 and 82, even though I soldered wires to one slot only:

Found EEPROM at address: 80
Found EEPROM at address: 82

To read SPD from your DIMM, run:
spdrwcli.exe /read COM3 80

This will output SPD contents in HEX on screen. Plain and simple.

Reading EEPROM at address 80

000: 23 10 0C 02 84 19 00 08 00 00 00 03 01 03 00 00
010: 00 00 08 0C F4 1B 00 00 6C 6C 6C 11 00 74 20 08
020: 00 05 70 03 00 A8 1E 2B 2B 00 00 00 00 00 00 00
--edited on purpose--
1D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Read 512 bytes from EEPROM at address 80 on port COM3 in 1547 ms

To save SPD to file, add a file path at the end. File path can be absolute or relative. If file path has spaces in it, enclose it in "double quotes":
spdrwcli.exe /read COM3 80 C:\temp\spdoutput.bin

spdrwcli.exe /read COM3 80 "C:\temp\my ddr4 spd file.bin"


Reading EEPROM at address 80 to C:\temp\my ddr4 spd file.bin

000: 23 10 0C 02 84 19 00 08 00 00 00 03 01 03 00 00
--edited on purpose--
1F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Read 512 bytes from EEPROM at address 80 on port COM3 in 1562 ms to file "C:\temp\my ddr4 spd file.bin"

Edit your SPD in a HEX editor or any specialized program, like Thaiphoon Burner. 
To write your new SPD to DIMM, run:
spdrwcli.exe /write COM3 80 C:\temp\newspd.bin

EEPROMs have limited number of write cycles, sometimes between 10,000 and 1,000,000. By default, the program writes bytes only if they differ from existing values. By doing that, it first reads a byte and if it matches the input value, the write operation is skipped. Byte is written only it it needs to. If you want to force writes, regardless of existing SPD data, use 
switch instead of
It will take a bit longer (5-10 s.) because of required delays after each write.

Writing "C:\temp\my ddr4 spd file.bin" (512 bytes) to EEPROM at address 80

000: 23 10 0C 02 84 19 00 08 00 00 00 03 01 03 00 00
010: 00 00 08 0C F4 1B 00 00 6C 6C 6C 11 00 74 20 08
020: 00 05 70 03 00 A8 1E 2B 2B 00 00 00 00 00 00 00
--edited on purpose--
1D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Written 512 bytes to EEPROM at address 80 on port COM3 in 7093 ms

Optionally, to hide progress you may add 
switch at the end after a file path is specified when using
, or
spdrwcli.exe /read COM3 80 C:\temp\spdoutput.bin /silent

spdrwcli.exe /write COM3 80 C:\temp\newspd.bin /silent

You may protect the entire EEPROM from writing with this command:
spdrwcli.exe /enablewriteprotection COM3

If the blocks weren't write-protected previously, you'll receive a response like this:

Block 0 is now read-only
Block 1 is now read-only
Block 2 is now read-only
Block 3 is now read-only

Or enable write protection on each individual block by specifying block number (0-3):
spdrwcli.exe /enablewriteprotection COM3 0

spdrwcli.exe /enablewriteprotection COM3 2

Each block specifies a section of 128 bytes. Block 0 is bytes at offset 0-127, block 1 is bytes 128-255, and so on.
Please note the address isn't used, only port number. That's because RSWP commands don't use addresses, all devices on the I2C bus will act simultaneously.
To clear write protection, run the following command:
spdrwcli.exe /disablewriteprotection COM3

Again, the device address isn't used.
Once write-protection is cleared, you'll see this response:

Write protection successfully disabled.

To enable permanent write protection, run this command:
spdrwcli.exe /enablepermanentwriteprotection COM3 80

Please note permanent protection can be enabled on most DDR3 and some DDR2 modules. DDR4 does not support permanent software write protection.
That's all for now.
I'm always open to suggestions and comments. Feel free to report any bugs or ask to add any feature you'd like to see in the project, whether it's related to hardware, firmware, or applications.
2020/05/14 00:10:21
I just started with Arduino and I am very interested in you project.
2020/05/15 03:06:16
Almost there, everything is working properly now.


2020/05/15 07:02:49
Cool GTX
sounds interesting
can damaged/ corrupted SPD code be overwritten -- Recovered ? 

Cause you know someone will OC it too far 
2020/05/15 22:19:07
Look what came in the mail today after weeks months of waiting. Now I no longer have to use slots I cut out of a dead motherboard.

Cool GTX
sounds interesting
can damaged/ corrupted SPD code be overwritten -- Recovered ? 

Cause you know someone will OC it too far

Yes, provided you have a backup of correct SPD data, or a working DIMM.
I'll aslo implement a CRC checker into the Windows app. In won't be accessible via command line switches initially, but rather by calling a function directly.

Edit: Arduino sketch is posted, link is in the first post.
2020/05/16 10:55:55
Nice work. You can just buy slots from component retailers such as Digikey.
I've did this back in 2014 for internal use and testing at EVGA R&D lab :)
My box designed for onboard MCU or couples with Raspberry Pi. Worklog and code : https://xdevs.com/article/ddr4s/
Beware, many SPDs on memories are actually write-protected, so you can read them, but cannot write/modify. :)
2020/05/16 14:41:47
Thanks. So far I haven't encountered any write protected EEPROMs, in fact the DIMMs I used in my tests use EEPROMs which lack permanent write protection capabilities.
I'll start working on software write protection enabling/disabling capabilities soon. It will require a couple more components added to the circuit and an additional 9V source.
The project is complete. Windows binary and sources + updated Arduino sketch are up.
Links are in the first post.
2020/05/20 00:49:17
I got some bad news and (as a result) good news.
Today I finally stumbled upon a kit of G.Skill DIMMs with write-protected EEPROMs, which can be read, but can't be modified, unless write protection is reversed.
But that's history now, I just wanted to announce I implemented a software reversible write protection clearing function in my project.
It can't be implemented with software alone as it requires an update to both hardware and software, since it requires an additional external DC power source from 7V-10V, which Arduino alone isn't capable of providing, so stay tuned for updated schematics and code.

2020/05/22 04:24:54
Update: 22.05.2020 - now with write protection reversing and enabling options 
Updated schematics, description, firmware, binary, and sources are in the first post.
2020/05/24 01:29:34
May 24 update - fixed an issue where bytes were not written to higher half of EEPROM, if the second half was identical to the first half when using "/write" switch.
Updated links are in the first post, as usual.
PS: Aslo, because in 2020 RGB has to be everywhere, I added a color byte display.
It's enabled by default, to disable it, set "color" boolean parameter to "false" where DisplayByte() function is called.

Random bytes written to show as many colors as possible:


Use My Existing Forum Account

Use My Social Media Account