top of page
Writer's pictureAngelo Jacobo

SPD Reader for UberDDR3 - Post #11

Ever wanted to know the exact details of your DDR3 memory module? In this post, we’ll look at how an SPD (Serial Presence Detect) reader works to get important configuration data straight from your DDR3 DIMM.


In the last post, we talked about dual-rank support for UberDDR3. Now, let’s move on to the new SPD reader feature in UberDDR3. This feature allows you to read the SPD data from the DDR3 DIMM to correctly set up the top-level parameters of UberDDR3.


Table of Contents:


I. What is Serial Presence Detect (SPD)?

When you turn on a computer, it runs a quick check called the Power-On Self-Test (POST) to ensure everything is working properly. Part of this process involves identifying the installed memory and configuring it for efficient use. This is where Serial Presence Detect (SPD) comes in—it’s a feature in memory modules that allows the computer to automatically read important details like memory type and timing settings. (source here)


Serial Presence Detect (SPD) is implemented as an EEPROM (Electrically Erasable Programmable Read-Only Memory) chip embedded in DDR DIMMs. This chip stores essential configuration data that informs the system how to use the memory module efficiently and correctly when it is installed.

For a quick hands-on experience, you can actually read the SPD contents of the DIMM in your computer. On my Ubuntu system, I followed a simple guide that involved running the following commands:

  • $ sudo apt install i2c-tools

  • $ sudo modprobe eeprom

  • $ sudo modprobe i2c-i801

  • $ sudo decode-dimms

After running decode-dimms, I got the following output:


The information that most people are likely familiar with includes:

  • Memory Type: DDR4 SDRAM, a widely used type of memory.

  • Size: The memory module has a capacity of 16 GB (16384 MB).

  • Speed: This memory operates at a maximum speed of 3200 MHz (PC4-25600).

  • Module Type: It is a SO-DIMM module, commonly found in laptops and compact devices.

However, from the perspective of the DDR3 controller, nearly every detail provided in this data is essential.


For the purpose of this blog, we will focus on creating an SPD reader for UberDDR3. The aim is to read the SPD of a DDR3 DIMM and extract the necessary information that UberDDR3 requires to be properly configured and function optimally with the intended DDR3 module.


I.I How does SPD work?

The SPD (Serial Presence Detect) EEPROM firmware is accessed via SMBus, which is a variant of the I2C protocol. This means that, logically, any I2C master IP can communicate with the SPD. For example, in a typical DDR3 SO-DIMM datasheet as shown below (retrieved here), the SPD EEPROM includes SCL (clock) and SDA (data) pins for I2C communication.


Below is an image of the DDR3 SO-DIMM I used in my blog post demonstration of dual-rank support for UberDDR3. The small IC highlighted is the SPD EEPROM:

Since communication with the SPD is essentially I2C, I won’t delve deeply into how I2C works, but if you're unfamiliar, here's a great resource from Texas Instruments: I2C Bus Overview.

I.II Which Bytes Should We Read from SPD?

Now that we know how to access the SPD via I2C, the next question is: Which byte addresses are important for UberDDR3? SPD contains standardized data, defined by JEDEC, which is critical for configuring DDR3 modules. You can find the full JEDEC SPD specification here: JEDEC SPD Specification.

Below is the address map for DDR3 SPD, as described in the JEDEC specification (highlighted are the bytes needed by UberDDR3):


For the UberDDR3 SPD reader, we’ll focus on reading the following critical bytes: Sanity Checks (Not used directly by UberDDR3 but useful for validation):

  • Byte 1 (SPD Revision): Ensures compatibility. For most DDR3 SO-DIMMs, this should be greater than Revision 1.1 (e.g., 0x11).

  • Byte 2 (DRAM Device Type): Confirms the memory type is DDR3 SDRAM. This should read as 0x0B.

  • Byte 3 (Module Type): Validates the module type. For SO-DIMM DDR3, this should read as 0x03.

