The attached diffs provide some preliminary decode of the DDR3 SPD... This work is based on the recently-published Appendix K to JESD21-C
(see http://www.jedec.org/download/search/4_01_02_11R18.pdf).

Warning:  I'm no perl expert.  The code I've written is probably ugly to
most of the readers of this list.  But it does work!

There's no attempt made here to decode the Intel XMP extension data (located beyond byte 176). I still haven't been able to find a spec.

----------------------------------------------------------------------
|   Paul Goyette   | PGP DSS Key fingerprint: |  E-mail addresses:   |
| Customer Service | FA29 0E3B 35AF E8AE 6651 |  [EMAIL PROTECTED]   |
| Network Engineer | 0786 F758 55DE 53BA 7731 | [EMAIL PROTECTED] |
----------------------------------------------------------------------
872a873,1035
> # Parameter: bytes 0-127
> sub decode_ddr3_sdram($)
> {
>       my $bytes = shift;
>       my $l;
>       my $temp;
>       my $ctime;
> 
> # SPD revision
>       if ($bytes->[62] != 0xff) {
>               printl "SPD Revision", ($bytes->[62] >> 4) . "." .
>                                      ($bytes->[62] & 0xf);
>       }
> 
> # speed
>       prints "Memory Characteristics";
> 
>       $l = "Fine time base";
>       my $dividend = ($bytes->[9] >> 4) & 15;
>       my $divisor  = $bytes->[9] & 15;
>       printl $l, sprintf("%.3f", $dividend / $divisor) . " ps";
> 
>       $l = "Medium time base";
>       $dividend = $bytes->[10];
>       $divisor  = $bytes->[11];
>       my $mtb = $dividend / $divisor;
>       printl $l, tns($mtb);
> 
>       $l = "Maximum module speed";
>       $ctime = $bytes->[12] * $mtb;
>       my $ddrclk = 2 * (1000 / $ctime);
>       my $tbits = 1 << (($bytes->[8] & 7) + 3);
>       my $pcclk = int ($ddrclk * $tbits / 8);
>       $ddrclk = int ($ddrclk);
>       printl $l, "${ddrclk}MHz (PC3-${pcclk})";
> 
> # Size computation
> 
>       my $cap =  ($bytes->[4]       & 15) + 28;
>       $cap   +=  ($bytes->[8]       & 7)  + 3;
>       $cap   -=  ($bytes->[7]       & 7)  + 2;
>       $cap   -= 20 + 3;
>       my $k   = (($bytes->[7] >> 3) & 31) + 1;
>       printl "Size", ((1 << $cap) * $k) . " MB";
> 
>       printl "Banks x Rows x Columns x Bits",
>              join(' x ', 1 << ((($bytes->[4] >> 4) &  7) +  3),
>                          ((($bytes->[5] >> 3) & 31) + 12),
>                          ( ($bytes->[5]       &  7) +  9),
>                          ( 1 << (($bytes->[6] &  7) + 3)) );
>       printl "Ranks", $k;
> 
>       printl "SDRAM Device Width", $bytes->[13]." bits";
> 
>       my $taa;
>       my $trcd;
>       my $trp;
>       my $tras;
> 
>       $taa  = int($bytes->[16] / $bytes->[12]);
>       $trcd = int($bytes->[18] / $bytes->[12]);
>       $trp  = int($bytes->[20] / $bytes->[12]);
>       $tras = int((($bytes->[21] >> 4) * 256 + $bytes->[22]) / $bytes->[12]);
> 
>       printl "tCL-tRCD-tRP-tRAS", join("-", $taa, $trcd, $trp, $tras);
> 
> # latencies
>       my $highestCAS = 0;
>       my %cas;
>       my $ii;
>       my $cas_sup = $bytes->[15] << 8 + $bytes->[14];
>       for ($ii = 0; $ii < 15; $ii++) {
>               if ($cas_sup & (1 << $ii)) {
>                       $highestCAS = $ii + 4;
>                       $cas{$highestCAS}++;
>               }
>       }
>       printl "Supported CAS Latencies (tCL)", cas_latencies(keys %cas);
> 
> # more timing information
>       prints "Timing Parameters" ;
> 
>       printl "Minimum Write Recovery time (tWRmin)", tns($bytes->[17] * $mtb);
>       printl "Minimum Row Active-to-Row Active Delay (tRRDmin)",
>               tns($bytes->[19] * $mtb);
>       printl "Minimum Active-to-Active Refresh Delay (tRCmin)",
>               tns((((($bytes->[21] >> 4) & 15) << 8) + $bytes->[23]) * $mtb);
>       printl "Minimum Refresh Recovery Delay (tRFCmin)", 
>               tns((($bytes->[25] << 8) + $bytes->[24]) * $mtb);
>       printl "Minimum Write-to-Read Cmd Delay (tWTRmin)",
>               tns($bytes->[26] * $mtb);
>       printl "Minimum Read-to-Precharge Cmd Delay (tRTPmin)",
>               tns($bytes->[27] * $mtb);
>       printl "Minimum Four Activate Window Delay (tFAWmin)",
>               tns((($bytes->[28] & 15) << 8 + $bytes->[29]) * $mtb);
> 
> # miscellaneous stuff
>       prints "Optional Features";
> 
>       printl "RZQ/6 supported?", ($bytes->[30] & 1) ? "Yes" : "No";
>       printl "RZQ/7 supported?", ($bytes->[30] & 2) ? "Yes" : "No";
>       printl "DLL-Off Mode supported?", ($bytes->[30] & 128) ? "Yes" : "No";
>       printl "Extended (90-95C) operating range?",
>               ($bytes->[31] & 1) ? "Yes" : "No";
>       printl "Refresh Rate in extended range",
>               ($bytes->[31] & 2) ? "2X" : "1X";
>       printl "Auto Self-Refresh?", ($bytes->[31] & 4) ? "Yes" : "No";
>       printl "On-Die Thermal Sensor readout?",
>               ($bytes->[31] & 8) ? "Yes" : "No";
>       printl "Partial Array Self-Refresh?",
>               ($bytes->[31] & 128) ? "Yes" : "No";
>       printl "Thermal Sensor Accuracy",
>               ($bytes->[32] & 128) ? sprintf($bytes->[32] & 127) :
>                                       "Not implemented";
>       printl "SDRAM Device Type",
>               ($bytes->[33] & 128) ? sprintf($bytes->[33] & 127) :
>                                       "Standard Monolithic";
> 
>       prints "Manufacturer Data";
> 
>       $l = "Module Manufacturer Code";
>       printl $l, sprintf("0x%.2X%.2X", $bytes->[118], $bytes->[117]);
> 
>       $l = "DRAM Manufacturer Code";
>       printl $l, sprintf("0x%.2X%.2X", $bytes->[148], $bytes->[149]);
> 
>       $l = "Manufacturing Location";
>       $temp = (chr($bytes->[8]) =~ m/^[\w\d]$/) ? chr($bytes->[8])
>             : sprintf("0x%.2X", $bytes->[8]);
>       printl $l, $temp;
> 
>       $l = "Part Number";
>       $temp = "";
>       for (my $i = 128; $i <= 145; $i++) {
>               $temp .= chr($bytes->[$i]);
>       };
>       printl $l, $temp;
> 
>       $l = "Revision";
>       $temp = sprintf("0x%02X%02X\n", $bytes->[146], $bytes->[147]);
>       printl $l, $temp;
> 
>       $l = "Manufacturing Date";
>       # In theory the year and week are in BCD format, but
>       # this is not always true in practice :(
>       if (($bytes->[120] & 0xf0) <= 0x90
>        && ($bytes->[120] & 0x0f) <= 0x09
>        && ($bytes->[121] & 0xf0) <= 0x90
>        && ($bytes->[121] & 0x0f) <= 0x09) {
>               # Note that this heuristic will break in year 2080
>               $temp = sprintf("%d%02X-W%02X\n",
>                               $bytes->[120] >= 0x80 ? 19 : 20,
>                               $bytes->[120], $bytes->[121]);
>       } else {
>               $temp = sprintf("0x%02X%02X\n", $bytes->[120], $bytes->[121]);
>       }
>       printl $l, $temp;
> 
>       $l = "Assembly Serial Number";
>       $temp = sprintf("0x%02X%02X%02X%02X\n", $bytes->[122], $bytes->[123]);
>       printl $l, $temp;
> }
> 
1063a1227
>       "DDR3 SDRAM"    => \&decode_ddr3_sdram,
1181a1346,1371
> sub readfullspd($$) # reads all bytes from SPD-EEPROM
> {
>       my ($size, $dimm_i) = @_;
>       my @bytes;
>       if ($use_hexdump) {
>               @bytes = read_hexdump($dimm_i);
>               return @bytes[0..$size];
>       } elsif ($use_sysfs) {
>               # Kernel 2.6 with sysfs
>               sysopen(HANDLE, "/sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom", 
> O_RDONLY)
>                       or die "Cannot open 
> /sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom";
>               binmode HANDLE;
>               sysseek(HANDLE, 0, SEEK_SET);
>               sysread(HANDLE, my $eeprom, $size);
>               close HANDLE;
>               @bytes = unpack(sprintf("C%d", $size), $eeprom);
>       } else {
>               # Kernel 2.4 with procfs
>               for my $i (0 .. $size/16) {
>                       my $hexoff = sprintf('%02x', $i * 16);
>                       push @bytes, split(" ", `cat 
> /proc/sys/dev/sensors/$dimm_i/$hexoff`);
>               }
>       }
>       return @bytes;
> }
> 
1274,1276d1463
<               next unless $bytes[63] == $dimm_checksum || $opt_igncheck;
<               $dimm_count++;
< 
1295,1300d1481
<               my $l = "EEPROM Checksum of bytes 0-62";
<               printl $l, ($bytes[63] == $dimm_checksum ?
<                       sprintf("OK (0x%.2X)", $bytes[63]):
<                       sprintf("Bad\n(found 0x%.2X, calculated 0x%.2X)\n",
<                               $bytes[63], $dimm_checksum));
< 
1302c1483,1528
<               my $is_rambus = $bytes[0] < 4;
---
>               my $is_rambus = ($bytes[0] < 4 && $bytes[0] >= 0);
> 
>               if ($is_rambus || $bytes[2] <= 8) {
>                       my $l = "EEPROM Checksum of bytes 0-62";
>                       printl $l, ($bytes[63] == $dimm_checksum ?
>                               sprintf("OK (0x%.2X)", $bytes[63]):
>                               sprintf("Bad\n(found 0x%.2X, calculated 
> 0x%.2X)\n",
>                                       $bytes[63], $dimm_checksum));
>                       next unless $bytes[63] == $dimm_checksum ||
>                                   $opt_igncheck;
>                       $dimm_count++;
> 
>               } else {
>                       my @sizes = ( 128, 176, 256, 0, 0, 0, 0, 0);
>                       my $spdsize = $sizes[($bytes[0] >> 4) & 7];
> 
>                       @bytes = readfullspd($spdsize, $dimm_list[$i]);
>                       my $dimm_crc = 0;
>                       my $crc_cover = $bytes[0] & 0x80 ? 116 : 125;
>                       my $crc_ptr = 0;
>                       my $crc_bit;
>                       while ($crc_ptr <= $crc_cover) {
>                               $dimm_crc = $dimm_crc ^ ($bytes[$crc_ptr] << 8);
>                               for ($crc_bit = 0; $crc_bit < 8; $crc_bit++) {
>                                       if ($dimm_crc & 0x8000) {
>                                               $dimm_crc = ($dimm_crc << 1) ^
>                                                       0x1021;
>                                       } else {
>                                               $dimm_crc = $dimm_crc << 1
>                                       }
>                               }
>                               $crc_ptr++;
>                       }
>                       $dimm_crc = $dimm_crc & 0xffff;
> 
>                       my $l = "EEPROM CRC of bytes 0-" .
>                               sprintf("%d", $crc_cover);
>                       my $crc_calc = $bytes[127] << 8 | $bytes[126];
>                       printl $l, ($dimm_crc == $crc_calc)?
>                               sprintf("OK (0x%.4X)", $dimm_crc):
>                               sprintf("Bad\n(found 0x%.4X, calculated 
> 0x%.4X)\n",
>                                       $crc_calc, $dimm_crc);
>                       next unless $crc_calc == $dimm_crc || $opt_igncheck;
>                       $dimm_count++;
>               }
> 
1315c1541
<               $l = "Total number of bytes in EEPROM";
---
>               my $l = "Total number of bytes in EEPROM";
1335a1562,1564
>                       elsif ($bytes[2] == 9) { $type = "FB-DIMM"; }
>                       elsif ($bytes[2] == 10) { $type = "FB-DIMM PROBE"; }
>                       elsif ($bytes[2] == 11) { $type = "DDR3 SDRAM"; }
1342a1572,1576
>               # DDR3 Manufacturer info is already decoded
>               # (It's NOT common!)
> 
>               next if ($type eq "DDR3 SDRAM");
> 
1346c1580,1584
<               @bytes = readspd64(64, $dimm_list[$i]);
---
>               if ($#bytes == 63) {
>                       @bytes = readspd64(64, $dimm_list[$i]);
>               } else {
>                       @bytes = @bytes[64..$#bytes];
>               }

Reply via email to