Ticket #2323 - Preliminary support for DDR3 in decode-dimms

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The attached diffs provide some preliminary decode of the DDR3 SPD...

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!

----------------------------------------------------------------------
|   Paul Goyette   | PGP DSS Key fingerprint: |  E-mail addresses:   |
| Customer Service | FA29 0E3B 35AF E8AE 6651 |  paul at whooppee.com   |
| Network Engineer | 0786 F758 55DE 53BA 7731 | pgoyette at juniper.net |
----------------------------------------------------------------------
-------------- next part --------------
872a873,983
> # 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, sprintf("%.3f", $mtb) . " ns";
> 
> 	$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);
> 
> 	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;
> }
> 
1063a1175
> 	"DDR3 SDRAM"	=> \&decode_ddr3_sdram,
1181a1294,1319
> 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,1276d1411
< 		next unless $bytes[63] == $dimm_checksum || $opt_igncheck;
< 		$dimm_count++;
< 
1295,1300d1429
< 		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));
< 
1302c1431,1476
< 		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++;
> 		}
> 
1315c1489
< 		$l = "Total number of bytes in EEPROM";
---
> 		my $l = "Total number of bytes in EEPROM";
1335a1510,1512
> 			elsif ($bytes[2] == 9) { $type = "FB-DIMM"; }
> 			elsif ($bytes[2] == 10) { $type = "FB-DIMM PROBE"; }
> 			elsif ($bytes[2] == 11) { $type = "DDR3 SDRAM"; }
1342a1520,1524
> 		# DDR3 Manufacturer info is already decoded
> 		# (It's NOT common!)
> 
> 		next if ($type == "DDR3 SDRAM");
> 
1346c1528,1532
< 		@bytes = readspd64(64, $dimm_list[$i]);
---
> 		if ($#bytes == 63) {
> 			@bytes = readspd64(64, $dimm_list[$i]);
> 		} else {
> 			@bytes = @bytes[64..$#bytes];
> 		}


[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux