root/i2c-tools/trunk/eeprom/decode-dimms @ 5929

Revision 5929, 63.8 KB (checked in by khali, 2 years ago)

Add support for the at24 kernel driver.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#!/usr/bin/perl -w
2#
3# EEPROM data decoder for SDRAM DIMM modules
4#
5# Copyright 1998, 1999 Philip Edelbrock <phil@netroedge.com>
6# modified by Christian Zuckschwerdt <zany@triq.net>
7# modified by Burkart Lingner <burkart@bollchen.de>
8# Copyright (C) 2005-2011  Jean Delvare <khali@linux-fr.org>
9#
10#    This program is free software; you can redistribute it and/or modify
11#    it under the terms of the GNU General Public License as published by
12#    the Free Software Foundation; either version 2 of the License, or
13#    (at your option) any later version.
14#
15#    This program is distributed in the hope that it will be useful,
16#    but WITHOUT ANY WARRANTY; without even the implied warranty of
17#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18#    GNU General Public License for more details.
19#
20#    You should have received a copy of the GNU General Public License
21#    along with this program; if not, write to the Free Software
22#    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23#    MA 02110-1301 USA.
24#
25#
26# The eeprom driver must be loaded (unless option -x is used). For kernels
27# older than 2.6.0, the eeprom driver can be found in the lm-sensors package.
28#
29# References:
30# PC SDRAM Serial Presence
31# Detect (SPD) Specification, Intel,
32# 1997,1999, Rev 1.2B
33#
34# Jedec Standards 4.1.x & 4.5.x
35# http://www.jedec.org
36#
37
38require 5.004;
39
40use strict;
41use POSIX qw(ceil);
42use Fcntl qw(:DEFAULT :seek);
43use vars qw($opt_html $opt_bodyonly $opt_side_by_side $opt_merge
44            $opt_igncheck $use_sysfs $use_hexdump $sbs_col_width
45            @vendors %decode_callback $revision @dimm $current %hexdump_cache);
46
47use constant LITTLEENDIAN       => "little-endian";
48use constant BIGENDIAN          => "big-endian";
49
50$revision = '$Revision$ ($Date$)';
51$revision =~ s/\$\w+: (.*?) \$/$1/g;
52$revision =~ s/ \([^()]*\)//;
53
54@vendors = (
55["AMD", "AMI", "Fairchild", "Fujitsu",
56 "GTE", "Harris", "Hitachi", "Inmos",
57 "Intel", "I.T.T.", "Intersil", "Monolithic Memories",
58 "Mostek", "Freescale (former Motorola)", "National", "NEC",
59 "RCA", "Raytheon", "Conexant (Rockwell)", "Seeq",
60 "NXP (former Signetics, Philips Semi.)", "Synertek", "Texas Instruments", "Toshiba",
61 "Xicor", "Zilog", "Eurotechnique", "Mitsubishi",
62 "Lucent (AT&T)", "Exel", "Atmel", "SGS/Thomson",
63 "Lattice Semi.", "NCR", "Wafer Scale Integration", "IBM",
64 "Tristar", "Visic", "Intl. CMOS Technology", "SSSI",
65 "MicrochipTechnology", "Ricoh Ltd.", "VLSI", "Micron Technology",
66 "Hyundai Electronics", "OKI Semiconductor", "ACTEL", "Sharp",
67 "Catalyst", "Panasonic", "IDT", "Cypress",
68 "DEC", "LSI Logic", "Zarlink (former Plessey)", "UTMC",
69 "Thinking Machine", "Thomson CSF", "Integrated CMOS (Vertex)", "Honeywell",
70 "Tektronix", "Sun Microsystems", "SST", "ProMos/Mosel Vitelic",
71 "Infineon (former Siemens)", "Macronix", "Xerox", "Plus Logic",
72 "SunDisk", "Elan Circuit Tech.", "European Silicon Str.", "Apple Computer",
73 "Xilinx", "Compaq", "Protocol Engines", "SCI",
74 "Seiko Instruments", "Samsung", "I3 Design System", "Klic",
75 "Crosspoint Solutions", "Alliance Semiconductor", "Tandem", "Hewlett-Packard",
76 "Intg. Silicon Solutions", "Brooktree", "New Media", "MHS Electronic",
77 "Performance Semi.", "Winbond Electronic", "Kawasaki Steel", "Bright Micro",
78 "TECMAR", "Exar", "PCMCIA", "LG Semi (former Goldstar)",
79 "Northern Telecom", "Sanyo", "Array Microsystems", "Crystal Semiconductor",
80 "Analog Devices", "PMC-Sierra", "Asparix", "Convex Computer",
81 "Quality Semiconductor", "Nimbus Technology", "Transwitch", "Micronas (ITT Intermetall)",
82 "Cannon", "Altera", "NEXCOM", "QUALCOMM",
83 "Sony", "Cray Research", "AMS(Austria Micro)", "Vitesse",
84 "Aster Electronics", "Bay Networks (Synoptic)", "Zentrum or ZMD", "TRW",
85 "Thesys", "Solbourne Computer", "Allied-Signal", "Dialog",
86 "Media Vision", "Level One Communication"],
87["Cirrus Logic", "National Instruments", "ILC Data Device", "Alcatel Mietec",
88 "Micro Linear", "Univ. of NC", "JTAG Technologies", "BAE Systems",
89 "Nchip", "Galileo Tech", "Bestlink Systems", "Graychip",
90 "GENNUM", "VideoLogic", "Robert Bosch", "Chip Express",
91 "DATARAM", "United Microelec Corp.", "TCSI", "Smart Modular",
92 "Hughes Aircraft", "Lanstar Semiconductor", "Qlogic", "Kingston",
93 "Music Semi", "Ericsson Components", "SpaSE", "Eon Silicon Devices",
94 "Programmable Micro Corp", "DoD", "Integ. Memories Tech.", "Corollary Inc.",
95 "Dallas Semiconductor", "Omnivision", "EIV(Switzerland)", "Novatel Wireless",
96 "Zarlink (former Mitel)", "Clearpoint", "Cabletron", "STEC (former Silicon Technology)",
97 "Vanguard", "Hagiwara Sys-Com", "Vantis", "Celestica",
98 "Century", "Hal Computers", "Rohm Company Ltd.", "Juniper Networks",
99 "Libit Signal Processing", "Mushkin Enhanced Memory", "Tundra Semiconductor", "Adaptec Inc.",
100 "LightSpeed Semi.", "ZSP Corp.", "AMIC Technology", "Adobe Systems",
101 "Dynachip", "PNY Electronics", "Newport Digital", "MMC Networks",
102 "T Square", "Seiko Epson", "Broadcom", "Viking Components",
103 "V3 Semiconductor", "Flextronics (former Orbit)", "Suwa Electronics", "Transmeta",
104 "Micron CMS", "American Computer & Digital Components Inc", "Enhance 3000 Inc", "Tower Semiconductor",
105 "CPU Design", "Price Point", "Maxim Integrated Product", "Tellabs",
106 "Centaur Technology", "Unigen Corporation", "Transcend Information", "Memory Card Technology",
107 "CKD Corporation Ltd.", "Capital Instruments, Inc.", "Aica Kogyo, Ltd.", "Linvex Technology",
108 "MSC Vertriebs GmbH", "AKM Company, Ltd.", "Dynamem, Inc.", "NERA ASA",
109 "GSI Technology", "Dane-Elec (C Memory)", "Acorn Computers", "Lara Technology",
110 "Oak Technology, Inc.", "Itec Memory", "Tanisys Technology", "Truevision",
111 "Wintec Industries", "Super PC Memory", "MGV Memory", "Galvantech",
112 "Gadzoox Nteworks", "Multi Dimensional Cons.", "GateField", "Integrated Memory System",
113 "Triscend", "XaQti", "Goldenram", "Clear Logic",
114 "Cimaron Communications", "Nippon Steel Semi. Corp.", "Advantage Memory", "AMCC",
115 "LeCroy", "Yamaha Corporation", "Digital Microwave", "NetLogic Microsystems",
116 "MIMOS Semiconductor", "Advanced Fibre", "BF Goodrich Data.", "Epigram",
117 "Acbel Polytech Inc.", "Apacer Technology", "Admor Memory", "FOXCONN",
118 "Quadratics Superconductor", "3COM"],
119["Camintonn Corporation", "ISOA Incorporated", "Agate Semiconductor", "ADMtek Incorporated",
120 "HYPERTEC", "Adhoc Technologies", "MOSAID Technologies", "Ardent Technologies",
121 "Switchcore", "Cisco Systems, Inc.", "Allayer Technologies", "WorkX AG",
122 "Oasis Semiconductor", "Novanet Semiconductor", "E-M Solutions", "Power General",
123 "Advanced Hardware Arch.", "Inova Semiconductors GmbH", "Telocity", "Delkin Devices",
124 "Symagery Microsystems", "C-Port Corporation", "SiberCore Technologies", "Southland Microsystems",
125 "Malleable Technologies", "Kendin Communications", "Great Technology Microcomputer", "Sanmina Corporation",
126 "HADCO Corporation", "Corsair", "Actrans System Inc.", "ALPHA Technologies",
127 "Silicon Laboratories, Inc. (Cygnal)", "Artesyn Technologies", "Align Manufacturing", "Peregrine Semiconductor",
128 "Chameleon Systems", "Aplus Flash Technology", "MIPS Technologies", "Chrysalis ITS",
129 "ADTEC Corporation", "Kentron Technologies", "Win Technologies", "Tachyon Semiconductor (former ASIC Designs Inc.)",
130 "Extreme Packet Devices", "RF Micro Devices", "Siemens AG", "Sarnoff Corporation",
131 "Itautec Philco SA", "Radiata Inc.", "Benchmark Elect. (AVEX)", "Legend",
132 "SpecTek Incorporated", "Hi/fn", "Enikia Incorporated", "SwitchOn Networks",
133 "AANetcom Incorporated", "Micro Memory Bank", "ESS Technology", "Virata Corporation",
134 "Excess Bandwidth", "West Bay Semiconductor", "DSP Group", "Newport Communications",
135 "Chip2Chip Incorporated", "Phobos Corporation", "Intellitech Corporation", "Nordic VLSI ASA",
136 "Ishoni Networks", "Silicon Spice", "Alchemy Semiconductor", "Agilent Technologies",
137 "Centillium Communications", "W.L. Gore", "HanBit Electronics", "GlobeSpan",
138 "Element 14", "Pycon", "Saifun Semiconductors", "Sibyte, Incorporated",
139 "MetaLink Technologies", "Feiya Technology", "I & C Technology", "Shikatronics",
140 "Elektrobit", "Megic", "Com-Tier", "Malaysia Micro Solutions",
141 "Hyperchip", "Gemstone Communications", "Anadigm (former Anadyne)", "3ParData",
142 "Mellanox Technologies", "Tenx Technologies", "Helix AG", "Domosys",
143 "Skyup Technology", "HiNT Corporation", "Chiaro", "MDT Technologies GmbH (former MCI Computer GMBH)",
144 "Exbit Technology A/S", "Integrated Technology Express", "AVED Memory", "Legerity",
145 "Jasmine Networks", "Caspian Networks", "nCUBE", "Silicon Access Networks",
146 "FDK Corporation", "High Bandwidth Access", "MultiLink Technology", "BRECIS",
147 "World Wide Packets", "APW", "Chicory Systems", "Xstream Logic",
148 "Fast-Chip", "Zucotto Wireless", "Realchip", "Galaxy Power",
149 "eSilicon", "Morphics Technology", "Accelerant Networks", "Silicon Wave",
150 "SandCraft", "Elpida"],
151["Solectron", "Optosys Technologies", "Buffalo (former Melco)", "TriMedia Technologies",
152 "Cyan Technologies", "Global Locate", "Optillion", "Terago Communications",
153 "Ikanos Communications", "Princeton Technology", "Nanya Technology", "Elite Flash Storage",
154 "Mysticom", "LightSand Communications", "ATI Technologies", "Agere Systems",
155 "NeoMagic", "AuroraNetics", "Golden Empire", "Mushkin",
156 "Tioga Technologies", "Netlist", "TeraLogic", "Cicada Semiconductor",
157 "Centon Electronics", "Tyco Electronics", "Magis Works", "Zettacom",
158 "Cogency Semiconductor", "Chipcon AS", "Aspex Technology", "F5 Networks",
159 "Programmable Silicon Solutions", "ChipWrights", "Acorn Networks", "Quicklogic",
160 "Kingmax Semiconductor", "BOPS", "Flasys", "BitBlitz Communications",
161 "eMemory Technology", "Procket Networks", "Purple Ray", "Trebia Networks",
162 "Delta Electronics", "Onex Communications", "Ample Communications", "Memory Experts Intl",
163 "Astute Networks", "Azanda Network Devices", "Dibcom", "Tekmos",
164 "API NetWorks", "Bay Microsystems", "Firecron Ltd", "Resonext Communications",
165 "Tachys Technologies", "Equator Technology", "Concept Computer", "SILCOM",
166 "3Dlabs", "c't Magazine", "Sanera Systems", "Silicon Packets",
167 "Viasystems Group", "Simtek", "Semicon Devices Singapore", "Satron Handelsges",
168 "Improv Systems", "INDUSYS GmbH", "Corrent", "Infrant Technologies",
169 "Ritek Corp", "empowerTel Networks", "Hypertec", "Cavium Networks",
170 "PLX Technology", "Massana Design", "Intrinsity", "Valence Semiconductor",
171 "Terawave Communications", "IceFyre Semiconductor", "Primarion", "Picochip Designs Ltd",
172 "Silverback Systems", "Jade Star Technologies", "Pijnenburg Securealink",
173 "TakeMS International AG", "Cambridge Silicon Radio",
174 "Swissbit", "Nazomi Communications", "eWave System",
175 "Rockwell Collins", "Picocel Co., Ltd.", "Alphamosaic Ltd", "Sandburst",
176 "SiCon Video", "NanoAmp Solutions", "Ericsson Technology", "PrairieComm",
177 "Mitac International", "Layer N Networks", "MtekVision", "Allegro Networks",
178 "Marvell Semiconductors", "Netergy Microelectronic", "NVIDIA", "Internet Machines",
179 "Peak Electronics", "Litchfield Communication", "Accton Technology", "Teradiant Networks",
180 "Europe Technologies", "Cortina Systems", "RAM Components", "Raqia Networks",
181 "ClearSpeed", "Matsushita Battery", "Xelerated", "SimpleTech",
182 "Utron Technology", "Astec International", "AVM gmbH", "Redux Communications",
183 "Dot Hill Systems", "TeraChip"],
184["T-RAM Incorporated", "Innovics Wireless", "Teknovus", "KeyEye Communications",
185 "Runcom Technologies", "RedSwitch", "Dotcast", "Silicon Mountain Memory",
186 "Signia Technologies", "Pixim", "Galazar Networks", "White Electronic Designs",
187 "Patriot Scientific", "Neoaxiom Corporation", "3Y Power Technology", "Europe Technologies",
188 "Potentia Power Systems", "C-guys Incorporated", "Digital Communications Technology Incorporated", "Silicon-Based Technology",
189 "Fulcrum Microsystems", "Positivo Informatica Ltd", "XIOtech Corporation", "PortalPlayer",
190 "Zhiying Software", "Direct2Data", "Phonex Broadband", "Skyworks Solutions",
191 "Entropic Communications", "Pacific Force Technology", "Zensys A/S", "Legend Silicon Corp.",
192 "sci-worx GmbH", "SMSC (former Oasis Silicon Systems)", "Renesas Technology", "Raza Microelectronics",
193 "Phyworks", "MediaTek", "Non-cents Productions", "US Modular",
194 "Wintegra Ltd", "Mathstar", "StarCore", "Oplus Technologies",
195 "Mindspeed", "Just Young Computer", "Radia Communications", "OCZ",
196 "Emuzed", "LOGIC Devices", "Inphi Corporation", "Quake Technologies",
197 "Vixel", "SolusTek", "Kongsberg Maritime", "Faraday Technology",
198 "Altium Ltd.", "Insyte", "ARM Ltd.", "DigiVision",
199 "Vativ Technologies", "Endicott Interconnect Technologies", "Pericom", "Bandspeed",
200 "LeWiz Communications", "CPU Technology", "Ramaxel Technology", "DSP Group",
201 "Axis Communications", "Legacy Electronics", "Chrontel", "Powerchip Semiconductor",
202 "MobilEye Technologies", "Excel Semiconductor", "A-DATA Technology", "VirtualDigm",
203 "G Skill Intl", "Quanta Computer", "Yield Microelectronics", "Afa Technologies",
204 "KINGBOX Technology Co. Ltd.", "Ceva", "iStor Networks", "Advance Modules",
205 "Microsoft", "Open-Silicon", "Goal Semiconductor", "ARC International",
206 "Simmtec", "Metanoia", "Key Stream", "Lowrance Electronics",
207 "Adimos", "SiGe Semiconductor", "Fodus Communications", "Credence Systems Corp.",
208 "Genesis Microchip Inc.", "Vihana, Inc.", "WIS Technologies", "GateChange Technologies",
209 "High Density Devices AS", "Synopsys", "Gigaram", "Enigma Semiconductor Inc.",
210 "Century Micro Inc.", "Icera Semiconductor", "Mediaworks Integrated Systems", "O'Neil Product Development",
211 "Supreme Top Technology Ltd.", "MicroDisplay Corporation", "Team Group Inc.", "Sinett Corporation",
212 "Toshiba Corporation", "Tensilica", "SiRF Technology", "Bacoc Inc.",
213 "SMaL Camera Technologies", "Thomson SC", "Airgo Networks", "Wisair Ltd.",
214 "SigmaTel", "Arkados", "Compete IT gmbH Co. KG", "Eudar Technology Inc.",
215 "Focus Enhancements", "Xyratex"],
216["Specular Networks", "Patriot Memory", "U-Chip Technology Corp.", "Silicon Optix",
217 "Greenfield Networks", "CompuRAM GmbH", "Stargen, Inc.", "NetCell Corporation",
218 "Excalibrus Technologies Ltd", "SCM Microsystems", "Xsigo Systems, Inc.", "CHIPS & Systems Inc",
219 "Tier 1 Multichip Solutions", "CWRL Labs", "Teradici", "Gigaram, Inc.",
220 "g2 Microsystems", "PowerFlash Semiconductor", "P.A. Semi, Inc.", "NovaTech Solutions, S.A.",
221 "c2 Microsystems, Inc.", "Level5 Networks", "COS Memory AG", "Innovasic Semiconductor",
222 "02IC Co. Ltd", "Tabula, Inc.", "Crucial Technology", "Chelsio Communications",
223 "Solarflare Communications", "Xambala Inc.", "EADS Astrium", "ATO Semicon Co. Ltd.",
224 "Imaging Works, Inc.", "Astute Networks, Inc.", "Tzero", "Emulex",
225 "Power-One", "Pulse~LINK Inc.", "Hon Hai Precision Industry", "White Rock Networks Inc.",
226 "Telegent Systems USA, Inc.", "Atrua Technologies, Inc.", "Acbel Polytech Inc.",
227 "eRide Inc.","ULi Electronics Inc.", "Magnum Semiconductor Inc.", "neoOne Technology, Inc.",
228 "Connex Technology, Inc.", "Stream Processors, Inc.", "Focus Enhancements", "Telecis Wireless, Inc.",
229 "uNav Microelectronics", "Tarari, Inc.", "Ambric, Inc.", "Newport Media, Inc.", "VMTS",
230 "Enuclia Semiconductor, Inc.", "Virtium Technology Inc.", "Solid State System Co., Ltd.", "Kian Tech LLC",
231 "Artimi", "Power Quotient International", "Avago Technologies", "ADTechnology", "Sigma Designs",
232 "SiCortex, Inc.", "Ventura Technology Group", "eASIC", "M.H.S. SAS", "Micro Star International",
233 "Rapport Inc.", "Makway International", "Broad Reach Engineering Co.",
234 "Semiconductor Mfg Intl Corp", "SiConnect", "FCI USA Inc.", "Validity Sensors",
235 "Coney Technology Co. Ltd.", "Spans Logic", "Neterion Inc.", "Qimonda",
236 "New Japan Radio Co. Ltd.", "Velogix", "Montalvo Systems", "iVivity Inc.", "Walton Chaintech",
237 "AENEON", "Lorom Industrial Co. Ltd.", "Radiospire Networks", "Sensio Technologies, Inc.",
238 "Nethra Imaging", "Hexon Technology Pte Ltd", "CompuStocx (CSX)", "Methode Electronics, Inc.",
239 "Connect One Ltd.", "Opulan Technologies", "Septentrio NV", "Goldenmars Technology Inc.",
240 "Kreton Corporation", "Cochlear Ltd.", "Altair Semiconductor", "NetEffect, Inc.",
241 "Spansion, Inc.", "Taiwan Semiconductor Mfg", "Emphany Systems Inc.",
242 "ApaceWave Technologies", "Mobilygen Corporation", "Tego", "Cswitch Corporation",
243 "Haier (Beijing) IC Design Co.", "MetaRAM", "Axel Electronics Co. Ltd.", "Tilera Corporation",
244 "Aquantia", "Vivace Semiconductor", "Redpine Signals", "Octalica", "InterDigital Communications",
245 "Avant Technology", "Asrock, Inc.", "Availink", "Quartics, Inc.", "Element CXI",
246 "Innovaciones Microelectronicas", "VeriSilicon Microelectronics", "W5 Networks"],
247["MOVEKING", "Mavrix Technology, Inc.", "CellGuide Ltd.", "Faraday Technology",
248 "Diablo Technologies, Inc.", "Jennic", "Octasic", "Molex Incorporated", "3Leaf Networks",
249 "Bright Micron Technology", "Netxen", "NextWave Broadband Inc.", "DisplayLink", "ZMOS Technology",
250 "Tec-Hill", "Multigig, Inc.", "Amimon", "Euphonic Technologies, Inc.", "BRN Phoenix",
251 "InSilica", "Ember Corporation", "Avexir Technologies Corporation", "Echelon Corporation",
252 "Edgewater Computer Systems", "XMOS Semiconductor Ltd.", "GENUSION, Inc.", "Memory Corp NV",
253 "SiliconBlue Technologies", "Rambus Inc."]);
254
255$use_sysfs = -d '/sys/bus';
256
257# We consider that no data was written to this area of the SPD EEPROM if
258# all bytes read 0x00 or all bytes read 0xff
259sub spd_written(@)
260{
261        my $all_00 = 1;
262        my $all_ff = 1;
263
264        foreach my $b (@_) {
265                $all_00 = 0 unless $b == 0x00;
266                $all_ff = 0 unless $b == 0xff;
267                return 1 unless $all_00 or $all_ff;
268        }
269
270        return 0;
271}
272
273sub parity($)
274{
275        my $n = shift;
276        my $parity = 0;
277
278        while ($n) {
279                $parity++ if ($n & 1);
280                $n >>= 1;
281        }
282
283        return ($parity & 1);
284}
285
286# New encoding format (as of DDR3) for manufacturer just has a count of
287# leading 0x7F rather than all the individual bytes.  The count bytes includes
288# parity!
289sub manufacturer_ddr3($$)
290{
291        my ($count, $code) = @_;
292        return "Invalid" if parity($count) != 1;
293        return "Invalid" if parity($code) != 1;
294        return (($code & 0x7F) - 1 > $vendors[$count & 0x7F]) ? "Unknown" :
295                $vendors[$count & 0x7F][($code & 0x7F) - 1];
296}
297
298sub manufacturer(@)
299{
300        my @bytes = @_;
301        my $ai = 0;
302        my $first;
303
304        return ("Undefined", []) unless spd_written(@bytes);
305
306        while (defined($first = shift(@bytes)) && $first == 0x7F) {
307                $ai++;
308        }
309
310        return ("Invalid", []) unless defined $first;
311        return ("Invalid", [$first, @bytes]) if parity($first) != 1;
312        if (parity($ai) == 0) {
313                $ai |= 0x80;
314        }
315        return (manufacturer_ddr3($ai, $first), \@bytes);
316}
317
318sub manufacturer_data(@)
319{
320        my $hex = "";
321        my $asc = "";
322
323        return unless spd_written(@_);
324
325        foreach my $byte (@_) {
326                $hex .= sprintf("\%02X ", $byte);
327                $asc .= ($byte >= 32 && $byte < 127) ? chr($byte) : '?';
328        }
329
330        return "$hex(\"$asc\")";
331}
332
333sub part_number(@)
334{
335        my $asc = "";
336        my $byte;
337
338        while (defined ($byte = shift) && $byte >= 32 && $byte < 127) {
339                $asc .= chr($byte);
340        }
341
342        return ($asc eq "") ? "Undefined" : $asc;
343}
344
345sub cas_latencies(@)
346{
347        return "None" unless @_;
348        return join ', ', map("${_}T", sort { $b <=> $a } @_);
349}
350
351# Real printing functions
352
353sub html_encode($)
354{
355        my $text = shift;
356        $text =~ s/</\&lt;/sg;
357        $text =~ s/>/\&gt;/sg;
358        $text =~ s/\n/<br>\n/sg;
359        return $text;
360}
361
362sub same_values(@)
363{
364        my $value = shift;
365        while (@_) {
366                return 0 unless $value eq shift;
367        }
368        return 1;
369}
370
371sub real_printl($$) # print a line w/ label and values
372{
373        my ($label, @values) = @_;
374        local $_;
375        my $same_values = same_values(@values);
376
377        # If all values are N/A, don't bother printing
378        return if $values[0] eq "N/A" and $same_values;
379
380        if ($opt_html) {
381                $label = html_encode($label);
382                @values = map { html_encode($_) } @values;
383                print "<tr><td valign=top>$label</td>";
384                if ($opt_merge && $same_values) {
385                        print "<td colspan=".(scalar @values).">$values[0]</td>";
386                } else {
387                        print "<td>$_</td>" foreach @values;
388                }
389                print "</tr>\n";
390        } else {
391                if ($opt_merge && $same_values) {
392                        splice(@values, 1);
393                }
394
395                my $format = "%-47s".((" %-".$sbs_col_width."s") x (scalar @values - 1))." %s\n";
396                my $maxl = 0; # Keep track of the max number of lines
397
398                # It's a bit tricky because each value may span over more than
399                # one line. We can easily extract the values per column, but
400                # we need them per line at printing time. So we have to
401                # prepare a 2D array with all the individual string fragments.
402                my ($col, @lines);
403                for ($col = 0; $col < @values; $col++) {
404                        my @cells = split /\n/, $values[$col];
405                        $maxl = @cells if @cells > $maxl;
406                        for (my $l = 0; $l < @cells; $l++) {
407                                $lines[$l]->[$col] = $cells[$l];
408                        }
409                }
410
411                # Also make sure there are no holes in the array
412                for (my $l = 0; $l < $maxl; $l++) {
413                        for ($col = 0; $col < @values; $col++) {
414                                $lines[$l]->[$col] = ""
415                                        if not defined $lines[$l]->[$col];
416                        }
417                }
418
419                printf $format, $label, @{shift @lines};
420                printf $format, "", @{$_} foreach (@lines);
421        }
422}
423
424sub printl2($$) # print a line w/ label and value (outside a table)
425{
426        my ($label, $value) = @_;
427        if ($opt_html) {
428                $label = html_encode($label);
429                $value = html_encode($value);
430        }
431        print "$label: $value\n";
432}
433
434sub real_prints($) # print separator w/ given text
435{
436        my ($label, $ncol) = @_;
437        $ncol = 1 unless $ncol;
438        if ($opt_html) {
439                $label = html_encode($label);
440                print "<tr><td align=center colspan=".(1+$ncol)."><b>$label</b></td></tr>\n";
441        } else {
442                print "\n---=== $label ===---\n";
443        }
444}
445
446sub printh($$) # print header w/ given text
447{
448        my ($header, $sub) = @_;
449        if ($opt_html) {
450                $header = html_encode($header);
451                $sub = html_encode($sub);
452                print "<h1>$header</h1>\n";
453                print "<p>$sub</p>\n";
454        } else {
455                print "\n$header\n$sub\n";
456        }
457}
458
459sub printc($) # print comment
460{
461        my ($comment) = @_;
462        if ($opt_html) {
463                $comment = html_encode($comment);
464                print "<!-- $comment -->\n";
465        } else {
466                print "# $comment\n";
467        }
468}
469
470# Fake printing functions
471# These don't actually print anything, instead they store the desired
472# output for later processing.
473
474sub printl($$) # print a line w/ label and value
475{
476        my @output = (\&real_printl, @_);
477        push @{$dimm[$current]->{output}}, \@output;
478}
479
480sub printl_cond($$$) # same as printl but conditional
481{
482        my ($cond, $label, $value) = @_;
483        return unless $cond || $opt_side_by_side;
484        printl($label, $cond ? $value : "N/A");
485}
486
487sub prints($) # print separator w/ given text
488{
489        my @output = (\&real_prints, @_);
490        push @{$dimm[$current]->{output}}, \@output;
491}
492
493# Helper functions
494
495sub tns($) # print a time in ns
496{
497        return sprintf("%3.2f ns", $_[0]);
498}
499
500sub tns3($) # print a time in ns, with 3 decimal digits
501{
502        return sprintf("%.3f ns", $_[0]);
503}
504
505sub value_or_undefined
506{
507        my ($value, $unit) = @_;
508        return "Undefined!" unless $value;
509        $value .= " $unit" if defined $unit;
510        return $value;
511}
512
513# Common to SDR, DDR and DDR2 SDRAM
514sub sdram_voltage_interface_level($)
515{
516        my @levels = (
517                "TTL (5V tolerant)",            #  0
518                "LVTTL (not 5V tolerant)",      #  1
519                "HSTL 1.5V",                    #  2
520                "SSTL 3.3V",                    #  3
521                "SSTL 2.5V",                    #  4
522                "SSTL 1.8V",                    #  5
523        );
524       
525        return ($_[0] < @levels) ? $levels[$_[0]] : "Undefined!";
526}
527
528# Common to SDR and DDR SDRAM
529sub sdram_module_configuration_type($)
530{
531        my @types = (
532                "No Parity",                    # 0
533                "Parity",                       # 1
534                "ECC",                          # 2
535        );
536
537        return ($_[0] < @types) ? $types[$_[0]] : "Undefined!";
538}
539
540# Parameter: EEPROM bytes 0-127 (using 3-62)
541sub decode_sdr_sdram($)
542{
543        my $bytes = shift;
544        my $temp;
545
546# SPD revision
547        printl("SPD Revision", $bytes->[62]);
548
549#size computation
550
551        prints("Memory Characteristics");
552
553        my $k = 0;
554        my $ii = 0;
555
556        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
557        if (($bytes->[5] <= 8) && ($bytes->[17] <= 8)) {
558                 $k = $bytes->[5] * $bytes->[17];
559        }
560
561        if ($ii > 0 && $ii <= 12 && $k > 0) {
562                printl("Size", ((1 << $ii) * $k) . " MB");
563        } else {
564                printl("Size", "INVALID: " . $bytes->[3] . "," . $bytes->[4] . "," .
565                               $bytes->[5] . "," . $bytes->[17]);
566        }
567
568        my @cas;
569        for ($ii = 0; $ii < 7; $ii++) {
570                push(@cas, $ii + 1) if ($bytes->[18] & (1 << $ii));
571        }
572
573        my $trcd;
574        my $trp;
575        my $tras;
576        my $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
577
578        $trcd = $bytes->[29];
579        $trp = $bytes->[27];;
580        $tras = $bytes->[30];
581
582        printl("tCL-tRCD-tRP-tRAS",
583                $cas[$#cas] . "-" .
584                ceil($trcd/$ctime) . "-" .
585                ceil($trp/$ctime) . "-" .
586                ceil($tras/$ctime));
587
588        if ($bytes->[3] == 0) { $temp = "Undefined!"; }
589        elsif ($bytes->[3] == 1) { $temp = "1/16"; }
590        elsif ($bytes->[3] == 2) { $temp = "2/17"; }
591        elsif ($bytes->[3] == 3) { $temp = "3/18"; }
592        else { $temp = $bytes->[3]; }
593        printl("Number of Row Address Bits", $temp);
594
595        if ($bytes->[4] == 0) { $temp = "Undefined!"; }
596        elsif ($bytes->[4] == 1) { $temp = "1/16"; }
597        elsif ($bytes->[4] == 2) { $temp = "2/17"; }
598        elsif ($bytes->[4] == 3) { $temp = "3/18"; }
599        else { $temp = $bytes->[4]; }
600        printl("Number of Col Address Bits", $temp);
601
602        printl("Number of Module Rows", value_or_undefined($bytes->[5]));
603
604        if ($bytes->[7] > 1) { $temp = "Undefined!"; }
605        else { $temp = ($bytes->[7] * 256) + $bytes->[6]; }
606        printl("Data Width", $temp);
607
608        printl("Voltage Interface Level",
609               sdram_voltage_interface_level($bytes->[8]));
610
611        printl("Module Configuration Type",
612               sdram_module_configuration_type($bytes->[11]));
613
614        printl("Refresh Rate", ddr2_refresh_rate($bytes->[12]));
615
616        if ($bytes->[13] & 0x80) { $temp = "Bank2 = 2 x Bank1"; }
617        else { $temp = "No Bank2 OR Bank2 = Bank1 width"; }
618        printl("Primary SDRAM Component Bank Config", $temp);
619        printl("Primary SDRAM Component Widths",
620               value_or_undefined($bytes->[13] & 0x7f));
621
622        if ($bytes->[14] & 0x80) { $temp = "Bank2 = 2 x Bank1"; }
623        else { $temp = "No Bank2 OR Bank2 = Bank1 width"; }
624        printl("Error Checking SDRAM Component Bank Config", $temp);
625        printl("Error Checking SDRAM Component Widths",
626               value_or_undefined($bytes->[14] & 0x7f));
627
628        printl("Min Clock Delay for Back to Back Random Access",
629               value_or_undefined($bytes->[15]));
630
631        my @array;
632        for ($ii = 0; $ii < 4; $ii++) {
633                push(@array, 1 << $ii) if ($bytes->[16] & (1 << $ii));
634        }
635        push(@array, "Page") if ($bytes->[16] & 128);
636        if (@array) { $temp = join ', ', @array; }
637        else { $temp = "None"; }
638        printl("Supported Burst Lengths", $temp);
639
640        printl("Number of Device Banks",
641               value_or_undefined($bytes->[17]));
642
643        printl("Supported CAS Latencies", cas_latencies(@cas));
644
645        @array = ();
646        for ($ii = 0; $ii < 7; $ii++) {
647                push(@array, $ii) if ($bytes->[19] & (1 << $ii));
648        }
649        if (@array) { $temp = join ', ', @array; }
650        else { $temp = "None"; }
651        printl("Supported CS Latencies", $temp);
652
653        @array = ();
654        for ($ii = 0; $ii < 7; $ii++) {
655                push(@array, $ii) if ($bytes->[20] & (1 << $ii));
656        }
657        if (@array) { $temp = join ', ', @array; }
658        else { $temp = "None"; }
659        printl("Supported WE Latencies", $temp);
660
661        my ($cycle_time, $access_time);
662
663        if (@cas >= 1) {
664                $cycle_time = "$ctime ns at CAS ".$cas[$#cas];
665
666                $temp = ($bytes->[10] >> 4) + ($bytes->[10] & 0xf) * 0.1;
667                $access_time = "$temp ns at CAS ".$cas[$#cas];
668        }
669
670        if (@cas >= 2 && spd_written(@$bytes[23..24])) {
671                $temp = $bytes->[23] >> 4;
672                if ($temp == 0) { $temp = "Undefined!"; }
673                else {
674                        $temp += 15 if $temp < 4;
675                        $temp += ($bytes->[23] & 0xf) * 0.1;
676                        $temp .= " ns";
677                }
678                $cycle_time .= "\n$temp ns at CAS ".$cas[$#cas-1];
679
680                $temp = $bytes->[24] >> 4;
681                if ($temp == 0) { $temp = "Undefined!"; }
682                else {
683                        $temp += 15 if $temp < 4;
684                        $temp += ($bytes->[24] & 0xf) * 0.1;
685                        $temp .= " ns";
686                }
687                $access_time .= "\n$temp ns at CAS ".$cas[$#cas-1];
688        }
689
690        if (@cas >= 3 && spd_written(@$bytes[25..26])) {
691                $temp = $bytes->[25] >> 2;
692                if ($temp == 0) { $temp = "Undefined!"; }
693                else {
694                        $temp += ($bytes->[25] & 0x3) * 0.25;
695                        $temp .= " ns";
696                }
697                $cycle_time .= "\n$temp ns at CAS ".$cas[$#cas-2];
698
699                $temp = $bytes->[26] >> 2;
700                if ($temp == 0) { $temp = "Undefined!"; }
701                else {
702                        $temp += ($bytes->[26] & 0x3) * 0.25;
703                        $temp .= " ns";
704                }
705                $access_time .= "\n$temp ns at CAS ".$cas[$#cas-2];
706        }
707
708        printl_cond(defined $cycle_time, "Cycle Time", $cycle_time);
709        printl_cond(defined $access_time, "Access Time", $access_time);
710
711        $temp = "";
712        if ($bytes->[21] & 1) { $temp .= "Buffered Address/Control Inputs\n"; }
713        if ($bytes->[21] & 2) { $temp .= "Registered Address/Control Inputs\n"; }
714        if ($bytes->[21] & 4) { $temp .= "On card PLL (clock)\n"; }
715        if ($bytes->[21] & 8) { $temp .= "Buffered DQMB Inputs\n"; }
716        if ($bytes->[21] & 16) { $temp .= "Registered DQMB Inputs\n"; }
717        if ($bytes->[21] & 32) { $temp .= "Differential Clock Input\n"; }
718        if ($bytes->[21] & 64) { $temp .= "Redundant Row Address\n"; }
719        if ($bytes->[21] & 128) { $temp .= "Undefined (bit 7)\n"; }
720        if ($bytes->[21] == 0) { $temp .= "(None Reported)\n"; }
721        printl("SDRAM Module Attributes", $temp);
722
723        $temp = "";
724        if ($bytes->[22] & 1) { $temp .= "Supports Early RAS# Recharge\n"; }
725        if ($bytes->[22] & 2) { $temp .= "Supports Auto-Precharge\n"; }
726        if ($bytes->[22] & 4) { $temp .= "Supports Precharge All\n"; }
727        if ($bytes->[22] & 8) { $temp .= "Supports Write1/Read Burst\n"; }
728        if ($bytes->[22] & 16) { $temp .= "Lower VCC Tolerance: 5%\n"; }
729        else { $temp .= "Lower VCC Tolerance: 10%\n"; }
730        if ($bytes->[22] & 32) { $temp .= "Upper VCC Tolerance: 5%\n"; }
731        else { $temp .= "Upper VCC Tolerance: 10%\n"; }
732        if ($bytes->[22] & 64) { $temp .= "Undefined (bit 6)\n"; }
733        if ($bytes->[22] & 128) { $temp .= "Undefined (bit 7)\n"; }
734        printl("SDRAM Device Attributes (General)", $temp);
735
736        printl("Minimum Row Precharge Time",
737               value_or_undefined($bytes->[27], "ns"));
738
739        printl("Row Active to Row Active Min",
740               value_or_undefined($bytes->[28], "ns"));
741
742        printl("RAS to CAS Delay",
743               value_or_undefined($bytes->[29], "ns"));
744
745        printl("Min RAS Pulse Width",
746               value_or_undefined($bytes->[30], "ns"));
747
748        $temp = "";
749        if ($bytes->[31] & 1) { $temp .= "4 MByte\n"; }
750        if ($bytes->[31] & 2) { $temp .= "8 MByte\n"; }
751        if ($bytes->[31] & 4) { $temp .= "16 MByte\n"; }
752        if ($bytes->[31] & 8) { $temp .= "32 MByte\n"; }
753        if ($bytes->[31] & 16) { $temp .= "64 MByte\n"; }
754        if ($bytes->[31] & 32) { $temp .= "128 MByte\n"; }
755        if ($bytes->[31] & 64) { $temp .= "256 MByte\n"; }
756        if ($bytes->[31] & 128) { $temp .= "512 MByte\n"; }
757        if ($bytes->[31] == 0) { $temp .= "(Undefined! -- None Reported!)\n"; }
758        printl("Row Densities", $temp);
759
760        $temp = (($bytes->[32] & 0x7f) >> 4) + ($bytes->[32] & 0xf) * 0.1;
761        printl_cond(($bytes->[32] & 0xf) <= 9,
762                    "Command and Address Signal Setup Time",
763                    (($bytes->[32] >> 7) ? -$temp : $temp) . " ns");
764
765        $temp = (($bytes->[33] & 0x7f) >> 4) + ($bytes->[33] & 0xf) * 0.1;
766        printl_cond(($bytes->[33] & 0xf) <= 9,
767                    "Command and Address Signal Hold Time",
768                    (($bytes->[33] >> 7) ? -$temp : $temp) . " ns");
769
770        $temp = (($bytes->[34] & 0x7f) >> 4) + ($bytes->[34] & 0xf) * 0.1;
771        printl_cond(($bytes->[34] & 0xf) <= 9, "Data Signal Setup Time",
772                    (($bytes->[34] >> 7) ? -$temp : $temp) . " ns");
773
774        $temp = (($bytes->[35] & 0x7f) >> 4) + ($bytes->[35] & 0xf) * 0.1;
775        printl_cond(($bytes->[35] & 0xf) <= 9, "Data Signal Hold Time",
776                    (($bytes->[35] >> 7) ? -$temp : $temp) . " ns");
777}
778
779# Parameter: EEPROM bytes 0-127 (using 3-62)
780sub decode_ddr_sdram($)
781{
782        my $bytes = shift;
783        my $temp;
784
785# SPD revision
786        printl_cond($bytes->[62] != 0xff, "SPD Revision",
787                    ($bytes->[62] >> 4) . "." . ($bytes->[62] & 0xf));
788
789# speed
790        prints("Memory Characteristics");
791
792        $temp = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
793        my $ddrclk = 2 * (1000 / $temp);
794        my $tbits = ($bytes->[7] * 256) + $bytes->[6];
795        if (($bytes->[11] == 2) || ($bytes->[11] == 1)) { $tbits = $tbits - 8; }
796        my $pcclk = int ($ddrclk * $tbits / 8);
797        $pcclk += 100 if ($pcclk % 100) >= 50; # Round properly
798        $pcclk = $pcclk - ($pcclk % 100);
799        $ddrclk = int ($ddrclk);
800        printl("Maximum module speed", "${ddrclk}MHz (PC${pcclk})");
801
802#size computation
803        my $k = 0;
804        my $ii = 0;
805
806        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
807        if (($bytes->[5] <= 8) && ($bytes->[17] <= 8)) {
808                 $k = $bytes->[5] * $bytes->[17];
809        }
810
811        if ($ii > 0 && $ii <= 12 && $k > 0) {
812                printl("Size", ((1 << $ii) * $k) . " MB");
813        } else {
814                printl("Size", "INVALID: " . $bytes->[3] . ", " . $bytes->[4] . ", " .
815                               $bytes->[5] . ", " . $bytes->[17]);
816        }
817
818        printl("Voltage Interface Level",
819               sdram_voltage_interface_level($bytes->[8]));
820
821        printl("Module Configuration Type",
822               sdram_module_configuration_type($bytes->[11]));
823
824        printl("Refresh Rate", ddr2_refresh_rate($bytes->[12]));
825
826        my $highestCAS = 0;
827        my %cas;
828        for ($ii = 0; $ii < 7; $ii++) {
829                if ($bytes->[18] & (1 << $ii)) {
830                        $highestCAS = 1+$ii*0.5;
831                        $cas{$highestCAS}++;
832                }
833        }
834
835        my $trcd;
836        my $trp;
837        my $tras;
838        my $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
839
840        $trcd = ($bytes->[29] >> 2) + (($bytes->[29] & 3) * 0.25);
841        $trp = ($bytes->[27] >> 2) + (($bytes->[27] & 3) * 0.25);
842        $tras = $bytes->[30];
843
844        printl("tCL-tRCD-tRP-tRAS",
845                $highestCAS . "-" .
846                ceil($trcd/$ctime) . "-" .
847                ceil($trp/$ctime) . "-" .
848                ceil($tras/$ctime));
849
850# latencies
851        printl("Supported CAS Latencies", cas_latencies(keys %cas));
852
853        my @array;
854        for ($ii = 0; $ii < 7; $ii++) {
855                push(@array, $ii) if ($bytes->[19] & (1 << $ii));
856        }
857        if (@array) { $temp = join ', ', @array; }
858        else { $temp = "None"; }
859        printl("Supported CS Latencies", $temp);
860
861        @array = ();
862        for ($ii = 0; $ii < 7; $ii++) {
863                push(@array, $ii) if ($bytes->[20] & (1 << $ii));
864        }
865        if (@array) { $temp = join ', ', @array; }
866        else { $temp = "None"; }
867        printl("Supported WE Latencies", $temp);
868
869# timings
870        my ($cycle_time, $access_time);
871
872        if (exists $cas{$highestCAS}) {
873                $cycle_time = "$ctime ns at CAS $highestCAS";
874                $access_time = (($bytes->[10] >> 4) * 0.1 + ($bytes->[10] & 0xf) * 0.01)
875                             . " ns at CAS $highestCAS";
876        }
877
878        if (exists $cas{$highestCAS-0.5} && spd_written(@$bytes[23..24])) {
879                $cycle_time .= "\n".(($bytes->[23] >> 4) + ($bytes->[23] & 0xf) * 0.1)
880                             . " ns at CAS ".($highestCAS-0.5);
881                $access_time .= "\n".(($bytes->[24] >> 4) * 0.1 + ($bytes->[24] & 0xf) * 0.01)
882                              . " ns at CAS ".($highestCAS-0.5);
883        }
884
885        if (exists $cas{$highestCAS-1} && spd_written(@$bytes[25..26])) {
886                $cycle_time .= "\n".(($bytes->[25] >> 4) + ($bytes->[25] & 0xf) * 0.1)
887                             . " ns at CAS ".($highestCAS-1);
888                $access_time .= "\n".(($bytes->[26] >> 4) * 0.1 + ($bytes->[26] & 0xf) * 0.01)
889                              . " ns at CAS ".($highestCAS-1);
890        }
891
892        printl_cond(defined $cycle_time, "Minimum Cycle Time", $cycle_time);
893        printl_cond(defined $access_time, "Maximum Access Time", $access_time);
894
895# module attributes
896        if ($bytes->[47] & 0x03) {
897                if (($bytes->[47] & 0x03) == 0x01) { $temp = "1.125\" to 1.25\""; }
898                elsif (($bytes->[47] & 0x03) == 0x02) { $temp = "1.7\""; }
899                elsif (($bytes->[47] & 0x03) == 0x03) { $temp = "Other"; }
900                printl("Module Height", $temp);
901        }
902}
903
904sub ddr2_sdram_ctime($)
905{
906        my $byte = shift;
907        my $ctime;
908
909        $ctime = $byte >> 4;
910        if (($byte & 0xf) <= 9) { $ctime += ($byte & 0xf) * 0.1; }
911        elsif (($byte & 0xf) == 10) { $ctime += 0.25; }
912        elsif (($byte & 0xf) == 11) { $ctime += 0.33; }
913        elsif (($byte & 0xf) == 12) { $ctime += 0.66; }
914        elsif (($byte & 0xf) == 13) { $ctime += 0.75; }
915
916        return $ctime;
917}
918
919sub ddr2_sdram_atime($)
920{
921        my $byte = shift;
922        my $atime;
923
924        $atime = ($byte >> 4) * 0.1 + ($byte & 0xf) * 0.01;
925
926        return $atime;
927}
928
929# Base, high-bit, 3-bit fraction code
930sub ddr2_sdram_rtime($$$)
931{
932        my ($rtime, $msb, $ext) = @_;
933        my @table = (0, .25, .33, .50, .66, .75);
934
935        return $rtime + $msb * 256 + $table[$ext];
936}
937
938sub ddr2_module_types($)
939{
940        my $byte = shift;
941        my @types = qw(RDIMM UDIMM SO-DIMM Micro-DIMM Mini-RDIMM Mini-UDIMM);
942        my @widths = (133.35, 133.25, 67.6, 45.5, 82.0, 82.0);
943        my @suptypes;
944        local $_;
945
946        foreach (0..5) {
947                push @suptypes, "$types[$_] ($widths[$_] mm)"
948                        if ($byte & (1 << $_));
949        }
950
951        return @suptypes;
952}
953
954# Common to SDR, DDR and DDR2 SDRAM
955sub ddr2_refresh_rate($)
956{
957        my $byte = shift;
958        my @refresh = qw(Normal Reduced Reduced Extended Extended Extended);
959        my @refresht = (15.625, 3.9, 7.8, 31.3, 62.5, 125);
960
961        return "$refresh[$byte & 0x7f] ($refresht[$byte & 0x7f] us)".
962               ($byte & 0x80 ? " - Self Refresh" : "");
963}
964
965# Parameter: EEPROM bytes 0-127 (using 3-62)
966sub decode_ddr2_sdram($)
967{
968        my $bytes = shift;
969        my $temp;
970        my $ctime;
971
972# SPD revision
973        if ($bytes->[62] != 0xff) {
974                printl("SPD Revision", ($bytes->[62] >> 4) . "." .
975                                       ($bytes->[62] & 0xf));
976        }
977
978# speed
979        prints("Memory Characteristics");
980
981        $ctime = ddr2_sdram_ctime($bytes->[9]);
982        my $ddrclk = 2 * (1000 / $ctime);
983        my $tbits = ($bytes->[7] * 256) + $bytes->[6];
984        if ($bytes->[11] & 0x03) { $tbits = $tbits - 8; }
985        my $pcclk = int ($ddrclk * $tbits / 8);
986        # Round down to comply with Jedec
987        $pcclk = $pcclk - ($pcclk % 100);
988        $ddrclk = int ($ddrclk);
989        printl("Maximum module speed", "${ddrclk}MHz (PC2-${pcclk})");
990
991#size computation
992        my $k = 0;
993        my $ii = 0;
994
995        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
996        $k = (($bytes->[5] & 0x7) + 1) * $bytes->[17];
997
998        if($ii > 0 && $ii <= 12 && $k > 0) {
999                printl("Size", ((1 << $ii) * $k) . " MB");
1000        } else {
1001                printl("Size", "INVALID: " . $bytes->[3] . "," . $bytes->[4] . "," .
1002                               $bytes->[5] . "," . $bytes->[17]);
1003        }
1004
1005        printl("Banks x Rows x Columns x Bits",
1006               join(' x ', $bytes->[17], $bytes->[3], $bytes->[4], $bytes->[6]));
1007        printl("Ranks", ($bytes->[5] & 7) + 1);
1008
1009        printl("SDRAM Device Width", $bytes->[13]." bits");
1010
1011        my @heights = ('< 25.4', '25.4', '25.4 - 30.0', '30.0', '30.5', '> 30.5');
1012        printl("Module Height", $heights[$bytes->[5] >> 5]." mm");
1013
1014        my @suptypes = ddr2_module_types($bytes->[20]);
1015        printl("Module Type".(@suptypes > 1 ? 's' : ''), join(', ', @suptypes));
1016
1017        printl("DRAM Package", $bytes->[5] & 0x10 ? "Stack" : "Planar");
1018
1019        printl("Voltage Interface Level",
1020               sdram_voltage_interface_level($bytes->[8]));
1021
1022        printl("Refresh Rate", ddr2_refresh_rate($bytes->[12]));
1023
1024        my @burst;
1025        push @burst, 4 if ($bytes->[16] & 4);
1026        push @burst, 8 if ($bytes->[16] & 8);
1027        $burst[0] = 'None' if !@burst;
1028        printl("Supported Burst Lengths", join(', ', @burst));
1029
1030        my $highestCAS = 0;
1031        my %cas;
1032        for ($ii = 2; $ii < 7; $ii++) {
1033                if ($bytes->[18] & (1 << $ii)) {
1034                        $highestCAS = $ii;
1035                        $cas{$highestCAS}++;
1036                }
1037        }
1038
1039        my $trcd;
1040        my $trp;
1041        my $tras;
1042
1043        $trcd = ($bytes->[29] >> 2) + (($bytes->[29] & 3) * 0.25);
1044        $trp = ($bytes->[27] >> 2) + (($bytes->[27] & 3) * 0.25);
1045        $tras = $bytes->[30];
1046
1047        printl("tCL-tRCD-tRP-tRAS",
1048                $highestCAS . "-" .
1049                ceil($trcd/$ctime) . "-" .
1050                ceil($trp/$ctime) . "-" .
1051                ceil($tras/$ctime));
1052
1053# latencies
1054        printl("Supported CAS Latencies (tCL)", cas_latencies(keys %cas));
1055
1056# timings
1057        my ($cycle_time, $access_time);
1058
1059        if (exists $cas{$highestCAS}) {
1060                $cycle_time = tns($ctime) . " at CAS $highestCAS (tCK min)";
1061                $access_time = tns(ddr2_sdram_atime($bytes->[10]))
1062                             . " at CAS $highestCAS (tAC)";
1063        }
1064
1065        if (exists $cas{$highestCAS-1} && spd_written(@$bytes[23..24])) {
1066                $cycle_time .= "\n".tns(ddr2_sdram_ctime($bytes->[23]))
1067                             . " at CAS ".($highestCAS-1);
1068                $access_time .= "\n".tns(ddr2_sdram_atime($bytes->[24]))
1069                              . " at CAS ".($highestCAS-1);
1070        }
1071
1072        if (exists $cas{$highestCAS-2} && spd_written(@$bytes[25..26])) {
1073                $cycle_time .= "\n".tns(ddr2_sdram_ctime($bytes->[25]))
1074                             . " at CAS ".($highestCAS-2);
1075                $access_time .= "\n".tns(ddr2_sdram_atime($bytes->[26]))
1076                              . " at CAS ".($highestCAS-2);
1077        }
1078
1079        printl_cond(defined $cycle_time, "Minimum Cycle Time", $cycle_time);
1080        printl_cond(defined $access_time, "Maximum Access Time", $access_time);
1081
1082        printl("Maximum Cycle Time (tCK max)",
1083               tns(ddr2_sdram_ctime($bytes->[43])));
1084
1085# more timing information
1086        prints("Timing Parameters");
1087        printl("Address/Command Setup Time Before Clock (tIS)",
1088               tns(ddr2_sdram_atime($bytes->[32])));
1089        printl("Address/Command Hold Time After Clock (tIH)",
1090               tns(ddr2_sdram_atime($bytes->[33])));
1091        printl("Data Input Setup Time Before Strobe (tDS)",
1092               tns(ddr2_sdram_atime($bytes->[34])));
1093        printl("Data Input Hold Time After Strobe (tDH)",
1094               tns(ddr2_sdram_atime($bytes->[35])));
1095        printl("Minimum Row Precharge Delay (tRP)", tns($trp));
1096        printl("Minimum Row Active to Row Active Delay (tRRD)",
1097               tns($bytes->[28]/4));
1098        printl("Minimum RAS# to CAS# Delay (tRCD)", tns($trcd));
1099        printl("Minimum RAS# Pulse Width (tRAS)", tns($tras));
1100        printl("Write Recovery Time (tWR)", tns($bytes->[36]/4));
1101        printl("Minimum Write to Read CMD Delay (tWTR)", tns($bytes->[37]/4));
1102        printl("Minimum Read to Pre-charge CMD Delay (tRTP)", tns($bytes->[38]/4));
1103        printl("Minimum Active to Auto-refresh Delay (tRC)",
1104               tns(ddr2_sdram_rtime($bytes->[41], 0, ($bytes->[40] >> 4) & 7)));
1105        printl("Minimum Recovery Delay (tRFC)",
1106               tns(ddr2_sdram_rtime($bytes->[42], $bytes->[40] & 1,
1107                                    ($bytes->[40] >> 1) & 7)));
1108        printl("Maximum DQS to DQ Skew (tDQSQ)", tns($bytes->[44]/100));
1109        printl("Maximum Read Data Hold Skew (tQHS)", tns($bytes->[45]/100));
1110        printl("PLL Relock Time", $bytes->[46] . " us") if ($bytes->[46]);
1111}
1112
1113# Parameter: EEPROM bytes 0-127 (using 3-76)
1114sub decode_ddr3_sdram($)
1115{
1116        my $bytes = shift;
1117        my $temp;
1118        my $ctime;
1119
1120        my @module_types = ("Undefined", "RDIMM", "UDIMM", "SO-DIMM",
1121                            "Micro-DIMM", "Mini-RDIMM", "Mini-UDIMM");
1122
1123        printl("Module Type", ($bytes->[3] <= $#module_types) ?
1124                                        $module_types[$bytes->[3]] :
1125                                        sprint("Reserved (0x%.2X)", $bytes->[3]));
1126
1127# speed
1128        prints("Memory Characteristics");
1129
1130        my $dividend = ($bytes->[9] >> 4) & 15;
1131        my $divisor  = $bytes->[9] & 15;
1132        printl("Fine time base", sprintf("%.3f", $dividend / $divisor) . " ps");
1133
1134        $dividend = $bytes->[10];
1135        $divisor  = $bytes->[11];
1136        my $mtb = $dividend / $divisor;
1137        printl("Medium time base", tns3($mtb));
1138
1139        $ctime = $bytes->[12] * $mtb;
1140        my $ddrclk = 2 * (1000 / $ctime);
1141        my $tbits = 1 << (($bytes->[8] & 7) + 3);
1142        my $pcclk = int ($ddrclk * $tbits / 8);
1143        $ddrclk = int ($ddrclk);
1144        printl("Maximum module speed", "${ddrclk}MHz (PC3-${pcclk})");
1145
1146# Size computation
1147
1148        my $cap =  ($bytes->[4]       & 15) + 28;
1149        $cap   +=  ($bytes->[8]       & 7)  + 3;
1150        $cap   -=  ($bytes->[7]       & 7)  + 2;
1151        $cap   -= 20 + 3;
1152        my $k   = (($bytes->[7] >> 3) & 31) + 1;
1153        printl("Size", ((1 << $cap) * $k) . " MB");
1154
1155        printl("Banks x Rows x Columns x Bits",
1156               join(' x ', 1 << ((($bytes->[4] >> 4) &  7) +  3),
1157                           ((($bytes->[5] >> 3) & 31) + 12),
1158                           ( ($bytes->[5]       &  7) +  9),
1159                           ( 1 << (($bytes->[8] &  7) + 3)) ));
1160        printl("Ranks", $k);
1161
1162        printl("SDRAM Device Width", (1 << (($bytes->[7] & 7) + 2))." bits");
1163
1164        my $taa;
1165        my $trcd;
1166        my $trp;
1167        my $tras;
1168
1169        $taa  = int($bytes->[16] / $bytes->[12]);
1170        $trcd = int($bytes->[18] / $bytes->[12]);
1171        $trp  = int($bytes->[20] / $bytes->[12]);
1172        $tras = int((($bytes->[21] >> 4) * 256 + $bytes->[22]) / $bytes->[12]);
1173
1174        printl("tCL-tRCD-tRP-tRAS", join("-", $taa, $trcd, $trp, $tras));
1175
1176# latencies
1177        my $highestCAS = 0;
1178        my %cas;
1179        my $ii;
1180        my $cas_sup = ($bytes->[15] << 8) + $bytes->[14];
1181        for ($ii = 0; $ii < 15; $ii++) {
1182                if ($cas_sup & (1 << $ii)) {
1183                        $highestCAS = $ii + 4;
1184                        $cas{$highestCAS}++;
1185                }
1186        }
1187        printl("Supported CAS Latencies (tCL)", cas_latencies(keys %cas));
1188
1189# more timing information
1190        prints("Timing Parameters");
1191
1192        printl("Minimum Write Recovery time (tWR)", tns3($bytes->[17] * $mtb));
1193        printl("Minimum Row Active to Row Active Delay (tRRD)",
1194                tns3($bytes->[19] * $mtb));
1195        printl("Minimum Active to Auto-Refresh Delay (tRC)",
1196                tns3((((($bytes->[21] >> 4) & 15) << 8) + $bytes->[23]) * $mtb));
1197        printl("Minimum Recovery Delay (tRFC)",
1198                tns3((($bytes->[25] << 8) + $bytes->[24]) * $mtb));
1199        printl("Minimum Write to Read CMD Delay (tWTR)",
1200                tns3($bytes->[26] * $mtb));
1201        printl("Minimum Read to Pre-charge CMD Delay (tRTP)",
1202                tns3($bytes->[27] * $mtb));
1203        printl("Minimum Four Activate Window Delay (tFAW)",
1204                tns3(((($bytes->[28] & 15) << 8) + $bytes->[29]) * $mtb));
1205
1206# miscellaneous stuff
1207        prints("Optional Features");
1208
1209        my $volts = "1.5V";
1210        if ($bytes->[6] & 1) {
1211                $volts .= " tolerant";
1212        }
1213        if ($bytes->[6] & 2) {
1214                $volts .= ", 1.35V ";
1215        }
1216        if ($bytes->[6] & 4) {
1217                $volts .= ", 1.2X V";
1218        }
1219        printl("Operable voltages", $volts);
1220        printl("RZQ/6 supported?", ($bytes->[30] & 1) ? "Yes" : "No");
1221        printl("RZQ/7 supported?", ($bytes->[30] & 2) ? "Yes" : "No");
1222        printl("DLL-Off Mode supported?", ($bytes->[30] & 128) ? "Yes" : "No");
1223        printl("Operating temperature range", sprintf "0-%dC",
1224                ($bytes->[31] & 1) ? 95 : 85);
1225        printl("Refresh Rate in extended temp range",
1226                ($bytes->[31] & 2) ? "2X" : "1X");
1227        printl("Auto Self-Refresh?", ($bytes->[31] & 4) ? "Yes" : "No");
1228        printl("On-Die Thermal Sensor readout?",
1229                ($bytes->[31] & 8) ? "Yes" : "No");
1230        printl("Partial Array Self-Refresh?",
1231                ($bytes->[31] & 128) ? "Yes" : "No");
1232        printl("Thermal Sensor Accuracy",
1233                ($bytes->[32] & 128) ? sprintf($bytes->[32] & 127) :
1234                                        "Not implemented");
1235        printl("SDRAM Device Type",
1236                ($bytes->[33] & 128) ? sprintf($bytes->[33] & 127) :
1237                                        "Standard Monolithic");
1238        if ($bytes->[3] >= 1 && $bytes->[3] <= 6) {
1239
1240                prints("Physical Characteristics");
1241                printl("Module Height (mm)", ($bytes->[60] & 31) + 15);
1242                printl("Module Thickness (mm)", sprintf("%d front, %d back",
1243                                                ($bytes->[61] & 15) + 1,
1244                                                (($bytes->[61] >> 4) & 15) +1));
1245                printl("Module Width (mm)", ($bytes->[3] <= 2) ? 133.5 :
1246                                        ($bytes->[3] == 3) ? 67.6 : "TBD");
1247
1248                my $alphabet = "ABCDEFGHJKLMNPRTUVWY";
1249                my $ref = $bytes->[62] & 31;
1250                my $ref_card;
1251                if ($ref == 31) {
1252                        $ref_card = "ZZ";
1253                } else {
1254                        if ($bytes->[62] & 128) {
1255                                $ref += 31;
1256                        }
1257                        if ($ref < length $alphabet) {
1258                                $ref_card = substr $alphabet, $ref, 1;
1259                        } else {
1260                                my $ref1 = int($ref / (length $alphabet));
1261                                $ref -= (length $alphabet) * $ref1;
1262                                $ref_card = (substr $alphabet, $ref1, 1) .
1263                                            (substr $alphabet, $ref, 1);
1264                        }
1265                }
1266                printl("Module Reference Card", $ref_card);
1267        }
1268        if ($bytes->[3] == 1 || $bytes->[3] == 5) {
1269                prints("Registered DIMM");
1270
1271                my @rows = ("Undefined", 1, 2, 4);
1272                printl("# DRAM Rows", $rows[($bytes->[63] >> 2) & 3]);
1273                printl("# Registers", $rows[$bytes->[63] & 3]);
1274                printl("Register manufacturer",
1275                        manufacturer_ddr3($bytes->[65], $bytes->[66]));
1276                printl("Register device type",
1277                                (($bytes->[68] & 7) == 0) ? "SSTE32882" :
1278                                        "Undefined");
1279                printl("Register revision", sprintf("0x%.2X", $bytes->[67]));
1280                printl("Heat spreader characteristics",
1281                                ($bytes->[64] < 128) ? "Not incorporated" :
1282                                        sprintf("%.2X", ($bytes->[64] & 127)));
1283                my $regs;
1284                for (my $i = 0; $i < 8; $i++) {
1285                        $regs = sprintf("SSTE32882 RC%d/RC%d",
1286                                        $i * 2, $i * 2 + 1);
1287                        printl($regs, sprintf("%.2X", $bytes->[$i + 69]));
1288                }
1289        }
1290}
1291
1292# Parameter: EEPROM bytes 0-127 (using 4-5)
1293sub decode_direct_rambus($)
1294{
1295        my $bytes = shift;
1296
1297#size computation
1298        prints("Memory Characteristics");
1299
1300        my $ii;
1301
1302        $ii = ($bytes->[4] & 0x0f) + ($bytes->[4] >> 4) + ($bytes->[5] & 0x07) - 13;
1303
1304        if ($ii > 0 && $ii < 16) {
1305                printl("Size", (1 << $ii) . " MB");
1306        } else {
1307                printl("Size", sprintf("INVALID: 0x%02x, 0x%02x",
1308                                       $bytes->[4], $bytes->[5]));
1309        }
1310}
1311
1312# Parameter: EEPROM bytes 0-127 (using 3-5)
1313sub decode_rambus($)
1314{
1315        my $bytes = shift;
1316
1317#size computation
1318        prints("Memory Characteristics");
1319
1320        my $ii;
1321
1322        $ii = ($bytes->[3] & 0x0f) + ($bytes->[3] >> 4) + ($bytes->[5] & 0x07) - 13;
1323
1324        if ($ii > 0 && $ii < 16) {
1325                printl("Size", (1 << $ii) . " MB");
1326        } else {
1327                printl("Size", "INVALID: " . sprintf("0x%02x, 0x%02x",
1328                                               $bytes->[3], $bytes->[5]));
1329        }
1330}
1331
1332%decode_callback = (
1333        "SDR SDRAM"     => \&decode_sdr_sdram,
1334        "DDR SDRAM"     => \&decode_ddr_sdram,
1335        "DDR2 SDRAM"    => \&decode_ddr2_sdram,
1336        "DDR3 SDRAM"    => \&decode_ddr3_sdram,
1337        "Direct Rambus" => \&decode_direct_rambus,
1338        "Rambus"        => \&decode_rambus,
1339);
1340
1341# Parameter: Manufacturing year/week bytes
1342sub manufacture_date($$)
1343{
1344        my ($year, $week) = @_;
1345
1346        # In theory the year and week are in BCD format, but
1347        # this is not always true in practice :(
1348        if (($year & 0xf0) <= 0x90 && ($year & 0x0f) <= 0x09
1349         && ($week & 0xf0) <= 0x90 && ($week & 0x0f) <= 0x09) {
1350                # Note that this heuristic will break in year 2080
1351                return sprintf("%d%02X-W%02X",
1352                                $year >= 0x80 ? 19 : 20, $year, $week);
1353        # Fallback to binary format if it seems to make sense
1354        } elsif ($year <= 99 && $week >= 1 && $week <= 53) {
1355                return sprintf("%d%02d-W%02d",
1356                                $year >= 80 ? 19 : 20, $year, $week);
1357        } else {
1358                return sprintf("0x%02X%02X", $year, $week);
1359        }
1360}
1361
1362sub printl_mfg_location_code($)
1363{
1364        my $code = shift;
1365        my $letter = chr($code);
1366
1367        # Try the location code as ASCII first, as earlier specifications
1368        # suggested this. As newer specifications don't mention it anymore,
1369        # we still fall back to binary.
1370        printl_cond(spd_written($code), "Manufacturing Location Code",
1371                    $letter =~ m/^[\w\d]$/ ? $letter : sprintf("0x%.2X", $code));
1372}
1373
1374sub printl_mfg_assembly_serial(@)
1375{
1376        printl_cond(spd_written(@_), "Assembly Serial Number",
1377                    sprintf("0x%02X%02X%02X%02X", @_));
1378}
1379
1380# Parameter: EEPROM bytes 0-175 (using 117-149)
1381sub decode_ddr3_mfg_data($)
1382{
1383        my $bytes = shift;
1384
1385        prints("Manufacturer Data");
1386
1387        printl("Module Manufacturer",
1388               manufacturer_ddr3($bytes->[117], $bytes->[118]));
1389
1390        if (spd_written(@{$bytes}[148..149])) {
1391                printl("DRAM Manufacturer",
1392                       manufacturer_ddr3($bytes->[148], $bytes->[149]));
1393        }
1394
1395        printl_mfg_location_code($bytes->[119]);
1396
1397        if (spd_written(@{$bytes}[120..121])) {
1398                printl("Manufacturing Date",
1399                       manufacture_date($bytes->[120], $bytes->[121]));
1400        }
1401
1402        printl_mfg_assembly_serial(@{$bytes}[122..125]);
1403
1404        printl("Part Number", part_number(@{$bytes}[128..145]));
1405
1406        if (spd_written(@{$bytes}[146..147])) {
1407                printl("Revision Code",
1408                       sprintf("0x%02X%02X", $bytes->[146], $bytes->[147]));
1409        }
1410}
1411
1412# Parameter: EEPROM bytes 0-127 (using 64-98)
1413sub decode_manufacturing_information($)
1414{
1415        my $bytes = shift;
1416        my ($temp, $extra);
1417
1418        prints("Manufacturing Information");
1419
1420        # $extra is a reference to an array containing up to
1421        # 7 extra bytes from the Manufacturer field. Sometimes
1422        # these bytes are filled with interesting data.
1423        ($temp, $extra) = manufacturer(@{$bytes}[64..71]);
1424        printl("Manufacturer", $temp);
1425        $temp = manufacturer_data(@{$extra});
1426        printl_cond(defined $temp, "Custom Manufacturer Data", $temp);
1427
1428        printl_mfg_location_code($bytes->[72]);
1429
1430        printl("Part Number", part_number(@{$bytes}[73..90]));
1431
1432        printl_cond(spd_written(@{$bytes}[91..92]), "Revision Code",
1433                    sprintf("0x%02X%02X", @{$bytes}[91..92]));
1434
1435        printl_cond(spd_written(@{$bytes}[93..94]), "Manufacturing Date",
1436               manufacture_date($bytes->[93], $bytes->[94]));
1437
1438        printl_mfg_assembly_serial(@{$bytes}[95..98]);
1439}
1440
1441# Parameter: EEPROM bytes 0-127 (using 126-127)
1442sub decode_intel_spec_freq($)
1443{
1444        my $bytes = shift;
1445        my $temp;
1446
1447        prints("Intel Specification");
1448
1449        if ($bytes->[126] == 0x66) { $temp = "66MHz"; }
1450        elsif ($bytes->[126] == 100) { $temp = "100MHz or 133MHz"; }
1451        elsif ($bytes->[126] == 133) { $temp = "133MHz"; }
1452        else { $temp = "Undefined!"; }
1453        printl("Frequency", $temp);
1454
1455        $temp = "";
1456        if ($bytes->[127] & 1) { $temp .= "Intel Concurrent Auto-precharge\n"; }
1457        if ($bytes->[127] & 2) { $temp .= "CAS Latency = 2\n"; }
1458        if ($bytes->[127] & 4) { $temp .= "CAS Latency = 3\n"; }
1459        if ($bytes->[127] & 8) { $temp .= "Junction Temp A (100 degrees C)\n"; }
1460        else { $temp .= "Junction Temp B (90 degrees C)\n"; }
1461        if ($bytes->[127] & 16) { $temp .= "CLK 3 Connected\n"; }
1462        if ($bytes->[127] & 32) { $temp .= "CLK 2 Connected\n"; }
1463        if ($bytes->[127] & 64) { $temp .= "CLK 1 Connected\n"; }
1464        if ($bytes->[127] & 128) { $temp .= "CLK 0 Connected\n"; }
1465        if (($bytes->[127] & 192) == 192) { $temp .= "Double-sided DIMM\n"; }
1466        elsif (($bytes->[127] & 192) != 0) { $temp .= "Single-sided DIMM\n"; }
1467        printl("Details for 100MHz Support", $temp);
1468}
1469
1470# Read various hex dump style formats: hexdump, hexdump -C, i2cdump, eeprog
1471# note that normal 'hexdump' format on a little-endian system byte-swaps
1472# words, using hexdump -C is better.
1473sub read_hexdump($)
1474{
1475        my $addr = 0;
1476        my $repstart = 0;
1477        my @bytes;
1478        my $header = 1;
1479        my $word = 0;
1480
1481        # Look in the cache first
1482        return @{$hexdump_cache{$_[0]}} if exists $hexdump_cache{$_[0]};
1483
1484        open F, '<', $_[0] or die "Unable to open: $_[0]";
1485        while (<F>) {
1486                chomp;
1487                if (/^\*$/) {
1488                        $repstart = $addr;
1489                        next;
1490                }
1491                /^(?:0000 )?([a-f\d]{2,8}):?\s+((:?[a-f\d]{4}\s*){8}|(:?[a-f\d]{2}\s*){16})/i ||
1492                /^(?:0000 )?([a-f\d]{2,8}):?\s*$/i;
1493                next if (!defined $1 && $header);               # skip leading unparsed lines
1494
1495                defined $1 or die "Unable to parse input";
1496                $header = 0;
1497
1498                $addr = hex $1;
1499                if ($repstart) {
1500                        @bytes[$repstart .. ($addr-1)] =
1501                                (@bytes[($repstart-16)..($repstart-1)]) x (($addr-$repstart)/16);
1502                        $repstart = 0;
1503                }
1504                last unless defined $2;
1505                foreach (split(/\s+/, $2)) {
1506                        if (/^(..)(..)$/) {
1507                                $word |= 1;
1508                                if ($use_hexdump eq LITTLEENDIAN) {
1509                                        $bytes[$addr++] = hex($2);
1510                                        $bytes[$addr++] = hex($1);
1511                                } else {
1512                                        $bytes[$addr++] = hex($1);
1513                                        $bytes[$addr++] = hex($2);
1514                                }
1515                        } else {
1516                                $bytes[$addr++] = hex($_);
1517                        }
1518                }
1519        }
1520        close F;
1521        $header and die "Unable to parse any data from hexdump '$_[0]'";
1522        $word and printc("Using $use_hexdump 16-bit hex dump");
1523
1524        # Cache the data for later use
1525        $hexdump_cache{$_[0]} = \@bytes;
1526        return @bytes;
1527}
1528
1529# Returns the (total, used) number of bytes in the EEPROM,
1530# assuming it is a non-Rambus SPD EEPROM.
1531sub spd_sizes($)
1532{
1533        my $bytes = shift;
1534
1535        if ($bytes->[2] >= 9) {
1536                # For FB-DIMM and newer, decode number of bytes written
1537                my $spd_len = ($bytes->[0] >> 4) & 7;
1538                my $size = 64 << ($bytes->[0] & 15);
1539                if ($spd_len == 0) {
1540                        return ($size, 128);
1541                } elsif ($spd_len == 1) {
1542                        return ($size, 176);
1543                } elsif ($spd_len == 2) {
1544                        return ($size, 256);
1545                } else {
1546                        return (64, 64);
1547                }
1548        } else {
1549                my $size;
1550                if ($bytes->[1] <= 14) {
1551                        $size = 1 << $bytes->[1];
1552                } elsif ($bytes->[1] == 0) {
1553                        $size = "RFU";
1554                } else { $size = "ERROR!" }
1555
1556                return ($size, ($bytes->[0] < 64) ? 64 : $bytes->[0]);
1557        }
1558}
1559
1560# Read bytes from SPD-EEPROM
1561# Note: offset must be a multiple of 16!
1562sub readspd($$$)
1563{
1564        my ($offset, $size, $dimm_i) = @_;
1565        my @bytes;
1566        if ($use_hexdump) {
1567                @bytes = read_hexdump($dimm_i);
1568                return @bytes[$offset..($offset + $size - 1)];
1569        } elsif ($use_sysfs) {
1570                # Kernel 2.6 with sysfs
1571                sysopen(HANDLE, "$dimm_i/eeprom", O_RDONLY)
1572                        or die "Cannot open $dimm_i/eeprom";
1573                binmode HANDLE;
1574                sysseek(HANDLE, $offset, SEEK_SET)
1575                        or die "Cannot seek $dimm_i/eeprom";
1576                sysread(HANDLE, my $eeprom, $size)
1577                        or die "Cannot read $dimm_i/eeprom";
1578                close HANDLE;
1579                @bytes = unpack("C*", $eeprom);
1580        } else {
1581                # Kernel 2.4 with procfs
1582                for my $i (0 .. ($size-1)/16) {
1583                        my $hexoff = sprintf('%02x', $offset + $i * 16);
1584                        push @bytes, split(" ", `cat $dimm_i/$hexoff`);
1585                }
1586        }
1587        return @bytes;
1588}
1589
1590# Calculate and verify checksum of first 63 bytes
1591sub checksum($)
1592{
1593        my $bytes = shift;
1594        my $dimm_checksum = 0;
1595        local $_;
1596
1597        $dimm_checksum += $bytes->[$_] foreach (0 .. 62);
1598        $dimm_checksum &= 0xff;
1599
1600        return ("EEPROM Checksum of bytes 0-62",
1601                ($bytes->[63] == $dimm_checksum) ? 1 : 0,
1602                sprintf('0x%02X', $bytes->[63]),
1603                sprintf('0x%02X', $dimm_checksum));
1604}
1605
1606# Calculate and verify CRC
1607sub check_crc($)
1608{
1609        my $bytes = shift;
1610        my $crc = 0;
1611        my $crc_cover = $bytes->[0] & 0x80 ? 116 : 125;
1612        my $crc_ptr = 0;
1613        my $crc_bit;
1614
1615        while ($crc_ptr <= $crc_cover) {
1616                $crc = $crc ^ ($bytes->[$crc_ptr] << 8);
1617                for ($crc_bit = 0; $crc_bit < 8; $crc_bit++) {
1618                        if ($crc & 0x8000) {
1619                                $crc = ($crc << 1) ^ 0x1021;
1620                        } else {
1621                                $crc = $crc << 1
1622                        }
1623                }
1624                $crc_ptr++;
1625        }
1626        $crc &= 0xffff;
1627
1628        my $dimm_crc = ($bytes->[127] << 8) | $bytes->[126];
1629        return ("EEPROM CRC of bytes 0-$crc_cover",
1630                ($dimm_crc == $crc) ? 1 : 0,
1631                sprintf("0x%04X", $dimm_crc),
1632                sprintf("0x%04X", $crc));
1633}
1634
1635# Parse command-line
1636foreach (@ARGV) {
1637        if ($_ eq '-h' || $_ eq '--help') {
1638                print "Usage: $0 [-c] [-f [-b]] [-x|-X file [files..]]\n",
1639                        "       $0 -h\n\n",
1640                        "  -f, --format            Print nice html output\n",
1641                        "  -b, --bodyonly          Don't print html header\n",
1642                        "                          (useful for postprocessing the output)\n",
1643                        "      --side-by-side      Display all DIMMs side-by-side if possible\n",
1644                        "      --merge-cells       Merge neighbour cells with identical values\n",
1645                        "                          (side-by-side output only)\n",
1646                        "  -c, --checksum          Decode completely even if checksum fails\n",
1647                        "  -x,                     Read data from hexdump files\n",
1648                        "  -X,                     Same as -x except treat multibyte hex\n",
1649                        "                          data as little endian\n",
1650                        "  -h, --help              Display this usage summary\n";
1651                print <<"EOF";
1652
1653Hexdumps can be the output from hexdump, hexdump -C, i2cdump, eeprog and
1654likely many other progams producing hex dumps of one kind or another.  Note
1655that the default output of "hexdump" will be byte-swapped on little-endian
1656systems and you must use -X instead of -x, otherwise the dump will not be
1657parsed correctly.  It is better to use "hexdump -C", which is not ambiguous.
1658EOF
1659                exit;
1660        }
1661
1662        if ($_ eq '-f' || $_ eq '--format') {
1663                $opt_html = 1;
1664                next;
1665        }
1666        if ($_ eq '-b' || $_ eq '--bodyonly') {
1667                $opt_bodyonly = 1;
1668                next;
1669        }
1670        if ($_ eq '--side-by-side') {
1671                $opt_side_by_side = 1;
1672                next;
1673        }
1674        if ($_ eq '--merge-cells') {
1675                $opt_merge = 1;
1676                next;
1677        }
1678        if ($_ eq '-c' || $_ eq '--checksum') {
1679                $opt_igncheck = 1;
1680                next;
1681        }
1682        if ($_ eq '-x') {
1683                $use_hexdump = BIGENDIAN;
1684                next;
1685        }
1686        if ($_ eq '-X') {
1687                $use_hexdump = LITTLEENDIAN;
1688                next;
1689        }
1690
1691        if (m/^-/) {
1692                print STDERR "Unrecognized option $_\n";
1693                exit;
1694        }
1695
1696        push @dimm, { eeprom => $_, file => $_ } if $use_hexdump;
1697}
1698
1699if ($opt_html && !$opt_bodyonly) {
1700        print "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
1701              "<html><head>\n",
1702                  "\t<meta HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">\n",
1703                  "\t<title>PC DIMM Serial Presence Detect Tester/Decoder Output</title>\n",
1704                  "</head><body>\n";
1705}
1706
1707printc("decode-dimms version $revision");
1708printh('Memory Serial Presence Detect Decoder',
1709'By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner,
1710Jean Delvare, Trent Piepho and others');
1711
1712
1713# From a sysfs device path and an attribute name, return the attribute
1714# value, or undef (stolen from sensors-detect)
1715sub sysfs_device_attribute
1716{
1717        my ($device, $attr) = @_;
1718        my $value;
1719
1720        open(local *FILE, "$device/$attr") or return "";
1721        $value = <FILE>;
1722        close(FILE);
1723        return unless defined $value;
1724
1725        chomp($value);
1726        return $value;
1727}
1728
1729sub get_dimm_list
1730{
1731        my (@dirs, $dir, $file, @files);
1732
1733        if ($use_sysfs) {
1734                @dirs = ('/sys/bus/i2c/drivers/eeprom', '/sys/bus/i2c/drivers/at24');
1735        } else {
1736                @dirs = ('/proc/sys/dev/sensors');
1737        }
1738
1739        foreach $dir (@dirs) {
1740                next unless opendir(local *DIR, $dir);
1741                while (defined($file = readdir(DIR))) {
1742                        if ($use_sysfs) {
1743                                # We look for I2C devices like 0-0050 or 2-0051
1744                                next unless $file =~ /^\d+-[\da-f]+$/i;
1745                                next unless -d "$dir/$file";
1746
1747                                # Device name must be eeprom (driver eeprom)
1748                                # or spd (driver at24)
1749                                my $attr = sysfs_device_attribute("$dir/$file", "name");
1750                                next unless defined $attr &&
1751                                            ($attr eq "eeprom" || $attr eq "spd");
1752                        } else {
1753                                next unless $file =~ /^eeprom-/;
1754                        }
1755                        push @files, { eeprom => "$file",
1756                                       file => "$dir/$file" };
1757                }
1758                close(DIR);
1759        }
1760
1761        if (@files) {
1762                return sort { $a->{file} cmp $b->{file} } @files;
1763        } elsif (! -d '/sys/module/eeprom') {
1764                print "No EEPROM found, are you sure the eeprom module is loaded?\n";
1765                exit;
1766        }
1767}
1768
1769# @dimm is a list of hashes. There's one hash for each EEPROM we found.
1770# Each hash has the following keys:
1771#  * eeprom: Name of the eeprom data file
1772#  * file: Full path to the eeprom data file
1773#  * bytes: The EEPROM data (array)
1774#  * is_rambus: Whether this is a RAMBUS DIMM or not (boolean)
1775#  * chk_label: The label to display for the checksum or CRC
1776#  * chk_valid: Whether the checksum or CRC is valid or not (boolean)
1777#  * chk_spd: The checksum or CRC value found in the EEPROM
1778#  * chk_calc: The checksum or CRC computed from the EEPROM data
1779# Keys are added over time.
1780@dimm = get_dimm_list() unless $use_hexdump;
1781
1782for my $i (0 .. $#dimm) {
1783        my @bytes = readspd(0, 128, $dimm[$i]->{file});
1784        $dimm[$i]->{bytes} = \@bytes;
1785        $dimm[$i]->{is_rambus} = $bytes[0] < 4;         # Simple heuristic
1786        if ($dimm[$i]->{is_rambus} || $bytes[2] < 9) {
1787                ($dimm[$i]->{chk_label}, $dimm[$i]->{chk_valid},
1788                 $dimm[$i]->{chk_spd}, $dimm[$i]->{chk_calc}) =
1789                        checksum(\@bytes);
1790        } else {
1791                ($dimm[$i]->{chk_label}, $dimm[$i]->{chk_valid},
1792                 $dimm[$i]->{chk_spd}, $dimm[$i]->{chk_calc}) =
1793                        check_crc(\@bytes);
1794        }
1795}
1796
1797# Checksum or CRC validation
1798if (!$opt_igncheck) {
1799        for (my $i = 0; $i < @dimm; ) {
1800                if ($dimm[$i]->{chk_valid}) {
1801                        $i++;
1802                } else {
1803                        splice(@dimm, $i, 1);
1804                }
1805        }
1806}
1807
1808# Process the valid entries
1809for $current (0 .. $#dimm) {
1810        my @bytes = @{$dimm[$current]->{bytes}};
1811
1812        if ($opt_side_by_side) {
1813                printl("Decoding EEPROM", $dimm[$current]->{eeprom});
1814        }
1815
1816        if (!$use_hexdump) {
1817                if ($dimm[$current]->{file} =~ /-([\da-f]+)$/i) {
1818                        my $dimm_num = hex($1) - 0x50 + 1;
1819                        if ($dimm_num >= 1 && $dimm_num <= 8) {
1820                                printl("Guessing DIMM is in", "bank $dimm_num");
1821                        }
1822                }
1823        }
1824
1825# Decode first 3 bytes (0-2)
1826        prints("SPD EEPROM Information");
1827
1828        printl($dimm[$current]->{chk_label}, ($dimm[$current]->{chk_valid} ?
1829                sprintf("OK (%s)", $dimm[$current]->{chk_calc}) :
1830                sprintf("Bad\n(found %s, calculated %s)",
1831                        $dimm[$current]->{chk_spd}, $dimm[$current]->{chk_calc})));
1832
1833        my $temp;
1834        if ($dimm[$current]->{is_rambus}) {
1835                if ($bytes[0] == 1) { $temp = "0.7"; }
1836                elsif ($bytes[0] == 2) { $temp = "1.0"; }
1837                elsif ($bytes[0] == 0) { $temp = "Invalid"; }
1838                else { $temp = "Reserved"; }
1839                printl("SPD Revision", $temp);
1840        } else {
1841                my ($spd_size, $spd_used) = spd_sizes(\@bytes);
1842                printl("# of bytes written to SDRAM EEPROM", $spd_used);
1843                printl("Total number of bytes in EEPROM", $spd_size);
1844
1845                # If there's more data than what we've read, let's
1846                # read it now.  DDR3 will need this data.
1847                if ($spd_used > @bytes) {
1848                        push (@bytes,
1849                              readspd(@bytes, $spd_used - @bytes,
1850                                      $dimm[$current]->{file}));
1851                }
1852        }
1853
1854        my $type = sprintf("Unknown (0x%02x)", $bytes[2]);
1855        if ($dimm[$current]->{is_rambus}) {
1856                if ($bytes[2] == 1) { $type = "Direct Rambus"; }
1857                elsif ($bytes[2] == 17) { $type = "Rambus"; }
1858        } else {
1859                my @type_list = (
1860                        "Reserved", "FPM DRAM",         # 0, 1
1861                        "EDO", "Pipelined Nibble",      # 2, 3
1862                        "SDR SDRAM", "Multiplexed ROM", # 4, 5
1863                        "DDR SGRAM", "DDR SDRAM",       # 6, 7
1864                        "DDR2 SDRAM", "FB-DIMM",        # 8, 9
1865                        "FB-DIMM Probe", "DDR3 SDRAM",  # 10, 11
1866                );
1867                if ($bytes[2] < @type_list) {
1868                        $type = $type_list[$bytes[2]];
1869                }
1870        }
1871        printl("Fundamental Memory type", $type);
1872
1873# Decode next 61 bytes (3-63, depend on memory type)
1874        $decode_callback{$type}->(\@bytes)
1875                if exists $decode_callback{$type};
1876
1877        if ($type eq "DDR3 SDRAM") {
1878                # Decode DDR3-specific manufacturing data in bytes
1879                # 117-149
1880                decode_ddr3_mfg_data(\@bytes)
1881        } else {
1882                # Decode next 35 bytes (64-98, common to most
1883                # memory types)
1884                decode_manufacturing_information(\@bytes);
1885        }
1886
1887# Next 27 bytes (99-125) are manufacturer specific, can't decode
1888
1889# Last 2 bytes (126-127) are reserved, Intel used them as an extension
1890        if ($type eq "SDR SDRAM") {
1891                decode_intel_spec_freq(\@bytes);
1892        }
1893}
1894
1895# Side-by-side output format is only possible if all DIMMs have a similar
1896# output structure
1897if ($opt_side_by_side) {
1898        for $current (1 .. $#dimm) {
1899                my @ref_output = @{$dimm[0]->{output}};
1900                my @test_output = @{$dimm[$current]->{output}};
1901                my $line;
1902
1903                if (scalar @ref_output != scalar @test_output) {
1904                        $opt_side_by_side = 0;
1905                        last;
1906                }
1907
1908                for ($line = 0; $line < @ref_output; $line++) {
1909                        my ($ref_func, $ref_label, @ref_dummy) = @{$ref_output[$line]};
1910                        my ($test_func, $test_label, @test_dummy) = @{$test_output[$line]};
1911
1912                        if ($ref_func != $test_func || $ref_label ne $test_label) {
1913                                $opt_side_by_side = 0;
1914                                last;
1915                        }
1916                }
1917        }
1918
1919        if (!$opt_side_by_side) {
1920                printc("Side-by-side output only possible if all DIMMS are similar\n");
1921
1922                # Discard "Decoding EEPROM" entry from all outputs
1923                for $current (0 .. $#dimm) {
1924                        shift(@{$dimm[$current]->{output}});
1925                }
1926        }
1927}
1928
1929# Find out the longest value string to adjust the column width
1930# Note: this could be improved a bit by not taking into account strings
1931# which will end up being merged.
1932$sbs_col_width = 15;
1933if ($opt_side_by_side && !$opt_html) {
1934        for $current (0 .. $#dimm) {
1935                my @output = @{$dimm[$current]->{output}};
1936                my $line;
1937                my @strings;
1938
1939                for ($line = 0; $line < @output; $line++) {
1940                        my ($func, $label, $value) = @{$output[$line]};
1941                        push @strings, split("\n", $value) if defined $value;
1942                }
1943
1944                foreach $line (@strings) {
1945                        my $len = length($line);
1946                        $sbs_col_width = $len if $len > $sbs_col_width;
1947                }
1948        }
1949}
1950
1951# Print the decoded information for all DIMMs
1952for $current (0 .. $#dimm) {
1953        if ($opt_side_by_side) {
1954                print "\n\n";
1955        } else {
1956                print "<b><u>" if $opt_html;
1957                printl2("\n\nDecoding EEPROM", $dimm[$current]->{file});
1958                print "</u></b>" if $opt_html;
1959        }
1960        print "<table border=1>\n" if $opt_html;
1961
1962        my @output = @{$dimm[$current]->{output}};
1963        for (my $line = 0; $line < @output; $line++) {
1964                my ($func, @param) = @{$output[$line]};
1965
1966                if ($opt_side_by_side) {
1967                        foreach ($current+1 .. $#dimm) {
1968                                my @xoutput = @{$dimm[$_]->{output}};
1969                                if (@{$xoutput[$line]} == 3) {
1970                                        # Line with data, stack all values
1971                                        push @param, @{$xoutput[$line]}[2];
1972                                } else {
1973                                        # Separator, make it span
1974                                        push @param, scalar @dimm;
1975                                }
1976                        }
1977                }
1978
1979                $func->(@param);
1980        }
1981
1982        print "</table>\n" if $opt_html;
1983        last if $opt_side_by_side;
1984}
1985printl2("\n\nNumber of SDRAM DIMMs detected and decoded", scalar @dimm);
1986
1987print "</body></html>\n" if ($opt_html && !$opt_bodyonly);
Note: See TracBrowser for help on using the browser.