* [PATCH v2 1/6] decode-dimms: Implement DDR5 checksum parsing
2025-11-06 2:14 [PATCH v2 0/6] decode-dimms: Implement DDR5 decoding Stephen Horvath
@ 2025-11-06 2:14 ` Stephen Horvath
2025-11-06 2:14 ` [PATCH v2 2/6] decode-dimms: Decode DDR5 Manufacturer Data Stephen Horvath
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Stephen Horvath @ 2025-11-06 2:14 UTC (permalink / raw)
To: Jean Delvare, linux-i2c@vger.kernel.org
Cc: Guenter Roeck, Kamil Aronowski, Stephen Horvath
The CRC for the DDR5 SPD is located at bytes 510-511 of the SPD data.
There is now functionality to read upto byte 512 and calculate the CRC16
if byte 2 indicates DDR5 memory and byte 0 indicates at least 512 bytes.
This patch is based off of a patch by Guenter Roeck, linked below.
Link: https://lore.kernel.org/linux-hwmon/efb77b37-30e5-48a8-b4af-eb9995a2882b@roeck-us.net/
Cc: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Stephen Horvath <s.horvath@outlook.com.au>
---
eeprom/decode-dimms | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/eeprom/decode-dimms b/eeprom/decode-dimms
index 32e840a..baf85e0 100755
--- a/eeprom/decode-dimms
+++ b/eeprom/decode-dimms
@@ -2402,7 +2402,12 @@ sub spd_sizes($)
my $bytes = shift;
my $type = $bytes->[2];
- if ($type == 12 || $type == 14 || $type == 16 || $type == 17) {
+ if ($type == 18 || $type == 19 || $type == 20 || $type == 21) {
+ # DDR5
+ my $spd_len = 256 * ((($bytes->[0] >> 4) & 7) + 1);
+ my $used = $spd_len;
+ return ($spd_len, $used);
+ } elsif ($type == 12 || $type == 14 || $type == 16 || $type == 17) {
# DDR4
my $spd_len = 256 * (($bytes->[0] >> 4) & 7);
my $used = 128 * ($bytes->[0] & 15);
@@ -2511,10 +2516,16 @@ sub calculate_crc($$$)
sub check_crc($)
{
my $bytes = shift;
+ my $is_ddr5 = ($bytes->[0] & 0x70) == 0x30;
my $crc_cover = $bytes->[0] & 0x80 ? 116 : 125;
+ my $crc_start = 126;
+ if ($is_ddr5) {
+ $crc_cover = 509;
+ $crc_start = 510;
+ }
my $crc = calculate_crc($bytes, 0, $crc_cover + 1);
- my $dimm_crc = ($bytes->[127] << 8) | $bytes->[126];
+ my $dimm_crc = ($bytes->[$crc_start + 1] << 8) | $bytes->[$crc_start];
return ("EEPROM CRC of bytes 0-$crc_cover",
($dimm_crc == $crc) ? 1 : 0,
sprintf("0x%04X", $dimm_crc),
@@ -2617,7 +2628,8 @@ sub get_dimm_list
if ($use_sysfs) {
@drivers = ('eeprom',
'at24',
- 'ee1004'); # DDR4
+ 'ee1004', # DDR4
+ 'spd5118'); # DDR5
} else {
@drivers = ('eeprom');
$dir = '/proc/sys/dev/sensors';
@@ -2642,7 +2654,8 @@ sub get_dimm_list
next unless defined $attr &&
($attr eq "eeprom" ||
$attr eq "spd" ||
- $attr eq "ee1004"); # DDR4
+ $attr eq "ee1004" || # DDR4
+ $attr eq "spd5118"); # DDR5
} else {
next unless $file =~ /^eeprom-/;
}
@@ -2684,6 +2697,12 @@ for my $i (0 .. $#dimm) {
$dimm[$i]->{chk_spd}, $dimm[$i]->{chk_calc}) =
checksum(\@bytes);
} else {
+
+ # Check for DDR5 protocol type 18, 19, 20, 21 and size >= 512
+ if ($bytes[2] >= 18 && $bytes[2] <= 21 && ($bytes[0] & 0x70) >= 0x30) {
+ # DDR5's checksum is at 510-511
+ push(@bytes, readspd(@bytes, 512, $dimm[$i]->{file}));
+ }
($dimm[$i]->{chk_label}, $dimm[$i]->{chk_valid},
$dimm[$i]->{chk_spd}, $dimm[$i]->{chk_calc}) =
check_crc(\@bytes);
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 2/6] decode-dimms: Decode DDR5 Manufacturer Data
2025-11-06 2:14 [PATCH v2 0/6] decode-dimms: Implement DDR5 decoding Stephen Horvath
2025-11-06 2:14 ` [PATCH v2 1/6] decode-dimms: Implement DDR5 checksum parsing Stephen Horvath
@ 2025-11-06 2:14 ` Stephen Horvath
2025-11-06 2:15 ` [PATCH v2 4/6] decode-dimms: Decode DDR5 common module information Stephen Horvath
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Stephen Horvath @ 2025-11-06 2:14 UTC (permalink / raw)
To: Jean Delvare, linux-i2c@vger.kernel.org
Cc: Guenter Roeck, Kamil Aronowski, Stephen Horvath
Decode the DRAM's manufacturer data for DDR5.
There are more manufacturers in the common and type specific sections,
but only the 'Manufacturing Information' section of the eeprom has been
implemented.
Signed-off-by: Stephen Horvath <s.horvath@outlook.com.au>
---
eeprom/decode-dimms | 51 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/eeprom/decode-dimms b/eeprom/decode-dimms
index baf85e0..9979259 100755
--- a/eeprom/decode-dimms
+++ b/eeprom/decode-dimms
@@ -2278,6 +2278,48 @@ sub decode_ddr4_mfg_data($)
sprintf("0x%02X", $bytes->[349]));
}
+# Parameter: EEPROM bytes 0-639 (using 512-554)
+sub decode_ddr5_mfg_data($)
+{
+ my $bytes = shift;
+
+ prints("Manufacturer Data");
+
+ printl("Module Manufacturer",
+ manufacturer_ddr3($bytes->[512], $bytes->[513]));
+
+ printl_cond(spd_written(@{$bytes}[552..553]),
+ "DRAM Manufacturer",
+ manufacturer_ddr3($bytes->[552], $bytes->[553]));
+
+ printl_mfg_location_code($bytes->[514]);
+
+ printl_cond(spd_written(@{$bytes}[515..516]),
+ "Manufacturing Date",
+ manufacture_date($bytes->[515], $bytes->[516]));
+
+ printl_mfg_assembly_serial(@{$bytes}[517..520]);
+
+ printl("Part Number", part_number(@{$bytes}[521..550]));
+
+ printl_cond(spd_written(@{$bytes}[551]),
+ "Revision Code",
+ sprintf("0x%02X", $bytes->[551]));
+
+ if ($bytes->[554] != 0xff) {
+ # DRAM Stepping may be a number or an uppercase ASCII letter
+ # 0x00-0xfe is valid, 0xff is invalid
+ my $stepping = $bytes->[554];
+ if ($stepping < 0x41 || $stepping > 0x5a) {
+ printl("DRAM Stepping",
+ sprintf("0x%02X", $stepping));
+ } else {
+ printl("DRAM Stepping",
+ sprintf("%c", $stepping));
+ }
+ }
+}
+
# Parameter: EEPROM bytes 0-127 (using 64-98)
sub decode_manufacturing_information($)
{
@@ -2831,6 +2873,15 @@ for $current (0 .. $#dimm) {
} elsif (!$use_hexdump && $dimm[$current]->{driver} ne "ee1004") {
print STDERR "HINT: You should be using the ee1004 driver instead of the $dimm[$current]->{driver} driver\n";
}
+ } elsif ($type eq "DDR5 SDRAM" ||
+ $type eq "LPDDR5 SDRAM" ||
+ $type eq "DDR5 NVDIMM-P" ||
+ $type eq "LPDDR5X SDRAM") {
+ if (@bytes >= 640) {
+ # Decode DDR5-specific manufacturing data in bytes
+ # 512-639
+ decode_ddr5_mfg_data(\@bytes);
+ }
} else {
# Decode next 35 bytes (64-98, common to most
# memory types)
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 4/6] decode-dimms: Decode DDR5 common module information
2025-11-06 2:14 [PATCH v2 0/6] decode-dimms: Implement DDR5 decoding Stephen Horvath
2025-11-06 2:14 ` [PATCH v2 1/6] decode-dimms: Implement DDR5 checksum parsing Stephen Horvath
2025-11-06 2:14 ` [PATCH v2 2/6] decode-dimms: Decode DDR5 Manufacturer Data Stephen Horvath
@ 2025-11-06 2:15 ` Stephen Horvath
2025-11-06 2:15 ` [PATCH v2 3/6] decode-dimms: Decode timings and other data for DDR5 Stephen Horvath
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Stephen Horvath @ 2025-11-06 2:15 UTC (permalink / raw)
To: Jean Delvare, linux-i2c@vger.kernel.org
Cc: Guenter Roeck, Kamil Aronowski, Stephen Horvath
Decode the extra manufacturer information that
was mentioned in a previous patch, but only the
info this is common between all DDR5 memory
modules.
The physical size is now implemented too.
Signed-off-by: Stephen Horvath <s.horvath@outlook.com.au>
---
eeprom/decode-dimms | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/eeprom/decode-dimms b/eeprom/decode-dimms
index a5fba1e..4fd4531 100755
--- a/eeprom/decode-dimms
+++ b/eeprom/decode-dimms
@@ -2350,6 +2350,42 @@ sub decode_ddr5_sdram($)
printl("Thermal Sensor",
$bytes->[14] & 0x08 ? "Supported" : "No");
+
+# common module information
+ prints("Common Information");
+
+ my $spd_info_rev = $bytes->[192];
+ printl("SPD Module Info Revision", ($spd_info_rev >> 4) . "." . ($spd_info_rev & 0xf));
+
+ printl_cond($bytes->[196] & 0x80,
+ "SPD Manufacturer",
+ manufacturer_ddr3($bytes->[194], $bytes->[195]));
+ printl_cond($bytes->[200] & 0x80,
+ "PMIC0 Manufacturer",
+ manufacturer_ddr3($bytes->[198], $bytes->[199]));
+ printl_cond($bytes->[204] & 0x80,
+ "PMIC1 Manufacturer",
+ manufacturer_ddr3($bytes->[202], $bytes->[203]));
+ printl_cond($bytes->[208] & 0x80,
+ "PMIC2 Manufacturer",
+ manufacturer_ddr3($bytes->[206], $bytes->[207]));
+ printl_cond($bytes->[212] & 0xC0,
+ "Thermal Sensors Manufacturer",
+ manufacturer_ddr3($bytes->[210], $bytes->[211]));
+
+ prints("Physical Characteristics");
+
+ my $height = $bytes->[230] & 0x1f;
+ printl("Module Height",
+ $height == 0x00 ? "15 mm or less" :
+ $height == 0x1f ? "more than 45 mm" :
+ sprintf("%u mm", $height + 15));
+ printl("Module Thickness",
+ sprintf("%d mm front, %d mm back",
+ ($bytes->[231] & 0x0f) + 1,
+ (($bytes->[231] >> 4) & 15) + 1));
+ printl("Module Reference Card",
+ ddr3_reference_card($bytes->[232], $bytes->[232]));
}
# Parameter: EEPROM bytes 0-127 (using 4-5)
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 3/6] decode-dimms: Decode timings and other data for DDR5
2025-11-06 2:14 [PATCH v2 0/6] decode-dimms: Implement DDR5 decoding Stephen Horvath
` (2 preceding siblings ...)
2025-11-06 2:15 ` [PATCH v2 4/6] decode-dimms: Decode DDR5 common module information Stephen Horvath
@ 2025-11-06 2:15 ` Stephen Horvath
2025-11-06 2:15 ` [PATCH v2 5/6] decode-dimms: Add basic decoding of type specific information " Stephen Horvath
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Stephen Horvath @ 2025-11-06 2:15 UTC (permalink / raw)
To: Jean Delvare, linux-i2c@vger.kernel.org
Cc: Guenter Roeck, Kamil Aronowski, Stephen Horvath
Decode size, timings, and other data for DDR5. The values in my BIOS do
seem to match up with those here, so I think this is correct.
There are also more timings that I'd like to add in the future.
Signed-off-by: Stephen Horvath <s.horvath@outlook.com.au>
---
eeprom/decode-dimms | 230 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 230 insertions(+)
diff --git a/eeprom/decode-dimms b/eeprom/decode-dimms
index 9979259..a5fba1e 100755
--- a/eeprom/decode-dimms
+++ b/eeprom/decode-dimms
@@ -2127,6 +2127,231 @@ sub decode_ddr4_sdram($)
}
}
+# DDR5 Rounding Algorithm
+sub ddr5_round($$)
+{
+ my ($tck, $twr) = @_;
+ my $correction = 3; # 0.30% per the rounding algorithm
+ my $new_twr = $twr * (1000 - $correction);
+ $tck = ($new_twr / $tck) + 1000;
+
+ return $twr / int($tck / 1000);
+}
+
+# Return combined time in ns
+sub ddr5_ns($$)
+{
+ my ($bytes, $index) = @_;
+
+ return (($bytes->[$index + 1] << 8) | $bytes->[$index]) / 1000;
+}
+
+# Parameter: EEPROM bytes 0-639 (using 1-255)
+sub decode_ddr5_sdram($)
+{
+ my $bytes = shift;
+ my ($ctime, $ctime_max);
+ my $ii;
+
+ my @module_types = (
+ { type => "Reserved (0x00)", },
+ { type => "RDIMM", },
+ { type => "UDIMM", },
+ { type => "SODIMM", },
+ { type => "LRDIMM", },
+ { type => "CUDIMM", },
+ { type => "CSOUDIMM", },
+ { type => "MRDIMM", },
+ { type => "CAMM2", },
+ { type => "Reserved (0x09)", },
+ { type => "DDIMM", },
+ { type => "Solder down", },
+ { type => "Reserved (0x0C)", },
+ { type => "Reserved (0x0D)", },
+ { type => "Reserved (0x0E)", },
+ { type => "Reserved (0x0F)", },
+ );
+
+# SPD revision
+ printl("SPD Revision", ($bytes->[1] >> 4) . "." . ($bytes->[1] & 0xf));
+
+ my $raw_type = $bytes->[3];
+ my $type = $raw_type & 0x0f;
+ printl("Module Type", $module_types[$type]->{type});
+
+# time bases
+ if (($bytes->[19] & 0x03) != 0x00 || ($bytes->[19] & 0xc0) != 0x00) {
+ print STDERR "Unknown time base values, can't decode\n";
+ return;
+ }
+
+ my $twr = ddr5_ns($bytes, 40);
+
+# speed
+ prints("Memory Characteristics");
+
+ $ctime = ddr5_ns($bytes, 20);
+ $ctime = ddr5_round($ctime, $twr);
+ $ctime_max = ddr5_ns($bytes, 22);
+ $ctime_max = ddr5_round($ctime_max, $twr);
+
+ my $ddrclk = 2 * (1000 / $ctime);
+ my $tbits = 8 << ($bytes->[235] & 7);
+ my $pcclk = int ($ddrclk * $tbits / 8);
+ # Round down to comply with Jedec
+ $pcclk = $pcclk - ($pcclk % 100);
+ $ddrclk = int ($ddrclk);
+ printl("Maximum module speed", "$ddrclk MT/s (PC5-${pcclk})");
+
+# Size computation
+ my $rank_mix = $bytes->[234] & 0x40;
+ my $sdram_width0 = 4 << (($bytes->[6] >> 5) & 0x07);
+ my $sdram_width1 = 4 << (($bytes->[10] >> 5) & 0x07);
+ my $bus_width = 8 << ($bytes->[235] & 0x07);
+ my $ranks = (($bytes->[234] >> 3) & 0x07) + 1;
+ my $subchannels = 1 << (($bytes->[235] >> 5) & 0x07);
+
+ my $die_count0 = (($bytes->[4] >> 5) & 0x07) + 1;
+ my $die_3ds0 = $die_count0 > 2;
+ if ($die_3ds0) { $die_count0 >>= 1; }
+
+ my $die_count1 = (($bytes->[8] >> 5) & 0x07) + 1;
+ my $die_3ds1 = $die_count1 > 2;
+ if ($die_3ds1) { $die_count1 >>= 1; }
+
+ my $die_count = $die_count0 + $die_count1;
+ my $density0 = ($bytes->[4] & 0x1f) * 4;
+ my $density1 = ($bytes->[8] & 0x1f) * 4;
+
+ my $cap0 = $subchannels * ($bus_width / $sdram_width0) * $die_count0 * ($density0 / 8) * $ranks;
+ my $cap1 = $subchannels * ($bus_width / $sdram_width1) * $die_count1 * ($density1 / 8) * $ranks;
+ my $cap = $cap0 + $cap1;
+
+ printl("Size", $cap . " GB");
+
+ printl("Banks x Rows x Columns x Bits" . ($rank_mix ? " (Even Rank)" : ""),
+ join(' x ', (1 << (($bytes->[7] >> 5) & 0x07)) * (1 << ($bytes->[7] & 0x07)),
+ (( $bytes->[5] & 0x1f) + 16),
+ ((($bytes->[5] >> 5) & 0x05) + 10),
+ (8 << ($bytes->[235] & 0x07))));
+
+ printl_cond($rank_mix, "Banks x Rows x Columns x Bits (Odd Rank)",
+ join(' x ', (1 << (($bytes->[11] >> 5) & 0x07)) * (1 << ($bytes->[11] & 0x07)),
+ (( $bytes->[9] & 0x1f) + 16),
+ ((($bytes->[9] >> 5) & 0x05) + 10),
+ (8 << ($bytes->[235] & 0x07))));
+
+ printl("SDRAM Device Width" . ($rank_mix ? " (Even Rank)" : ""), "$sdram_width0 bits");
+ printl_cond($rank_mix, "SDRAM Device Width (Odd Rank)", "$sdram_width1 bits");
+
+ printl("Ranks", $ranks);
+ printl_cond($ranks > 1, "Rank Mix",
+ $rank_mix ? "Asymmetrical" : "Symmetrical");
+ printl("Primary Bus Width", (8 << ($bytes->[235] & 7))." bits");
+ printl_cond($bytes->[235] & 0x18, "Bus Width Extension", (($bytes->[235] & 0x18) >> 1) ." bits");
+
+ my $taa;
+ my $trcd;
+ my $trp;
+ my $tras;
+
+ $taa = ddr5_ns($bytes, 30);
+ $trcd = ddr5_ns($bytes, 32);
+ $trp = ddr5_ns($bytes, 34);
+ $tras = ddr5_ns($bytes, 36);
+
+ printl("AA-RCD-RP-RAS (cycles)",
+ ddr4_core_timings(ceil(($taa * 997 / $ctime + 1000) / 1000),
+ $ctime, $trcd, $trp, $tras));
+
+# latencies
+ my %cas;
+ my $cas_sup = ($bytes->[28] << 32) + ($bytes->[27] << 24) +
+ ($bytes->[26] << 16) + ($bytes->[25] << 8) + $bytes->[24];
+ my $base_cas = 20;
+
+ for ($ii = 0; $ii < 40; $ii++) {
+ if ($cas_sup & (1 << $ii)) {
+ $cas{$base_cas + ($ii * 2)}++;
+ }
+ }
+ printl("Supported CAS Latencies", cas_latencies(keys %cas));
+
+# standard DDR5 speeds
+ prints("Timings at Standard Speeds");
+ foreach my $ctime_at_speed (5/22, 5/21, 5/20, 5/19, 5/18, 5/17, 5/16,
+ 5/15, 5/14, 5/13, 5/12, 5/11, 5/10, 5/9, 5/8) {
+ my $best_cas = 0;
+
+
+ # Find min CAS latency at this speed
+ for ($ii = 39; $ii >= 0; $ii--) {
+ next unless ($cas_sup & (1 << $ii));
+ if (ceil(($taa * 997 / $ctime_at_speed + 1000) / 1000) <= $base_cas + ($ii * 2)) {
+ $best_cas = $base_cas + ($ii * 2);
+ }
+ }
+
+ printl_cond($best_cas && $ctime_at_speed >= $ctime
+ && $ctime_at_speed <= $ctime_max,
+ "AA-RCD-RP-RAS (cycles)" . as_ddr(5, $ctime_at_speed),
+ ddr4_core_timings($best_cas, $ctime_at_speed,
+ $trcd, $trp, $tras));
+ }
+
+# more timing information
+ prints("Timing Parameters");
+
+ printl("Minimum Cycle Time (tCKmin)", tns3($ctime));
+ printl("Maximum Cycle Time (tCKmax)", tns3($ctime_max));
+ printl("Minimum CAS Latency Time (tAA)", tns3($taa));
+ printl("Minimum RAS to CAS Delay (tRCD)", tns3($trcd));
+ printl("Minimum Row Precharge Delay (tRP)", tns3($trp));
+ printl("Minimum Active to Precharge Delay (tRAS)", tns3($tras));
+ printl("Minimum Active to Auto-Refresh Delay (tRC)", tns3(ddr5_ns($bytes, 38)));
+ printl("Minimum Recovery Delay (tRFC1)", tns3(ddr5_ns($bytes, 42)));
+ printl("Minimum Recovery Delay (tRFC2)", tns3(ddr5_ns($bytes, 44)));
+ printl("Minimum Recovery Delay (tRFCsb)", tns3(ddr5_ns($bytes, 46)));
+ printl("Minimum Four Activate Window Delay (tFAW)", tns3(ddr5_ns($bytes, 82)) . " (" . $bytes->[84] . " cycles)");
+ printl("Minimum Row Active to Row Active Delay (tRRD_L)", tns3(ddr5_ns($bytes, 70)) . " (" . $bytes->[72] . " cycles)");
+ printl("Minimum CAS to CAS Delay (tCCD_L)", tns3(ddr5_ns($bytes, 73)) . " (" . $bytes->[75] . " cycles)");
+ printl("Minimum Write Recovery Time (tWR)", tns3(ddr5_ns($bytes, 40)));
+ printl("Minimum Write to Read Time (tWTR_S)", tns3(ddr5_ns($bytes, 88)) . " (" . $bytes->[90] . " cycles)");
+ printl("Minimum Write to Read Time (tWTR_L)", tns3(ddr5_ns($bytes, 85)) . " (" . $bytes->[87] . " cycles)");
+
+# miscellaneous stuff
+ prints("Other Information");
+
+ my $package_type0 = $die_3ds0 ? "3DS" :
+ $die_count0 > 1 ? "Dual-die package" :
+ $die_count0 == 1 ? "Monolithic" : "Unknown";
+ $package_type0 .= sprintf(" (%u dies)", $die_count0) if $die_count0 >= 2;
+ printl("Package Type" . ($rank_mix ? " (Even Rank)" : ""), $package_type0);
+
+ my $package_type1 = $die_3ds1 ? "3DS" :
+ $die_count1 > 1 ? "Dual-die package" :
+ $die_count1 == 1 ? "Monolithic" : "Unknown";
+ $package_type1 .= sprintf(" (%u dies)", $die_count1) if $die_count1 >= 2;
+ printl_cond($rank_mix, "Package Type (Odd Rank)", $package_type1);
+
+ my $ppr = $bytes->[12] >> 7;
+ printl("Post Package Repair",
+ $ppr == 0x00 ? "One row per bank group" :
+ $ppr == 0x01 ? "One row per bank" : "Unknown");
+ printl("Soft PPR Undo/Lock", $bytes->[12] & 0x20 ?
+ "Supported" : "Not Supported");
+ printl("MBIST PPR", $bytes->[12] & 0x02 ?
+ "Supported" : "Not Supported");
+
+ printl("Module Nominal Voltage",
+ ($bytes->[16] & 0xf0) == 0x00 ? "1.1 V" :
+ ($bytes->[16] & 0x0c) == 0x00 ? "Unknown (1.1 V operable)" :
+ ($bytes->[16] & 0x03) == 0x00 ? "Unknown (1.1 V endurant)" : "Unknown");
+
+ printl("Thermal Sensor",
+ $bytes->[14] & 0x08 ? "Supported" : "No");
+}
+
# Parameter: EEPROM bytes 0-127 (using 4-5)
sub decode_direct_rambus($)
{
@@ -2177,6 +2402,10 @@ sub decode_rambus($)
"DDR4E SDRAM" => \&decode_ddr4_sdram,
"LPDDR4 SDRAM" => \&decode_ddr4_sdram,
"LPDDR4X SDRAM" => \&decode_ddr4_sdram,
+ "DDR5 SDRAM" => \&decode_ddr5_sdram,
+ "LPDDR5 SDRAM" => \&decode_ddr5_sdram,
+ "DDR5 NVDIMM-P" => \&decode_ddr5_sdram,
+ "LPDDR5X SDRAM" => \&decode_ddr5_sdram,
"Direct Rambus" => \&decode_direct_rambus,
"Rambus" => \&decode_rambus,
);
@@ -2844,6 +3073,7 @@ for $current (0 .. $#dimm) {
"DDR4E SDRAM", "LPDDR3 SDRAM", # 14, 15
"LPDDR4 SDRAM", "LPDDR4X SDRAM", # 16, 17
"DDR5 SDRAM", "LPDDR5 SDRAM", # 18, 19
+ "DDR5 NVDIMM-P", "LPDDR5X SDRAM", # 20, 21
);
if ($bytes[2] < @type_list) {
$type = $type_list[$bytes[2]];
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 5/6] decode-dimms: Add basic decoding of type specific information for DDR5
2025-11-06 2:14 [PATCH v2 0/6] decode-dimms: Implement DDR5 decoding Stephen Horvath
` (3 preceding siblings ...)
2025-11-06 2:15 ` [PATCH v2 3/6] decode-dimms: Decode timings and other data for DDR5 Stephen Horvath
@ 2025-11-06 2:15 ` Stephen Horvath
2025-11-06 2:15 ` [PATCH v2 6/6] decode-dimms: Decode DDR5 error log Stephen Horvath
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Stephen Horvath @ 2025-11-06 2:15 UTC (permalink / raw)
To: Jean Delvare, linux-i2c@vger.kernel.org
Cc: Guenter Roeck, Kamil Aronowski, Stephen Horvath
Decode more manufacturer information, but only the info that is specific
to certain types of DDR5 memory modules, and not common to all DDR5
modules.
This is completely untested since my modules don't expose this. I also
only have UDIMMs.
Signed-off-by: Stephen Horvath <s.horvath@outlook.com.au>
---
eeprom/decode-dimms | 71 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/eeprom/decode-dimms b/eeprom/decode-dimms
index 4fd4531..2ef2eb9 100755
--- a/eeprom/decode-dimms
+++ b/eeprom/decode-dimms
@@ -2386,6 +2386,77 @@ sub decode_ddr5_sdram($)
(($bytes->[231] >> 4) & 15) + 1));
printl("Module Reference Card",
ddr3_reference_card($bytes->[232], $bytes->[232]));
+
+# type-specific settings
+ if ($spd_info_rev == 0x11 &&
+ ($type == 0x02 || # UDIMM
+ $type == 0x03 || # SODIMM
+ $type == 0x05 || # CUDIMM
+ $type == 0x06)) { # CSODIMM
+ prints("Unbuffered Memory Module");
+
+ printl("Clock driver manufacturer",
+ manufacturer_ddr3($bytes->[240], $bytes->[241]));
+ }
+
+ if ($spd_info_rev == 0x11 &&
+ ($type == 0x01 || # RDIMM
+ $type == 0x04)) { # LRDIMM
+ prints("Registered Memory Module");
+
+ printl("Clock driver manufacturer",
+ manufacturer_ddr3($bytes->[240], $bytes->[241]));
+ printl("Data buffer manufacturer",
+ manufacturer_ddr3($bytes->[244], $bytes->[245]));
+ }
+
+ if ($spd_info_rev == 0x11 &&
+ ($type == 0x07)) { # MRDIMM
+ prints("Multiplexed Rank Memory Module");
+
+ printl("Clock driver manufacturer",
+ manufacturer_ddr3($bytes->[240], $bytes->[241]));
+ printl("Data buffer manufacturer",
+ manufacturer_ddr3($bytes->[244], $bytes->[245]));
+ }
+
+ if ($spd_info_rev == 0x10 &&
+ ($type == 0x0A)) { # DDIMM
+ prints("Differential Memory Module");
+
+ printl("Memory buffer manufacturer",
+ manufacturer_ddr3($bytes->[240], $bytes->[241]));
+ }
+
+ if ($spd_info_rev == 0x01 &&
+ ($raw_type & 0xf0 == 0x90)) { # NVDIMM-N
+ prints("Non-Volatile Memory Module");
+
+ printl("Clock driver manufacturer",
+ manufacturer_ddr3($bytes->[240], $bytes->[241]));
+ printl("Data buffer manufacturer",
+ manufacturer_ddr3($bytes->[244], $bytes->[245]));
+ }
+
+ if ($spd_info_rev == 0x10 &&
+ ($raw_type & 0xf0 == 0xA0)) { # NVDIMM-P
+ prints("Non-Volatile Memory Module");
+
+ printl("Clock driver manufacturer",
+ manufacturer_ddr3($bytes->[240], $bytes->[241]));
+ printl("Data buffer manufacturer",
+ manufacturer_ddr3($bytes->[244], $bytes->[245]));
+ }
+
+ if ($spd_info_rev == 0x10 &&
+ ($type == 0x08)) { # CAMM2
+ prints("Compression Attached Memory Module");
+
+ printl("Clock driver 0 manufacturer",
+ manufacturer_ddr3($bytes->[240], $bytes->[241]));
+ printl("Clock driver 1 manufacturer",
+ manufacturer_ddr3($bytes->[244], $bytes->[245]));
+ }
}
# Parameter: EEPROM bytes 0-127 (using 4-5)
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v2 6/6] decode-dimms: Decode DDR5 error log
2025-11-06 2:14 [PATCH v2 0/6] decode-dimms: Implement DDR5 decoding Stephen Horvath
` (4 preceding siblings ...)
2025-11-06 2:15 ` [PATCH v2 5/6] decode-dimms: Add basic decoding of type specific information " Stephen Horvath
@ 2025-11-06 2:15 ` Stephen Horvath
2026-01-14 11:55 ` [PATCH v2 0/6] decode-dimms: Implement DDR5 decoding Wolfram Sang
2026-01-29 1:58 ` Darsey Litzenberger
7 siblings, 0 replies; 9+ messages in thread
From: Stephen Horvath @ 2025-11-06 2:15 UTC (permalink / raw)
To: Jean Delvare, linux-i2c@vger.kernel.org
Cc: Guenter Roeck, Kamil Aronowski, Stephen Horvath
JESD400 specifies that an error log can be written to anywhere in the
end user programmable eeprom section, following a specific format. Code
to find and read this error log for DDR5 dimms has been added.
This is also completely untested on actual hardware implementations,
only tested by reading some manually constructed files.
Signed-off-by: Stephen Horvath <s.horvath@outlook.com.au>
---
eeprom/decode-dimms | 105 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 105 insertions(+)
diff --git a/eeprom/decode-dimms b/eeprom/decode-dimms
index 2ef2eb9..6bb1b2d 100755
--- a/eeprom/decode-dimms
+++ b/eeprom/decode-dimms
@@ -2656,6 +2656,108 @@ sub decode_ddr5_mfg_data($)
}
}
+# Parameter: EEPROM bytes 0-1023 (using 640-1023)
+sub decode_ddr5_error_data($)
+{
+ my $bytes = shift;
+
+ # Zero or more error logs may appear anywhere in any End User Programmable blocks of the SPD,
+ # including over SPD Block boundaries. They may be found by searching for a four byte anchor string.
+
+ my $errors = [];
+
+ my $size = scalar @{$bytes} < 1023 ? scalar @{$bytes} : 1023;
+
+ for (my $ii = 0; $ii < $size - 640 - 23; $ii++) {
+ if (join('', @{$bytes}[640 + $ii .. 640 + $ii + 3]) eq "95707695") {
+ push @{$errors}, [@{$bytes}[640 + $ii .. 640 + $ii + 23]];
+ $ii += 23;
+ }
+ }
+
+ if (@{$errors} == 0) {
+ # No error logs found
+ return;
+ }
+
+ prints("Error Log");
+
+ printl("Error Log Count", scalar @{$errors});
+
+ for (my $ii = 0; $ii < scalar @{$errors}; $ii++) {
+ my $error = @{$errors}[$ii];
+
+ # error location
+ printl_cond($error->[4] & (1 << 0), "Error $ii Type", "DRAM Uncorrectable Error");
+ printl_cond($error->[4] & (1 << 1), "Error $ii Type", "DRAM Correctable Error");
+ printl_cond($error->[4] & (1 << 2), "Error $ii Type", "DRAM ECS Error");
+ printl_cond($error->[4] & (1 << 3), "Error $ii Type", "hPPR Was Required");
+ printl_cond($error->[4] & (1 << 4), "Error $ii Type", "hPPR Resource Error");
+
+ printl("Error $ii Location CPU", ($error->[5] >> 3) & 0x07);
+ printl("Error $ii Location CPUMC", (($error->[5] & 3) << 2) | ($error->[6] >> 6));
+ printl("Error $ii Location DIMM", ($error->[6] >> 4) & 0x01);
+
+ # these are active low
+ printl_cond(~$error->[6] & (1 << 3), "Error $ii Location Rank", "0 (sub-channel A)");
+ printl_cond(~$error->[6] & (1 << 2), "Error $ii Location Rank", "1 (sub-channel A)");
+ printl_cond(~$error->[6] & (1 << 1), "Error $ii Location Rank", "0 (sub-channel B)");
+ printl_cond(~$error->[6] & (1 << 0), "Error $ii Location Rank", "1 (sub-channel B)");
+
+ printl("Error $ii Location Parity", ($error->[7] >> 6) & 0x01);
+ if (($error->[7] >> 5) & 1) {
+ # chip identifier?
+ printl("Error $ii Location Chip", ($error->[7] >> 2) & 0x07);
+ } else {
+ # row address?
+ printl("Error $ii Location Bank Group", (($error->[7] & 0x03) << 1) | (($error->[8] & 0x80) >> 7));
+ printl("Error $ii Location Bank Address", ($error->[8] >> 5) & 0x03);
+ printl("Error $ii Location Row Address", (($error->[8] & 0x1f) << 12) | ($error->[9] << 4) | ($error->[10] >> 4));
+ printl("Error $ii Location Column Address", (($error->[10] & 0x0f) << 7) | (($error->[11] & 0xf0) >> 1));
+ }
+
+ # also active low
+ printl_cond(~$error->[11] & (1 << 0), "Error $ii Location Device", "DQS6A");
+ printl_cond(~$error->[11] & (1 << 1), "Error $ii Location Device", "DQS7A");
+ printl_cond(~$error->[11] & (1 << 2), "Error $ii Location Device", "DQS8A");
+ printl_cond(~$error->[11] & (1 << 3), "Error $ii Location Device", "DQS9A");
+
+ printl_cond(~$error->[12] & (1 << 0), "Error $ii Location Device", "DQS8B");
+ printl_cond(~$error->[12] & (1 << 1), "Error $ii Location Device", "DQS9B");
+ printl_cond(~$error->[12] & (1 << 2), "Error $ii Location Device", "DQS0A");
+ printl_cond(~$error->[12] & (1 << 3), "Error $ii Location Device", "DQS1A");
+ printl_cond(~$error->[12] & (1 << 4), "Error $ii Location Device", "DQS2A");
+ printl_cond(~$error->[12] & (1 << 5), "Error $ii Location Device", "DQS3A");
+ printl_cond(~$error->[12] & (1 << 6), "Error $ii Location Device", "DQS4A");
+ printl_cond(~$error->[12] & (1 << 7), "Error $ii Location Device", "DQS5A");
+
+ printl_cond(~$error->[12] & (1 << 0), "Error $ii Location Device", "DQS0B");
+ printl_cond(~$error->[12] & (1 << 1), "Error $ii Location Device", "DQS1B");
+ printl_cond(~$error->[12] & (1 << 2), "Error $ii Location Device", "DQS2B");
+ printl_cond(~$error->[12] & (1 << 3), "Error $ii Location Device", "DQS3B");
+ printl_cond(~$error->[12] & (1 << 4), "Error $ii Location Device", "DQS4B");
+ printl_cond(~$error->[12] & (1 << 5), "Error $ii Location Device", "DQS5B");
+ printl_cond(~$error->[12] & (1 << 6), "Error $ii Location Device", "DQS6B");
+ printl_cond(~$error->[12] & (1 << 7), "Error $ii Location Device", "DQS7B");
+
+ # timestamp
+ my $year = ($error->[14] >> 2) + 2020;
+ my $month = (($error->[14] & 0x03) << 2) | ($error->[15] >> 6);
+ my $day = ($error->[15] & 0x3e) >> 1;
+ my $hour = (($error->[15] & 0x01) << 4) | ($error->[16] >> 4);
+ my $minute = (($error->[16] & 0x0f) << 2) | ($error->[17] >> 6);
+ my $second = $error->[17] & 0x3f;
+ printl("Error $ii Timestamp", sprintf("%04d-%02d-%02d %02d:%02d:%02d",
+ $year, $month, $day, $hour, $minute, $second));
+
+ # DRAM refresh settings
+ # TODO
+
+ # measured temperature
+ # TODO
+ }
+}
+
# Parameter: EEPROM bytes 0-127 (using 64-98)
sub decode_manufacturing_information($)
{
@@ -3218,6 +3320,9 @@ for $current (0 .. $#dimm) {
# Decode DDR5-specific manufacturing data in bytes
# 512-639
decode_ddr5_mfg_data(\@bytes);
+ # Decode DDR5-specific error log
+ # 640-1023 (max)
+ decode_ddr5_error_data(\@bytes);
}
} else {
# Decode next 35 bytes (64-98, common to most
--
2.51.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v2 0/6] decode-dimms: Implement DDR5 decoding
2025-11-06 2:14 [PATCH v2 0/6] decode-dimms: Implement DDR5 decoding Stephen Horvath
` (5 preceding siblings ...)
2025-11-06 2:15 ` [PATCH v2 6/6] decode-dimms: Decode DDR5 error log Stephen Horvath
@ 2026-01-14 11:55 ` Wolfram Sang
2026-01-29 1:58 ` Darsey Litzenberger
7 siblings, 0 replies; 9+ messages in thread
From: Wolfram Sang @ 2026-01-14 11:55 UTC (permalink / raw)
To: Stephen Horvath, Jean Delvare
Cc: linux-i2c@vger.kernel.org, Guenter Roeck, Kamil Aronowski
On Thu, Nov 06, 2025 at 02:14:57AM +0000, Stephen Horvath wrote:
> Hi, this series of patches adds DDR5 support to decode-dimms.
>
> I'm not too experienced with perl or the JEDEC specs, so there's
> probably going to be some questionable choices here, but I'd love to
> hear feedback.
Jean, do you have time for this?
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v2 0/6] decode-dimms: Implement DDR5 decoding
2025-11-06 2:14 [PATCH v2 0/6] decode-dimms: Implement DDR5 decoding Stephen Horvath
` (6 preceding siblings ...)
2026-01-14 11:55 ` [PATCH v2 0/6] decode-dimms: Implement DDR5 decoding Wolfram Sang
@ 2026-01-29 1:58 ` Darsey Litzenberger
7 siblings, 0 replies; 9+ messages in thread
From: Darsey Litzenberger @ 2026-01-29 1:58 UTC (permalink / raw)
To: Stephen Horvath
Cc: Jean Delvare, linux-i2c@vger.kernel.org, Guenter Roeck,
Kamil Aronowski
[-- Attachment #1: Type: text/plain, Size: 6996 bytes --]
On Thu, Nov 06, 2025 at 02:14:57AM +0000, Stephen Horvath wrote:
>Hi, this series of patches adds DDR5 support to decode-dimms.
[snip]
I fed the eeprom for one of my RAM sticks into decode-dimms with your
patches applied, and it seems to output a number of suspicious values
(at least to me, as someone completely unfamiliar with the JEDEC specs).
This is for a 48 GiB DDR5-5600 ECC SODIMM, not actually made by Micron,
but a clone sold on eBay[1], so it's possible that its SPD data aren't
exactly what they should be. The module is supposedly equivalent to the
Kingston KSM56T46BD8KM-48HM, which has these specs[2]:
CL(IDD) 46 cycles
Row Cycle Time (tRCmin) 48ns(min.)
Refresh to Active/Refresh Command Time (tRFCmin) 295ns(min.)
Row Active Time (tRASmin) 32ns(min.)
Row Precharge Time (tRPmin) 16ns(min.)
decode-dimms with your patches says this (with my comments):
>---=== SPD EEPROM Information ===---
>EEPROM CRC of bytes 0-509 OK (0xB3D5)
># of bytes written to SDRAM EEPROM 1024
>Total number of bytes in EEPROM 1024
>Fundamental Memory type DDR5 SDRAM
>SPD Revision 1.0
>Module Type SODIMM
>
>---=== Memory Characteristics ===---
>Maximum module speed 5600 MT/s (PC5-22400)
That should be PC5-44800, not PC5-22400. Maybe you're forgetting to
double the clock speed due to DDR doing two transfers per cycle?
>Size 40 GB
It should be 48 GiB, not 40. dmidecode correctly shows it as "48 GB".
>Banks x Rows x Columns x Bits 32 x 17 x 10 x 32
>SDRAM Device Width 8 bits
>Ranks 2
>Rank Mix Symmetrical
>Primary Bus Width 32 bits
>Bus Width Extension 4 bits
>AA-RCD-RP-RAS (cycles) 46-45-45-90
>Supported CAS Latencies 50T, 46T, 42T, 40T, 36T, 32T, 30T, 28T, 26T, 22T
>
>---=== Timings at Standard Speeds ===---
>AA-RCD-RP-RAS (cycles) as DDR5-5600 46-45-45-90
>AA-RCD-RP-RAS (cycles) as DDR5-5200 46-42-42-84
>AA-RCD-RP-RAS (cycles) as DDR5-4800 40-39-39-77
>AA-RCD-RP-RAS (cycles) as DDR5-4400 40-36-36-71
>AA-RCD-RP-RAS (cycles) as DDR5-4000 36-32-32-64
>AA-RCD-RP-RAS (cycles) as DDR5-3600 30-29-29-58
>AA-RCD-RP-RAS (cycles) as DDR5-3200 28-26-26-52
>
>---=== Timing Parameters ===---
>Minimum Cycle Time (tCKmin) 0.357 ns
>Maximum Cycle Time (tCKmax) 1.000 ns
>Minimum CAS Latency Time (tAA) 16.000 ns
>Minimum RAS to CAS Delay (tRCD) 16.000 ns
>Minimum Row Precharge Delay (tRP) 16.000 ns
>Minimum Active to Precharge Delay (tRAS) 32.000 ns
>Minimum Active to Auto-Refresh Delay (tRC) 48.000 ns
>Minimum Recovery Delay (tRFC1) 0.410 ns
>Minimum Recovery Delay (tRFC2) 0.220 ns
>Minimum Recovery Delay (tRFCsb) 0.190 ns
>Minimum Four Activate Window Delay (tFAW) 11.428 ns (32 cycles)
>Minimum Row Active to Row Active Delay (tRRD_L) 5.000 ns (8 cycles)
>Minimum CAS to CAS Delay (tCCD_L) 5.000 ns (8 cycles)
>Minimum Write Recovery Time (tWR) 30.000 ns
>Minimum Write to Read Time (tWTR_S) 2.500 ns (4 cycles)
>Minimum Write to Read Time (tWTR_L) 10.000 ns (16 cycles)
Are those sub-nanosecond timing values reasonable? I'm not familiar
with the JEDEC specs so I don't know if tRFC1/tRFC2/tRFCsb should be
that much smaller than the specified tRFCmin, but the discrepancy looks
suspicious to me.
>---=== Other Information ===---
>Package Type Monolithic
>Post Package Repair One row per bank
>Soft PPR Undo/Lock Not Supported
>MBIST PPR Not Supported
>Module Nominal Voltage 1.1 V
>Thermal Sensor No
>
>---=== Common Information ===---
>SPD Module Info Revision 1.0
>SPD Manufacturer IDT
>PMIC0 Manufacturer Richtek Power
>
>---=== Physical Characteristics ===---
>Module Height 32 mm
>Module Thickness 2 mm front, 2 mm back
>Module Reference Card E revision 0
>
>---=== Manufacturer Data ===---
>Module Manufacturer Micron Technology
>DRAM Manufacturer Micron Technology
>Manufacturing Location Code 0x01
>Manufacturing Date 2025-W44
>Assembly Serial Number 0xFE0E0000
>Part Number MB48G56S80M2R8.RtR
>Revision Code 0x11
>DRAM Stepping G
>
>
>Number of SDRAM DIMMs detected and decoded: 1
Here's a hexdump of the SPD EEPROM, and I've included the dmidecode
output as an attachment.
--[snip]--
00000000: 3010 1203 0501 2062 0000 0000 9002 0000 0..... b........
00000010: 0000 0000 6501 f203 7aad 0000 0000 803e ....e...z......>
00000020: 803e 803e 007d 80bb 3075 9a01 dc00 be00 .>.>.}..0u......
00000030: 0000 0000 0000 d400 0000 d400 0000 d400 ................
00000040: 0000 d400 0000 8813 0888 1308 204e 2010 ............ N .
00000050: 2710 a42c 2010 2710 c409 044c 1d0c 0000 '.., .'....L....
00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
*
000000c0: 1000 80b3 8015 8a8c 8220 0000 0000 0000 ......... ......
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000e0: 0000 0000 0000 1111 0481 082a 0000 0000 ...........*....
000000f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
*
000001f0: 0000 0000 0000 0000 0000 0000 0000 d5b3 ................
00000200: 802c 0125 44fe 0e00 004d 4234 3847 3536 .,.%D....MB48G56
00000210: 5338 304d 3252 382e 5274 5220 2020 2020 S80M2R8.RtR
00000220: 2020 2020 2020 2011 802c 4734 3233 3330 ..,G42330
00000230: 3134 3933 0000 0000 0000 0000 0000 0000 1493............
00000240: 0000 0000 0000 0000 0000 0000 0000 0000 ................
*
000003f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
--[snip]--
[1] https://web.archive.org/web/20260129012956/https://www.ebay.ca/itm/205158078392
[2] https://www.kingston.com/datasheets/KSM56T46BD8KM-48HM.pdf
--
Darsey Litzenberger <dlitz@dlitz.net>
[-- Attachment #2: 48gb-ram-spd-eeprom.bin.xxd --]
[-- Type: text/plain, Size: 1230 bytes --]
00000000: 3010 1203 0501 2062 0000 0000 9002 0000 0..... b........
00000010: 0000 0000 6501 f203 7aad 0000 0000 803e ....e...z......>
00000020: 803e 803e 007d 80bb 3075 9a01 dc00 be00 .>.>.}..0u......
00000030: 0000 0000 0000 d400 0000 d400 0000 d400 ................
00000040: 0000 d400 0000 8813 0888 1308 204e 2010 ............ N .
00000050: 2710 a42c 2010 2710 c409 044c 1d0c 0000 '.., .'....L....
00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
*
000000c0: 1000 80b3 8015 8a8c 8220 0000 0000 0000 ......... ......
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
000000e0: 0000 0000 0000 1111 0481 082a 0000 0000 ...........*....
000000f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
*
000001f0: 0000 0000 0000 0000 0000 0000 0000 d5b3 ................
00000200: 802c 0125 44fe 0e00 004d 4234 3847 3536 .,.%D....MB48G56
00000210: 5338 304d 3252 382e 5274 5220 2020 2020 S80M2R8.RtR
00000220: 2020 2020 2020 2011 802c 4734 3233 3330 ..,G42330
00000230: 3134 3933 0000 0000 0000 0000 0000 0000 1493............
00000240: 0000 0000 0000 0000 0000 0000 0000 0000 ................
*
000003f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
[-- Attachment #3: dmidecode-memory.txt --]
[-- Type: text/plain, Size: 1535 bytes --]
# dmidecode 3.6
Getting SMBIOS data from sysfs.
SMBIOS 3.7.0 present.
Handle 0x0013, DMI type 16, 23 bytes
Physical Memory Array
Location: System Board Or Motherboard
Use: System Memory
Error Correction Type: Multi-bit ECC
Maximum Capacity: 64 GB
Error Information Handle: 0x0012
Number Of Devices: 2
Handle 0x0016, DMI type 17, 92 bytes
Memory Device
Array Handle: 0x0013
Error Information Handle: 0x0015
Total Width: Unknown
Data Width: Unknown
Size: No Module Installed
Form Factor: Unknown
Set: None
Locator: DIMM 0
Bank Locator: P0 CHANNEL A
Type: Unknown
Type Detail: Unknown
Handle 0x0018, DMI type 17, 92 bytes
Memory Device
Array Handle: 0x0013
Error Information Handle: 0x0017
Total Width: 72 bits
Data Width: 64 bits
Size: 48 GB
Form Factor: SODIMM
Set: None
Locator: DIMM 0
Bank Locator: P0 CHANNEL B
Type: DDR5
Type Detail: Synchronous Unbuffered (Unregistered)
Speed: 5600 MT/s
Manufacturer: Micron Technology
Serial Number: FE0E0000
Asset Tag: Not Specified
Part Number: MB48G56S80M2R8.RtR
Rank: 2
Configured Memory Speed: 5600 MT/s
Minimum Voltage: 1.1 V
Maximum Voltage: 1.1 V
Configured Voltage: 1.1 V
Memory Technology: DRAM
Memory Operating Mode Capability: Volatile memory
Firmware Version: Unknown
Module Manufacturer ID: Bank 1, Hex 0x2C
Module Product ID: Unknown
Memory Subsystem Controller Manufacturer ID: Unknown
Memory Subsystem Controller Product ID: Unknown
Non-Volatile Size: None
Volatile Size: 48 GB
Cache Size: None
Logical Size: None
[-- Attachment #4: dmidecode-memory-u.txt --]
[-- Type: text/plain, Size: 1512 bytes --]
# dmidecode 3.6
Getting SMBIOS data from sysfs.
SMBIOS 3.7.0 present.
Handle 0x0013, DMI type 16, 23 bytes
Header and Data:
10 17 13 00 03 03 06 00 00 00 04 12 00 02 00 00
00 00 00 00 00 00 00
Handle 0x0016, DMI type 17, 92 bytes
Header and Data:
11 5C 16 00 13 00 15 00 FF FF FF FF 00 00 02 00
01 02 02 04 00 00 00 03 04 00 05 00 00 00 00 00
00 00 00 00 00 00 00 00 02 04 00 06 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00
Strings:
44 49 4D 4D 20 30 00
DIMM 0
50 30 20 43 48 41 4E 4E 45 4C 20 41 00
P0 CHANNEL A
55 6E 6B 6E 6F 77 6E 00
Unknown
55 6E 6B 6E 6F 77 6E 00
Unknown
55 6E 6B 6E 6F 77 6E 00
Unknown
55 6E 6B 6E 6F 77 6E 00
Unknown
Handle 0x0018, DMI type 17, 92 bytes
Header and Data:
11 5C 18 00 13 00 17 00 48 00 40 00 FF 7F 0D 00
01 02 22 80 40 E0 15 03 04 00 05 02 00 C0 00 00
E0 15 4C 04 4C 04 4C 04 03 08 00 06 80 2C 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00
Strings:
44 49 4D 4D 20 30 00
DIMM 0
50 30 20 43 48 41 4E 4E 45 4C 20 42 00
P0 CHANNEL B
4D 69 63 72 6F 6E 20 54 65 63 68 6E 6F 6C 6F 67
79 00
Micron Technology
46 45 30 45 30 30 30 30 00
FE0E0000
4D 42 34 38 47 35 36 53 38 30 4D 32 52 38 2E 52
74 52 20 20 20 20 20 20 20 20 20 20 20 20 00
MB48G56S80M2R8.RtR
55 6E 6B 6E 6F 77 6E 00
Unknown
[-- Attachment #5: decode-dimms.txt --]
[-- Type: text/plain, Size: 4188 bytes --]
# Using big-endian 16-bit hex dump
# decode-dimms version 4.4
Memory Serial Presence Detect Decoder
By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner,
Jean Delvare, Trent Piepho and others
Decoding EEPROM: ../48gb-ram-spd-eeprom.bin.xxd
---=== SPD EEPROM Information ===---
EEPROM CRC of bytes 0-509 OK (0xB3D5)
# of bytes written to SDRAM EEPROM 1024
Total number of bytes in EEPROM 1024
Fundamental Memory type DDR5 SDRAM
SPD Revision 1.0
Module Type SODIMM
---=== Memory Characteristics ===---
Maximum module speed 5600 MT/s (PC5-22400)
Size 40 GB
Banks x Rows x Columns x Bits 32 x 17 x 10 x 32
SDRAM Device Width 8 bits
Ranks 2
Rank Mix Symmetrical
Primary Bus Width 32 bits
Bus Width Extension 4 bits
AA-RCD-RP-RAS (cycles) 46-45-45-90
Supported CAS Latencies 50T, 46T, 42T, 40T, 36T, 32T, 30T, 28T, 26T, 22T
---=== Timings at Standard Speeds ===---
AA-RCD-RP-RAS (cycles) as DDR5-5600 46-45-45-90
AA-RCD-RP-RAS (cycles) as DDR5-5200 46-42-42-84
AA-RCD-RP-RAS (cycles) as DDR5-4800 40-39-39-77
AA-RCD-RP-RAS (cycles) as DDR5-4400 40-36-36-71
AA-RCD-RP-RAS (cycles) as DDR5-4000 36-32-32-64
AA-RCD-RP-RAS (cycles) as DDR5-3600 30-29-29-58
AA-RCD-RP-RAS (cycles) as DDR5-3200 28-26-26-52
---=== Timing Parameters ===---
Minimum Cycle Time (tCKmin) 0.357 ns
Maximum Cycle Time (tCKmax) 1.000 ns
Minimum CAS Latency Time (tAA) 16.000 ns
Minimum RAS to CAS Delay (tRCD) 16.000 ns
Minimum Row Precharge Delay (tRP) 16.000 ns
Minimum Active to Precharge Delay (tRAS) 32.000 ns
Minimum Active to Auto-Refresh Delay (tRC) 48.000 ns
Minimum Recovery Delay (tRFC1) 0.410 ns
Minimum Recovery Delay (tRFC2) 0.220 ns
Minimum Recovery Delay (tRFCsb) 0.190 ns
Minimum Four Activate Window Delay (tFAW) 11.428 ns (32 cycles)
Minimum Row Active to Row Active Delay (tRRD_L) 5.000 ns (8 cycles)
Minimum CAS to CAS Delay (tCCD_L) 5.000 ns (8 cycles)
Minimum Write Recovery Time (tWR) 30.000 ns
Minimum Write to Read Time (tWTR_S) 2.500 ns (4 cycles)
Minimum Write to Read Time (tWTR_L) 10.000 ns (16 cycles)
---=== Other Information ===---
Package Type Monolithic
Post Package Repair One row per bank
Soft PPR Undo/Lock Not Supported
MBIST PPR Not Supported
Module Nominal Voltage 1.1 V
Thermal Sensor No
---=== Common Information ===---
SPD Module Info Revision 1.0
SPD Manufacturer IDT
PMIC0 Manufacturer Richtek Power
---=== Physical Characteristics ===---
Module Height 32 mm
Module Thickness 2 mm front, 2 mm back
Module Reference Card E revision 0
---=== Manufacturer Data ===---
Module Manufacturer Micron Technology
DRAM Manufacturer Micron Technology
Manufacturing Location Code 0x01
Manufacturing Date 2025-W44
Assembly Serial Number 0xFE0E0000
Part Number MB48G56S80M2R8.RtR
Revision Code 0x11
DRAM Stepping G
Number of SDRAM DIMMs detected and decoded: 1
^ permalink raw reply [flat|nested] 9+ messages in thread