Key Parameters for UberDDR3 Configuration:

  1. Byte 4 (SDRAM Density and Banks):

    • Determines the SDRAM_CAPACITY and BA_BITS parameters.

    • Example: For an 8GB dual-rank SO-DIMM, BA_BITS = 0x3 (8 banks), and SDRAM_CAPACITY = 0x4 (4Gb).

  2. Byte 5 (SDRAM Addressing):

    • Determines ROW_BITS and COL_BITS parameters for UberDDR3.

  3. Byte 7 (Module Organization):

    • Determines whether the DUAL_RANK_DIMM parameter is enabled. (2 Ranks = Dual Rank).

  4. Byte 8 (Module Memory Bus Width):

    • Maps to the BYTE_LANES parameter. For instance:

      • 8 bits = 1 byte lane

      • 16 bits = 2 byte lanes

      • 32 bits = 4 byte lanes

      • 64 bits = 8 byte lanes

  5. Bytes 10–11 (Medium Timebase - MTB):

    • This is not a parameter in UberDDR3, but these bytes define the timebase for medium-grain timing calculations for the succeeding timing parameters.

    • Example: MTB = Dividend ÷ Divisor, commonly resulting in 0.125 ns.

  6. Timing Parameters (Scaled by MTB):

    • Byte 18 (tRCD): Determines the TRCD parameter.

    • Byte 20 (tRP): Determines the TRP parameter.

    • Bytes 21–22 (tRAS): Determines the TRAS parameter.


II. SPD Reader Implementation

To implement the SPD reader, let’s jump straight into the architecture, as illustrated below:


Now, let’s break down the design and explain each block in detail:

  • I2C Master = This is the I2C Master IP responsible for communicating with the SPD EEPROM on the DDR3 DIMM. For this project, I used the open-source I2C Master design available on GitHub: Tiny But Mighty I2C Master Verilog. The author posted a blog for this documentation.


  • UART TX = This is the UART transmit IP, which sends the SPD data summary as text to the computer’s UART terminal. Since the SPD reader only needs to transmit data (not receive commands), there’s no need for a UART RX module. I used another GitHub project for this, found here: ben-marshall/uart.


  • UART Serializer FSM = This is a finite state machine designed to serialize text data and send it to the UART TX module one character at a time. This enables the transmission of complete text messages, such as "Device Type: DDR3", by breaking them into individual characters (e.g., "D", "e", "v", and so on).


  • I2C Byte Reader FSM = This FSM is the core component responsible for reading bytes from the SPD via the I2C Master. It issues I2C read commands, processes the received byte, and triggers the UART Serializer FSM to transmit a corresponding text description.


  • I2C Address Finder FSM = This FSM ensures the SPD reader is adaptable to changes in the SPD's I2C address. While most SPD EEPROMs default to 0x50, this FSM systematically scans all possible I2C addresses, issuing read commands until it finds the correct one by receiving an acknowledgment (ACK).


Let us now elaborate on each state machines:


I2C Address Finder FSM

This FSM works as follows:

  1. In the READ_ADDRESS state, it sends a read command to the current i2c_address.

  2. If no acknowledgment (ACK) is received, the FSM increments i2c_address and retries.

  3. When an ACK is received, the correct I2C address is identified, and the FSM permanently asserts find_i2c_address_done.




I2C Byte Reader FSM

Once the correct I2C address is found (find_i2c_address_done is asserted), this FSM begins its operation:

  1. In the IDLE state, it waits for the assertion of find_i2c_address_done.

  2. In the READ_BYTE state, it issues a read command to the SPD register specified by byte_address.

  3. If an ACK is received, the FSM asserts uart_start_send and sends text to the UART Serializer FSM for transmission.

  4. The transmitted text depends on the byte value. For example, if the byte corresponds to the DRAM Device Type, the UART sends "Device Type: DDR3".



UART Serializer FSM

This FSM is triggered when uart_start_send is asserted by the I2C Byte Reader FSM:

  1. It transitions to the SEND_BYTE state and starts sending characters to the UART TX module, one at a time.

  2. The FSM uses an index (uart_text_length_index) to track the current character being serialized.

  3. Characters are transmitted sequentially until the entire text string is sent. For example, if the text is "Device Type: DDR3", the FSM transmits "D", then "e", "v", and so on until it reaches "3".



III. Simulation Testbench

Now, let’s test the SPD reader through simulation. Follow these steps to get started:

1. First, clone the UberDDR3 repository using the following command:


2. Create a new Vivado project:

  • Open Vivado and create a new project.

  • Add the design directory rtl/ to the project as your design source.


3. Add the testbench/ directory to the project as your simulation source.


4. Under Simulation Sources, set spd_reader_tb as the top-level module for the testbench.


5. Add the simulation waveform configuration file located at testbench/spd_tb/spd_reader_tb_behav.wcfg:


6. Run the Simulation

  • Click Run Simulation in Vivado.

  • Select Run All to execute the simulation.


That’s it! The testbench should now be running.


III.I Waveform Analysis

1. I2C Address Finder

The first stage of the simulation tests the I2C Address Finder FSM.

  • As shown in the waveform below, the i2c_address starts at 1 (address is skipped since it’s reserved for general calls).

  • The i2c_address increments until the I2C slave acknowledges. In this simulation, the slave address is set to 0x04 for shorter simulation time (the actual SPD address is typically 0x50).

  • Notice how the i2c_address stops incrementing at 0x04 and the find_i2c_address_done signal is asserted. This indicates that the correct I2C address has been successfully identified.


2. SPD Register Reading

After the I2C address is found, the SPD reader begins reading individual SPD registers and transmitting a text report contents via UART.

  • For example (shown below), when byte_address 2 is read, the SPD data corresponds to "DRAM Type: DDR3 SDRAM", which is sent to the UART terminal.

  • Similarly, when byte_address 3 is read, the text "Module Type: SO-DIMM" is transmitted


And there you go, by running this simple simulation, we’ve validated the SPD reader’s ability to locate the I2C slave address, read SPD registers, and output the SPD report over UART.


IV. Project Demonstration

As is tradition in our blog series, we’ll wrap up with a project demonstration. For this SPD Reader, we’re using the AX7325B FPGA board from ALINX. This board includes a SODIMM slot that supports up to 8GB of DDR3 memory, making it perfect for this project:


For the memory, I’m using a Hynix 8GB 2Rx8 DDR3 SODIMM module:


Steps for Implementation

1. Set Up the Design

2. Add the Constraint File

  • Include the constraint file for the AX7325B FPGA board, located at example_demo/alinx_ax7325b/ax7325b_spd_reader.xdc

3. Generate the Bitstream

  • Run the synthesis-to-bitstream flow in Vivado.

  • Program the generated bitstream file onto the AX7325B FPGA board.

4. Connect the UART Terminal

  • Connect the USB cable to enable UART communication.

  • Press key1 to reset on the FPGA board.


Once the system is up and running, you should see the following output on the UART terminal:


IV.I Understanding the SPD Report

Let’s break down the output to understand each line:

  • I2C Address: 0x50 – The SPD I2C address is 0x50, the standard address for most DDR3 SODIMMs.

  • SPD Revision: 1.1 – Indicates the SPD uses the JEDEC SPD version 1.1 specification, common in modern DDR3 modules.

  • DRAM Type: DDR3 SDRAM – Confirms the SPD data correctly identifies the DRAM type as DDR3.

  • Module Type: SO-DIMM – Verifies the module type is SO-DIMM, matching the physical memory used in the demonstration.


The following parameters are extracted from the SPD and should be used to configure the top-level module of UberDDR3:

Parameter

Value

Explanation

BA_BITS

3

Bank Address width of the DDR3 memory.

SDRAM_CAPACITY

4

Capacity of each DRAM chip (in gigabytes).

ROW_BITS

16

Number of row address bits.

COL_BITS

10

Number of column address bits.

DUAL_RANK_DIMM

1

Indicates the module has dual ranks.

BYTE_LANES

8

Number of byte lanes (width of the memory bus).

mtb

0x7D (125 ps)

Medium Timebase, used for timing calculations.

TRCD

1325

Row-to-column delay: mtb * 0x69 (125 × 105).

TRP

1325

Row precharge time: mtb * 0x69 (125 × 105).

TRAS

35000

Row active time: mtb * 0x118 (125 × 280).

These information retrieved from SPD should be used when configuring the top-level parameters of UberDDR3.


V. Conclusion

The new SPD reader feature in UberDDR3 provides a practical way to access critical DDR3 configuration details directly from your memory module. By leveraging the I2C protocol to read SPD data, this SPD reader will help the user to properly configure UberDDR3 for optimal performance. Whether you're debugging or fine-tuning your design, the SPD reader ensures UberDDR3 is correctly aligned with the module's specifications.


That wraps up this post. Catch you in the next blog post!

27 views

Comments


Computer Processor

Subscribe to Our Newsletter

Thanks for submitting!

bottom of page