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

Revision 5694, 57.3 KB (checked in by khali, 6 years ago)

Carry the path to eeprom data files around, instead of hard-coding it
in several places. This makes the code less fragile and easier to
modify.

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