| | 1039 | # Parameter: EEPROM bytes 0-127 (using 3-76) |
| | 1040 | sub decode_ddr3_sdram($) |
| | 1041 | { |
| | 1042 | my $bytes = shift; |
| | 1043 | my $l; |
| | 1044 | my $temp; |
| | 1045 | my $ctime; |
| | 1046 | |
| | 1047 | my @module_types = ("Undefined", "RDIMM", "UDIMM", "SO-DIMM", |
| | 1048 | "Micro-DIMM", "Mini-RDIMM", "Mini-UDIMM"); |
| | 1049 | |
| | 1050 | printl "Module Type", ($bytes->[3] <= $#module_types) ? |
| | 1051 | $module_types[$bytes->[3]] : |
| | 1052 | sprint("Reserved (0x%.2X)", $bytes->[3]); |
| | 1053 | |
| | 1054 | # speed |
| | 1055 | prints "Memory Characteristics"; |
| | 1056 | |
| | 1057 | $l = "Fine time base"; |
| | 1058 | my $dividend = ($bytes->[9] >> 4) & 15; |
| | 1059 | my $divisor = $bytes->[9] & 15; |
| | 1060 | printl $l, sprintf("%.3f", $dividend / $divisor) . " ps"; |
| | 1061 | |
| | 1062 | $l = "Medium time base"; |
| | 1063 | $dividend = $bytes->[10]; |
| | 1064 | $divisor = $bytes->[11]; |
| | 1065 | my $mtb = $dividend / $divisor; |
| | 1066 | printl $l, tns3($mtb); |
| | 1067 | |
| | 1068 | $l = "Maximum module speed"; |
| | 1069 | $ctime = $bytes->[12] * $mtb; |
| | 1070 | my $ddrclk = 2 * (1000 / $ctime); |
| | 1071 | my $tbits = 1 << (($bytes->[8] & 7) + 3); |
| | 1072 | my $pcclk = int ($ddrclk * $tbits / 8); |
| | 1073 | $ddrclk = int ($ddrclk); |
| | 1074 | printl $l, "${ddrclk}MHz (PC3-${pcclk})"; |
| | 1075 | |
| | 1076 | # Size computation |
| | 1077 | |
| | 1078 | my $cap = ($bytes->[4] & 15) + 28; |
| | 1079 | $cap += ($bytes->[8] & 7) + 3; |
| | 1080 | $cap -= ($bytes->[7] & 7) + 2; |
| | 1081 | $cap -= 20 + 3; |
| | 1082 | my $k = (($bytes->[7] >> 3) & 31) + 1; |
| | 1083 | printl "Size", ((1 << $cap) * $k) . " MB"; |
| | 1084 | |
| | 1085 | printl "Banks x Rows x Columns x Bits", |
| | 1086 | join(' x ', 1 << ((($bytes->[4] >> 4) & 7) + 3), |
| | 1087 | ((($bytes->[5] >> 3) & 31) + 12), |
| | 1088 | ( ($bytes->[5] & 7) + 9), |
| | 1089 | ( 1 << (($bytes->[8] & 7) + 3)) ); |
| | 1090 | printl "Ranks", $k; |
| | 1091 | |
| | 1092 | printl "SDRAM Device Width", (1 << (($bytes->[7] & 7) + 2))." bits"; |
| | 1093 | |
| | 1094 | my $taa; |
| | 1095 | my $trcd; |
| | 1096 | my $trp; |
| | 1097 | my $tras; |
| | 1098 | |
| | 1099 | $taa = int($bytes->[16] / $bytes->[12]); |
| | 1100 | $trcd = int($bytes->[18] / $bytes->[12]); |
| | 1101 | $trp = int($bytes->[20] / $bytes->[12]); |
| | 1102 | $tras = int((($bytes->[21] >> 4) * 256 + $bytes->[22]) / $bytes->[12]); |
| | 1103 | |
| | 1104 | printl "tCL-tRCD-tRP-tRAS", join("-", $taa, $trcd, $trp, $tras); |
| | 1105 | |
| | 1106 | # latencies |
| | 1107 | my $highestCAS = 0; |
| | 1108 | my %cas; |
| | 1109 | my $ii; |
| | 1110 | my $cas_sup = ($bytes->[15] << 8) + $bytes->[14]; |
| | 1111 | for ($ii = 0; $ii < 15; $ii++) { |
| | 1112 | if ($cas_sup & (1 << $ii)) { |
| | 1113 | $highestCAS = $ii + 4; |
| | 1114 | $cas{$highestCAS}++; |
| | 1115 | } |
| | 1116 | } |
| | 1117 | printl "Supported CAS Latencies (tCL)", cas_latencies(keys %cas); |
| | 1118 | |
| | 1119 | # more timing information |
| | 1120 | prints "Timing Parameters" ; |
| | 1121 | |
| | 1122 | printl "Minimum Write Recovery time (tWR)", tns3($bytes->[17] * $mtb); |
| | 1123 | printl "Minimum Row Active to Row Active Delay (tRRD)", |
| | 1124 | tns3($bytes->[19] * $mtb); |
| | 1125 | printl "Minimum Active to Auto-Refresh Delay (tRC)", |
| | 1126 | tns3((((($bytes->[21] >> 4) & 15) << 8) + $bytes->[23]) * $mtb); |
| | 1127 | printl "Minimum Recovery Delay (tRFC)", |
| | 1128 | tns3((($bytes->[25] << 8) + $bytes->[24]) * $mtb); |
| | 1129 | printl "Minimum Write to Read CMD Delay (tWTR)", |
| | 1130 | tns3($bytes->[26] * $mtb); |
| | 1131 | printl "Minimum Read to Pre-charge CMD Delay (tRTP)", |
| | 1132 | tns3($bytes->[27] * $mtb); |
| | 1133 | printl "Minimum Four Activate Window Delay (tFAW)", |
| | 1134 | tns3(((($bytes->[28] & 15) << 8) + $bytes->[29]) * $mtb); |
| | 1135 | |
| | 1136 | # miscellaneous stuff |
| | 1137 | prints "Optional Features"; |
| | 1138 | |
| | 1139 | my $volts = "1.5V"; |
| | 1140 | if ($bytes->[6] & 1) { |
| | 1141 | $volts .= " tolerant"; |
| | 1142 | } |
| | 1143 | if ($bytes->[6] & 2) { |
| | 1144 | $volts .= ", 1.35V "; |
| | 1145 | } |
| | 1146 | if ($bytes->[6] & 4) { |
| | 1147 | $volts .= ", 1.2X V"; |
| | 1148 | } |
| | 1149 | printl "Operable voltages", $volts; |
| | 1150 | printl "RZQ/6 supported?", ($bytes->[30] & 1) ? "Yes" : "No"; |
| | 1151 | printl "RZQ/7 supported?", ($bytes->[30] & 2) ? "Yes" : "No"; |
| | 1152 | printl "DLL-Off Mode supported?", ($bytes->[30] & 128) ? "Yes" : "No"; |
| | 1153 | printl "Operating temperature range", sprintf "0-%dC", |
| | 1154 | ($bytes->[31] & 1) ? 95 : 85; |
| | 1155 | printl "Refresh Rate in extended temp range", |
| | 1156 | ($bytes->[31] & 2) ? "2X" : "1X"; |
| | 1157 | printl "Auto Self-Refresh?", ($bytes->[31] & 4) ? "Yes" : "No"; |
| | 1158 | printl "On-Die Thermal Sensor readout?", |
| | 1159 | ($bytes->[31] & 8) ? "Yes" : "No"; |
| | 1160 | printl "Partial Array Self-Refresh?", |
| | 1161 | ($bytes->[31] & 128) ? "Yes" : "No"; |
| | 1162 | printl "Thermal Sensor Accuracy", |
| | 1163 | ($bytes->[32] & 128) ? sprintf($bytes->[32] & 127) : |
| | 1164 | "Not implemented"; |
| | 1165 | printl "SDRAM Device Type", |
| | 1166 | ($bytes->[33] & 128) ? sprintf($bytes->[33] & 127) : |
| | 1167 | "Standard Monolithic"; |
| | 1168 | if ($bytes->[3] >= 1 && $bytes->[3] <= 6) { |
| | 1169 | |
| | 1170 | prints "Physical Characteristics"; |
| | 1171 | printl "Module Height (mm)", ($bytes->[60] & 31) + 15; |
| | 1172 | printl "Module Thickness (mm)", sprintf("%d front, %d back", |
| | 1173 | ($bytes->[61] & 15) + 1, |
| | 1174 | (($bytes->[61] >> 4) & 15) +1); |
| | 1175 | printl "Module Width (mm)", ($bytes->[3] <= 2) ? 133.5 : |
| | 1176 | ($bytes->[3] == 3) ? 67.6 : "TBD"; |
| | 1177 | |
| | 1178 | my $alphabet = "ABCDEFGHJKLMNPRTUVWY"; |
| | 1179 | my $ref = $bytes->[62] & 31; |
| | 1180 | my $ref_card; |
| | 1181 | if ($ref == 31) { |
| | 1182 | $ref_card = "ZZ"; |
| | 1183 | } else { |
| | 1184 | if ($bytes->[62] & 128) { |
| | 1185 | $ref += 31; |
| | 1186 | } |
| | 1187 | if ($ref < length $alphabet) { |
| | 1188 | $ref_card = substr $alphabet, $ref, 1; |
| | 1189 | } else { |
| | 1190 | my $ref1 = int($ref / (length $alphabet)); |
| | 1191 | $ref -= (length $alphabet) * $ref1; |
| | 1192 | $ref_card = (substr $alphabet, $ref1, 1) . |
| | 1193 | (substr $alphabet, $ref, 1); |
| | 1194 | } |
| | 1195 | } |
| | 1196 | printl "Module Reference Card", $ref_card; |
| | 1197 | } |
| | 1198 | if ($bytes->[3] == 1 || $bytes->[3] == 5) { |
| | 1199 | prints "Registered DIMM"; |
| | 1200 | |
| | 1201 | my @rows = ("Undefined", 1, 2, 4); |
| | 1202 | printl "# DRAM Rows", $rows[($bytes->[63] >> 2) & 3]; |
| | 1203 | printl "# Registers", $rows[$bytes->[63] & 3]; |
| | 1204 | printl "Register manufacturer", |
| | 1205 | manufacturer_ddr3($bytes->[65], $bytes->[66]); |
| | 1206 | printl "Register device type", |
| | 1207 | (($bytes->[68] & 7) == 0) ? "SSTE32882" : |
| | 1208 | "Undefined"; |
| | 1209 | printl "Register revision", sprintf("0x%.2X", $bytes->[67]); |
| | 1210 | printl "Heat spreader characteristics", |
| | 1211 | ($bytes->[64] < 128) ? "Not incorporated" : |
| | 1212 | sprintf("%.2X", ($bytes->[64] & 127)); |
| | 1213 | my $regs; |
| | 1214 | for (my $i = 0; $i < 8; $i++) { |
| | 1215 | $regs = sprintf("SSTE32882 RC%d/RC%d", |
| | 1216 | $i * 2, $i * 2 + 1); |
| | 1217 | printl $regs, sprintf("%.2X", $bytes->[$i + 69]); |
| | 1218 | } |
| | 1219 | } |
| | 1220 | } |
| | 1221 | |