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

Revision 6061, 64.1 KB (checked in by khali, 10 months ago)

Read EEPROM contents before printing headers.

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