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

Revision 5551, 58.0 KB (checked in by khali, 5 years ago)

Add support for DDR3 SDRAM. Patch from Paul Goyette.

  • 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
435# Parameter: EEPROM bytes 0-127 (using 3-62)
436sub decode_sdr_sdram($)
437{
438        my $bytes = shift;
439        my ($l, $temp);
440
441# SPD revision
442        printl "SPD Revision", $bytes->[62];
443
444#size computation
445
446        prints "Memory Characteristics";
447
448        my $k = 0;
449        my $ii = 0;
450
451        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
452        if (($bytes->[5] <= 8) && ($bytes->[17] <= 8)) {
453                 $k = $bytes->[5] * $bytes->[17];
454        }
455
456        if ($ii > 0 && $ii <= 12 && $k > 0) {
457                printl "Size", ((1 << $ii) * $k) . " MB";
458        } else {
459                printl "INVALID SIZE", $bytes->[3] . "," . $bytes->[4] . "," .
460                                       $bytes->[5] . "," . $bytes->[17];
461        }
462
463        my @cas;
464        for ($ii = 0; $ii < 7; $ii++) {
465                push(@cas, $ii + 1) if ($bytes->[18] & (1 << $ii));
466        }
467
468        my $trcd;
469        my $trp;
470        my $tras;
471        my $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
472
473        $trcd = $bytes->[29];
474        $trp = $bytes->[27];;
475        $tras = $bytes->[30];
476
477        printl "tCL-tRCD-tRP-tRAS",
478                $cas[$#cas] . "-" .
479                ceil($trcd/$ctime) . "-" .
480                ceil($trp/$ctime) . "-" .
481                ceil($tras/$ctime);
482
483        $l = "Number of Row Address Bits";
484        if ($bytes->[3] == 0) { printl $l, "Undefined!"; }
485        elsif ($bytes->[3] == 1) { printl $l, "1/16"; }
486        elsif ($bytes->[3] == 2) { printl $l, "2/17"; }
487        elsif ($bytes->[3] == 3) { printl $l, "3/18"; }
488        else { printl $l, $bytes->[3]; }
489
490        $l = "Number of Col Address Bits";
491        if ($bytes->[4] == 0) { printl $l, "Undefined!"; }
492        elsif ($bytes->[4] == 1) { printl $l, "1/16"; }
493        elsif ($bytes->[4] == 2) { printl $l, "2/17"; }
494        elsif ($bytes->[4] == 3) { printl $l, "3/18"; }
495        else { printl $l, $bytes->[4]; }
496
497        $l = "Number of Module Rows";
498        if ($bytes->[5] == 0 ) { printl $l, "Undefined!"; }
499        else { printl $l, $bytes->[5]; }
500
501        $l = "Data Width";
502        if ($bytes->[7] > 1) {
503                printl $l, "Undefined!"
504        } else {
505                $temp = ($bytes->[7] * 256) + $bytes->[6];
506                printl $l, $temp;
507        }
508
509        $l = "Module Interface Signal Levels";
510        if ($bytes->[8] == 0) { printl $l, "5.0 Volt/TTL"; }
511        elsif ($bytes->[8] == 1) { printl $l, "LVTTL"; }
512        elsif ($bytes->[8] == 2) { printl $l, "HSTL 1.5"; }
513        elsif ($bytes->[8] == 3) { printl $l, "SSTL 3.3"; }
514        elsif ($bytes->[8] == 4) { printl $l, "SSTL 2.5"; }
515        elsif ($bytes->[8] == 255) { printl $l, "New Table"; }
516        else { printl $l, "Undefined!"; }
517
518        $l = "Module Configuration Type";
519        if ($bytes->[11] == 0) { printl $l, "No Parity"; }
520        elsif ($bytes->[11] == 1) { printl $l, "Parity"; }
521        elsif ($bytes->[11] == 2) { printl $l, "ECC"; }
522        else { printl $l, "Undefined!"; }
523
524        $l = "Refresh Type";
525        if ($bytes->[12] > 126) { printl $l, "Self Refreshing"; }
526        else { printl $l, "Not Self Refreshing"; }
527
528        $l = "Refresh Rate";
529        $temp = $bytes->[12] & 0x7f;
530        if ($temp == 0) { printl $l, "Normal (15.625 us)"; }
531        elsif ($temp == 1) { printl $l, "Reduced (3.9 us)"; }
532        elsif ($temp == 2) { printl $l, "Reduced (7.8 us)"; }
533        elsif ($temp == 3) { printl $l, "Extended (31.3 us)"; }
534        elsif ($temp == 4) { printl $l, "Extended (62.5 us)"; }
535        elsif ($temp == 5) { printl $l, "Extended (125 us)"; }
536        else { printl $l, "Undefined!"; }
537
538        $l = "Primary SDRAM Component Bank Config";
539        if ($bytes->[13] > 126) { printl $l, "Bank2 = 2 x Bank1"; }
540        else { printl $l, "No Bank2 OR Bank2 = Bank1 width"; }
541
542        $l = "Primary SDRAM Component Widths";
543        $temp = $bytes->[13] & 0x7f;
544        if ($temp == 0) { printl $l, "Undefined!\n"; }
545        else { printl $l, $temp; }
546
547        $l = "Error Checking SDRAM Component Bank Config";
548        if ($bytes->[14] > 126) { printl $l, "Bank2 = 2 x Bank1"; }
549        else { printl $l, "No Bank2 OR Bank2 = Bank1 width"; }
550
551        $l = "Error Checking SDRAM Component Widths";
552        $temp = $bytes->[14] & 0x7f;
553        if ($temp == 0) { printl $l, "Undefined!"; }
554        else { printl $l, $temp; }
555
556        $l = "Min Clock Delay for Back to Back Random Access";
557        if ($bytes->[15] == 0) { printl $l, "Undefined!"; }
558        else { printl $l, $bytes->[15]; }
559
560        $l = "Supported Burst Lengths";
561        my @array;
562        for ($ii = 0; $ii < 4; $ii++) {
563                push(@array, 1 << $ii) if ($bytes->[16] & (1 << $ii));
564        }
565        push(@array, "Page") if ($bytes->[16] & 128);
566        if (@array) { $temp = join ', ', @array; }
567        else { $temp = "None"; }
568        printl $l, $temp;
569
570        $l = "Number of Device Banks";
571        if ($bytes->[17] == 0) { printl $l, "Undefined/Reserved!"; }
572        else { printl $l, $bytes->[17]; }
573
574        $l = "Supported CAS Latencies";
575        printl $l, cas_latencies(@cas);
576
577        $l = "Supported CS Latencies";
578        @array = ();
579        for ($ii = 0; $ii < 7; $ii++) {
580                push(@array, $ii) if ($bytes->[19] & (1 << $ii));
581        }
582        if (@array) { $temp = join ', ', @array; }
583        else { $temp = "None"; }
584        printl $l, $temp;
585
586        $l = "Supported WE Latencies";
587        @array = ();
588        for ($ii = 0; $ii < 7; $ii++) {
589                push(@array, $ii) if ($bytes->[20] & (1 << $ii));
590        }
591        if (@array) { $temp = join ', ', @array; }
592        else { $temp = "None"; }
593        printl $l, $temp;
594
595        if (@cas >= 1) {
596                $l = "Cycle Time at CAS ".$cas[$#cas];
597                printl $l, "$ctime ns";
598
599                $l = "Access Time at CAS ".$cas[$#cas];
600                $temp = ($bytes->[10] >> 4) + ($bytes->[10] & 0xf) * 0.1;
601                printl $l, "$temp ns";
602        }
603
604        if (@cas >= 2 && spd_written(@$bytes[23..24])) {
605                $l = "Cycle Time at CAS ".$cas[$#cas-1];
606                $temp = $bytes->[23] >> 4;
607                if ($temp == 0) { printl $l, "Undefined!"; }
608                else {
609                        if ($temp < 4 ) { $temp += 15; }
610                        printl $l, $temp + (($bytes->[23] & 0xf) * 0.1) . " ns";
611                }
612
613                $l = "Access Time at CAS ".$cas[$#cas-1];
614                $temp = $bytes->[24] >> 4;
615                if ($temp == 0) { printl $l, "Undefined!"; }
616                else {
617                        if ($temp < 4 ) { $temp += 15; }
618                        printl $l, $temp + (($bytes->[24] & 0xf) * 0.1) . " ns";
619                }
620        }
621
622        if (@cas >= 3 && spd_written(@$bytes[25..26])) {
623                $l = "Cycle Time at CAS ".$cas[$#cas-2];
624                $temp = $bytes->[25] >> 2;
625                if ($temp == 0) { printl $l, "Undefined!"; }
626                else { printl $l, $temp + ($bytes->[25] & 0x3) * 0.25 . " ns"; }
627
628                $l = "Access Time at CAS ".$cas[$#cas-2];
629                $temp = $bytes->[26] >> 2;
630                if ($temp == 0) { printl $l, "Undefined!"; }
631                else { printl $l, $temp + ($bytes->[26] & 0x3) * 0.25 . " ns"; }
632        }
633
634        $l = "SDRAM Module Attributes";
635        $temp = "";
636        if ($bytes->[21] & 1) { $temp .= "Buffered Address/Control Inputs\n"; }
637        if ($bytes->[21] & 2) { $temp .= "Registered Address/Control Inputs\n"; }
638        if ($bytes->[21] & 4) { $temp .= "On card PLL (clock)\n"; }
639        if ($bytes->[21] & 8) { $temp .= "Buffered DQMB Inputs\n"; }
640        if ($bytes->[21] & 16) { $temp .= "Registered DQMB Inputs\n"; }
641        if ($bytes->[21] & 32) { $temp .= "Differential Clock Input\n"; }
642        if ($bytes->[21] & 64) { $temp .= "Redundant Row Address\n"; }
643        if ($bytes->[21] & 128) { $temp .= "Undefined (bit 7)\n"; }
644        if ($bytes->[21] == 0) { $temp .= "(None Reported)\n"; }
645        printl $l, $temp;
646
647        $l = "SDRAM Device Attributes (General)";
648        $temp = "";
649        if ($bytes->[22] & 1) { $temp .= "Supports Early RAS# Recharge\n"; }
650        if ($bytes->[22] & 2) { $temp .= "Supports Auto-Precharge\n"; }
651        if ($bytes->[22] & 4) { $temp .= "Supports Precharge All\n"; }
652        if ($bytes->[22] & 8) { $temp .= "Supports Write1/Read Burst\n"; }
653        if ($bytes->[22] & 16) { $temp .= "Lower VCC Tolerance: 5%\n"; }
654        else { $temp .= "Lower VCC Tolerance: 10%\n"; }
655        if ($bytes->[22] & 32) { $temp .= "Upper VCC Tolerance: 5%\n"; }
656        else { $temp .= "Upper VCC Tolerance: 10%\n"; }
657        if ($bytes->[22] & 64) { $temp .= "Undefined (bit 6)\n"; }
658        if ($bytes->[22] & 128) { $temp .= "Undefined (bit 7)\n"; }
659        printl $l, $temp;
660
661        $l = "Minimum Row Precharge Time";
662        if ($bytes->[27] == 0) { printl $l, "Undefined!"; }
663        else { printl $l, "$bytes->[27] ns"; }
664
665        $l = "Row Active to Row Active Min";
666        if ($bytes->[28] == 0) { printl $l, "Undefined!"; }
667        else { printl $l, "$bytes->[28] ns"; }
668
669        $l = "RAS to CAS Delay";
670        if ($bytes->[29] == 0) { printl $l, "Undefined!"; }
671        else { printl $l, "$bytes->[29] ns"; }
672
673        $l = "Min RAS Pulse Width";
674        if ($bytes->[30] == 0) { printl $l, "Undefined!"; }
675        else { printl $l, "$bytes->[30] ns"; }
676
677        $l = "Row Densities";
678        $temp = "";
679        if ($bytes->[31] & 1) { $temp .= "4 MByte\n"; }
680        if ($bytes->[31] & 2) { $temp .= "8 MByte\n"; }
681        if ($bytes->[31] & 4) { $temp .= "16 MByte\n"; }
682        if ($bytes->[31] & 8) { $temp .= "32 MByte\n"; }
683        if ($bytes->[31] & 16) { $temp .= "64 MByte\n"; }
684        if ($bytes->[31] & 32) { $temp .= "128 MByte\n"; }
685        if ($bytes->[31] & 64) { $temp .= "256 MByte\n"; }
686        if ($bytes->[31] & 128) { $temp .= "512 MByte\n"; }
687        if ($bytes->[31] == 0) { $temp .= "(Undefined! -- None Reported!)\n"; }
688        printl $l, $temp;
689
690        if (($bytes->[32] & 0xf) <= 9) {
691                $l = "Command and Address Signal Setup Time";
692                $temp = (($bytes->[32] & 0x7f) >> 4) + ($bytes->[32] & 0xf) * 0.1;
693                printl $l, (($bytes->[32] >> 7) ? -$temp : $temp) . " ns";
694        }
695
696        if (($bytes->[33] & 0xf) <= 9) {
697                $l = "Command and Address Signal Hold Time";
698                $temp = (($bytes->[33] & 0x7f) >> 4) + ($bytes->[33] & 0xf) * 0.1;
699                printl $l, (($bytes->[33] >> 7) ? -$temp : $temp) . " ns";
700        }
701
702        if (($bytes->[34] & 0xf) <= 9) {
703                $l = "Data Signal Setup Time";
704                $temp = (($bytes->[34] & 0x7f) >> 4) + ($bytes->[34] & 0xf) * 0.1;
705                printl $l, (($bytes->[34] >> 7) ? -$temp : $temp) . " ns";
706        }
707
708        if (($bytes->[35] & 0xf) <= 9) {
709                $l = "Data Signal Hold Time";
710                $temp = (($bytes->[35] & 0x7f) >> 4) + ($bytes->[35] & 0xf) * 0.1;
711                printl $l, (($bytes->[35] >> 7) ? -$temp : $temp) . " ns";
712        }
713}
714
715# Parameter: EEPROM bytes 0-127 (using 3-62)
716sub decode_ddr_sdram($)
717{
718        my $bytes = shift;
719        my ($l, $temp);
720
721# SPD revision
722        if ($bytes->[62] != 0xff) {
723                printl "SPD Revision", ($bytes->[62] >> 4) . "." .
724                                       ($bytes->[62] & 0xf);
725        }
726
727# speed
728        prints "Memory Characteristics";
729
730        $l = "Maximum module speed";
731        $temp = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
732        my $ddrclk = 2 * (1000 / $temp);
733        my $tbits = ($bytes->[7] * 256) + $bytes->[6];
734        if (($bytes->[11] == 2) || ($bytes->[11] == 1)) { $tbits = $tbits - 8; }
735        my $pcclk = int ($ddrclk * $tbits / 8);
736        $pcclk += 100 if ($pcclk % 100) >= 50; # Round properly
737        $pcclk = $pcclk - ($pcclk % 100);
738        $ddrclk = int ($ddrclk);
739        printl $l, "${ddrclk}MHz (PC${pcclk})";
740
741#size computation
742        my $k = 0;
743        my $ii = 0;
744
745        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
746        if (($bytes->[5] <= 8) && ($bytes->[17] <= 8)) {
747                 $k = $bytes->[5] * $bytes->[17];
748        }
749
750        if ($ii > 0 && $ii <= 12 && $k > 0) {
751                printl "Size", ((1 << $ii) * $k) . " MB";
752        } else {
753                printl "INVALID SIZE", $bytes->[3] . ", " . $bytes->[4] . ", " .
754                                       $bytes->[5] . ", " . $bytes->[17];
755        }
756
757        my $highestCAS = 0;
758        my %cas;
759        for ($ii = 0; $ii < 7; $ii++) {
760                if ($bytes->[18] & (1 << $ii)) {
761                        $highestCAS = 1+$ii*0.5;
762                        $cas{$highestCAS}++;
763                }
764        }
765
766        my $trcd;
767        my $trp;
768        my $tras;
769        my $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
770
771        $trcd = ($bytes->[29] >> 2) + (($bytes->[29] & 3) * 0.25);
772        $trp = ($bytes->[27] >> 2) + (($bytes->[27] & 3) * 0.25);
773        $tras = $bytes->[30];
774
775        printl "tCL-tRCD-tRP-tRAS",
776                $highestCAS . "-" .
777                ceil($trcd/$ctime) . "-" .
778                ceil($trp/$ctime) . "-" .
779                ceil($tras/$ctime);
780
781# latencies
782        printl "Supported CAS Latencies", cas_latencies(keys %cas);
783
784        my @array;
785        for ($ii = 0; $ii < 7; $ii++) {
786                push(@array, $ii) if ($bytes->[19] & (1 << $ii));
787        }
788        if (@array) { $temp = join ', ', @array; }
789        else { $temp = "None"; }
790        printl "Supported CS Latencies", $temp;
791
792        @array = ();
793        for ($ii = 0; $ii < 7; $ii++) {
794                push(@array, $ii) if ($bytes->[20] & (1 << $ii));
795        }
796        if (@array) { $temp = join ', ', @array; }
797        else { $temp = "None"; }
798        printl "Supported WE Latencies", $temp;
799
800# timings
801        if (exists $cas{$highestCAS}) {
802                printl "Minimum Cycle Time at CAS $highestCAS",
803                       "$ctime ns";
804
805                printl "Maximum Access Time at CAS $highestCAS",
806                       (($bytes->[10] >> 4) * 0.1 + ($bytes->[10] & 0xf) * 0.01) . " ns";
807        }
808
809        if (exists $cas{$highestCAS-0.5} && spd_written(@$bytes[23..24])) {
810                printl "Minimum Cycle Time at CAS ".($highestCAS-0.5),
811                       (($bytes->[23] >> 4) + ($bytes->[23] & 0xf) * 0.1) . " ns";
812
813                printl "Maximum Access Time at CAS ".($highestCAS-0.5),
814                       (($bytes->[24] >> 4) * 0.1 + ($bytes->[24] & 0xf) * 0.01) . " ns";
815        }
816
817        if (exists $cas{$highestCAS-1} && spd_written(@$bytes[25..26])) {
818                printl "Minimum Cycle Time at CAS ".($highestCAS-1),
819                       (($bytes->[25] >> 4) + ($bytes->[25] & 0xf) * 0.1) . " ns";
820
821                printl "Maximum Access Time at CAS ".($highestCAS-1),
822                       (($bytes->[26] >> 4) * 0.1 + ($bytes->[26] & 0xf) * 0.01) . " ns";
823        }
824
825# module attributes
826        if ($bytes->[47] & 0x03) {
827                if (($bytes->[47] & 0x03) == 0x01) { $temp = "1.125\" to 1.25\""; }
828                elsif (($bytes->[47] & 0x03) == 0x02) { $temp = "1.7\""; }
829                elsif (($bytes->[47] & 0x03) == 0x03) { $temp = "Other"; }
830                printl "Module Height", $temp;
831        }
832}
833
834sub ddr2_sdram_ctime($)
835{
836        my $byte = shift;
837        my $ctime;
838
839        $ctime = $byte >> 4;
840        if (($byte & 0xf) <= 9) { $ctime += ($byte & 0xf) * 0.1; }
841        elsif (($byte & 0xf) == 10) { $ctime += 0.25; }
842        elsif (($byte & 0xf) == 11) { $ctime += 0.33; }
843        elsif (($byte & 0xf) == 12) { $ctime += 0.66; }
844        elsif (($byte & 0xf) == 13) { $ctime += 0.75; }
845
846        return $ctime;
847}
848
849sub ddr2_sdram_atime($)
850{
851        my $byte = shift;
852        my $atime;
853
854        $atime = ($byte >> 4) * 0.1 + ($byte & 0xf) * 0.01;
855
856        return $atime;
857}
858
859# Base, high-bit, 3-bit fraction code
860sub ddr2_sdram_rtime($$$)
861{
862        my ($rtime, $msb, $ext) = @_;
863        my @table = (0, .25, .33, .50, .66, .75);
864
865        return $rtime + $msb * 256 + $table[$ext];
866}
867
868sub ddr2_module_types($)
869{
870        my $byte = shift;
871        my @types = qw(RDIMM UDIMM SO-DIMM Micro-DIMM Mini-RDIMM Mini-UDIMM);
872        my @widths = (133.35, 133.25, 67.6, 45.5, 82.0, 82.0);
873        my @suptypes;
874        local $_;
875
876        foreach (0..5) {
877                push @suptypes, "$types[$_] ($widths[$_] mm)"
878                        if ($byte & (1 << $_));
879        }
880
881        return @suptypes;
882}
883
884sub ddr2_refresh_rate($)
885{
886        my $byte = shift;
887        my @refresh = qw(Normal Reduced Reduced Extended Extended Extended);
888        my @refresht = (15.625, 3.9, 7.8, 31.3, 62.5, 125);
889
890        return "$refresh[$byte & 0x7f] ($refresht[$byte & 0x7f] us)".
891               ($byte & 0x80 ? " - Self Refresh" : "");
892}
893
894# Parameter: EEPROM bytes 0-127 (using 3-62)
895sub decode_ddr2_sdram($)
896{
897        my $bytes = shift;
898        my ($l, $temp);
899        my $ctime;
900
901# SPD revision
902        if ($bytes->[62] != 0xff) {
903                printl "SPD Revision", ($bytes->[62] >> 4) . "." .
904                                       ($bytes->[62] & 0xf);
905        }
906
907# speed
908        prints "Memory Characteristics";
909
910        $l = "Maximum module speed";
911        $ctime = ddr2_sdram_ctime($bytes->[9]);
912        my $ddrclk = 2 * (1000 / $ctime);
913        my $tbits = ($bytes->[7] * 256) + $bytes->[6];
914        if ($bytes->[11] & 0x03) { $tbits = $tbits - 8; }
915        my $pcclk = int ($ddrclk * $tbits / 8);
916        # Round down to comply with Jedec
917        $pcclk = $pcclk - ($pcclk % 100);
918        $ddrclk = int ($ddrclk);
919        printl $l, "${ddrclk}MHz (PC2-${pcclk})";
920
921#size computation
922        my $k = 0;
923        my $ii = 0;
924
925        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
926        $k = (($bytes->[5] & 0x7) + 1) * $bytes->[17];
927
928        if($ii > 0 && $ii <= 12 && $k > 0) {
929                printl "Size", ((1 << $ii) * $k) . " MB";
930        } else {
931                printl "INVALID SIZE", $bytes->[3] . "," . $bytes->[4] . "," .
932                                       $bytes->[5] . "," . $bytes->[17];
933        }
934
935        printl "Banks x Rows x Columns x Bits",
936               join(' x ', $bytes->[17], $bytes->[3], $bytes->[4], $bytes->[6]);
937        printl "Ranks", ($bytes->[5] & 7) + 1;
938
939        printl "SDRAM Device Width", $bytes->[13]." bits";
940
941        my @heights = ('< 25.4', '25.4', '25.4 - 30.0', '30.0', '30.5', '> 30.5');
942        printl "Module Height", $heights[$bytes->[5] >> 5]." mm";
943
944        my @suptypes = ddr2_module_types($bytes->[20]);
945        printl "Module Type".(@suptypes > 1 ? 's' : ''), join(', ', @suptypes);
946
947        printl "DRAM Package", $bytes->[5] & 0x10 ? "Stack" : "Planar";
948
949        my @volts = ("TTL (5V Tolerant)", "LVTTL", "HSTL 1.5V",
950                     "SSTL 3.3V", "SSTL 2.5V", "SSTL 1.8V", "TBD");
951        printl "Voltage Interface Level", $volts[$bytes->[8]];
952
953        printl "Refresh Rate", ddr2_refresh_rate($bytes->[12]);
954
955        my @burst;
956        push @burst, 4 if ($bytes->[16] & 4);
957        push @burst, 8 if ($bytes->[16] & 8);
958        $burst[0] = 'None' if !@burst;
959        printl "Supported Burst Lengths", join(', ', @burst);
960
961        my $highestCAS = 0;
962        my %cas;
963        for ($ii = 2; $ii < 7; $ii++) {
964                if ($bytes->[18] & (1 << $ii)) {
965                        $highestCAS = $ii;
966                        $cas{$highestCAS}++;
967                }
968        }
969
970        my $trcd;
971        my $trp;
972        my $tras;
973
974        $trcd = ($bytes->[29] >> 2) + (($bytes->[29] & 3) * 0.25);
975        $trp = ($bytes->[27] >> 2) + (($bytes->[27] & 3) * 0.25);
976        $tras = $bytes->[30];
977
978        printl "tCL-tRCD-tRP-tRAS",
979                $highestCAS . "-" .
980                ceil($trcd/$ctime) . "-" .
981                ceil($trp/$ctime) . "-" .
982                ceil($tras/$ctime);
983
984# latencies
985        printl "Supported CAS Latencies (tCL)", cas_latencies(keys %cas);
986
987# timings
988        if (exists $cas{$highestCAS}) {
989                printl "Minimum Cycle Time at CAS $highestCAS (tCK min)",
990                       tns($ctime);
991                printl "Maximum Access Time at CAS $highestCAS (tAC)",
992                       tns(ddr2_sdram_atime($bytes->[10]));
993        }
994
995        if (exists $cas{$highestCAS-1} && spd_written(@$bytes[23..24])) {
996                printl "Minimum Cycle Time at CAS ".($highestCAS-1),
997                       tns(ddr2_sdram_ctime($bytes->[23]));
998                printl "Maximum Access Time at CAS ".($highestCAS-1),
999                       tns(ddr2_sdram_atime($bytes->[24]));
1000        }
1001
1002        if (exists $cas{$highestCAS-2} && spd_written(@$bytes[25..26])) {
1003                printl "Minimum Cycle Time at CAS ".($highestCAS-2),
1004                       tns(ddr2_sdram_ctime($bytes->[25]));
1005                printl "Maximum Access Time at CAS ".($highestCAS-2),
1006                       tns(ddr2_sdram_atime($bytes->[26]));
1007        }
1008        printl "Maximum Cycle Time (tCK max)",
1009               tns(ddr2_sdram_ctime($bytes->[43]));
1010
1011# more timing information
1012        prints("Timing Parameters");
1013        printl "Address/Command Setup Time Before Clock (tIS)",
1014               tns(ddr2_sdram_atime($bytes->[32]));
1015        printl "Address/Command Hold Time After Clock (tIH)",
1016               tns(ddr2_sdram_atime($bytes->[33]));
1017        printl "Data Input Setup Time Before Strobe (tDS)",
1018               tns(ddr2_sdram_atime($bytes->[34]));
1019        printl "Data Input Hold Time After Strobe (tDH)",
1020               tns(ddr2_sdram_atime($bytes->[35]));
1021        printl "Minimum Row Precharge Delay (tRP)", tns($trp);
1022        printl "Minimum Row Active to Row Active Delay (tRRD)",
1023               tns($bytes->[28]/4);
1024        printl "Minimum RAS# to CAS# Delay (tRCD)", tns($trcd);
1025        printl "Minimum RAS# Pulse Width (tRAS)", tns($tras);
1026        printl "Write Recovery Time (tWR)", tns($bytes->[36]/4);
1027        printl "Minimum Write to Read CMD Delay (tWTR)", tns($bytes->[37]/4);
1028        printl "Minimum Read to Pre-charge CMD Delay (tRTP)", tns($bytes->[38]/4);
1029        printl "Minimum Active to Auto-refresh Delay (tRC)",
1030               tns(ddr2_sdram_rtime($bytes->[41], 0, ($bytes->[40] >> 4) & 7));
1031        printl "Minimum Recovery Delay (tRFC)",
1032               tns(ddr2_sdram_rtime($bytes->[42], $bytes->[40] & 1,
1033                                    ($bytes->[40] >> 1) & 7));
1034        printl "Maximum DQS to DQ Skew (tDQSQ)", tns($bytes->[44]/100);
1035        printl "Maximum Read Data Hold Skew (tQHS)", tns($bytes->[45]/100);
1036        printl "PLL Relock Time", $bytes->[46] . " us" if ($bytes->[46]);
1037}
1038
1039# Parameter: EEPROM bytes 0-127 (using 3-76)
1040sub decode_ddr3_sdram($)
1041{
1042        my $bytes = shift;
1043        my $l;
1044        my $temp;
1045        my $ctime;
1046
1047        my @module_types = ("Undefined", "RDIMM", "UDIMM", "SO-DIMM",
1048                            "Micro-DIMM", "Mini-RDIMM", "Mini-UDIMM");
1049
1050        printl "Module Type", ($bytes->[3] <= $#module_types) ?
1051                                        $module_types[$bytes->[3]] :
1052                                        sprint("Reserved (0x%.2X)", $bytes->[3]);
1053
1054# speed
1055        prints "Memory Characteristics";
1056
1057        $l = "Fine time base";
1058        my $dividend = ($bytes->[9] >> 4) & 15;
1059        my $divisor  = $bytes->[9] & 15;
1060        printl $l, sprintf("%.3f", $dividend / $divisor) . " ps";
1061
1062        $l = "Medium time base";
1063        $dividend = $bytes->[10];
1064        $divisor  = $bytes->[11];
1065        my $mtb = $dividend / $divisor;
1066        printl $l, tns3($mtb);
1067
1068        $l = "Maximum module speed";
1069        $ctime = $bytes->[12] * $mtb;
1070        my $ddrclk = 2 * (1000 / $ctime);
1071        my $tbits = 1 << (($bytes->[8] & 7) + 3);
1072        my $pcclk = int ($ddrclk * $tbits / 8);
1073        $ddrclk = int ($ddrclk);
1074        printl $l, "${ddrclk}MHz (PC3-${pcclk})";
1075
1076# Size computation
1077
1078        my $cap =  ($bytes->[4]       & 15) + 28;
1079        $cap   +=  ($bytes->[8]       & 7)  + 3;
1080        $cap   -=  ($bytes->[7]       & 7)  + 2;
1081        $cap   -= 20 + 3;
1082        my $k   = (($bytes->[7] >> 3) & 31) + 1;
1083        printl "Size", ((1 << $cap) * $k) . " MB";
1084
1085        printl "Banks x Rows x Columns x Bits",
1086               join(' x ', 1 << ((($bytes->[4] >> 4) &  7) +  3),
1087                           ((($bytes->[5] >> 3) & 31) + 12),
1088                           ( ($bytes->[5]       &  7) +  9),
1089                           ( 1 << (($bytes->[8] &  7) + 3)) );
1090        printl "Ranks", $k;
1091
1092        printl "SDRAM Device Width", (1 << (($bytes->[7] & 7) + 2))." bits";
1093
1094        my $taa;
1095        my $trcd;
1096        my $trp;
1097        my $tras;
1098
1099        $taa  = int($bytes->[16] / $bytes->[12]);
1100        $trcd = int($bytes->[18] / $bytes->[12]);
1101        $trp  = int($bytes->[20] / $bytes->[12]);
1102        $tras = int((($bytes->[21] >> 4) * 256 + $bytes->[22]) / $bytes->[12]);
1103
1104        printl "tCL-tRCD-tRP-tRAS", join("-", $taa, $trcd, $trp, $tras);
1105
1106# latencies
1107        my $highestCAS = 0;
1108        my %cas;
1109        my $ii;
1110        my $cas_sup = ($bytes->[15] << 8) + $bytes->[14];
1111        for ($ii = 0; $ii < 15; $ii++) {
1112                if ($cas_sup & (1 << $ii)) {
1113                        $highestCAS = $ii + 4;
1114                        $cas{$highestCAS}++;
1115                }
1116        }
1117        printl "Supported CAS Latencies (tCL)", cas_latencies(keys %cas);
1118
1119# more timing information
1120        prints "Timing Parameters" ;
1121
1122        printl "Minimum Write Recovery time (tWR)", tns3($bytes->[17] * $mtb);
1123        printl "Minimum Row Active to Row Active Delay (tRRD)",
1124                tns3($bytes->[19] * $mtb);
1125        printl "Minimum Active to Auto-Refresh Delay (tRC)",
1126                tns3((((($bytes->[21] >> 4) & 15) << 8) + $bytes->[23]) * $mtb);
1127        printl "Minimum Recovery Delay (tRFC)", 
1128                tns3((($bytes->[25] << 8) + $bytes->[24]) * $mtb);
1129        printl "Minimum Write to Read CMD Delay (tWTR)",
1130                tns3($bytes->[26] * $mtb);
1131        printl "Minimum Read to Pre-charge CMD Delay (tRTP)",
1132                tns3($bytes->[27] * $mtb);
1133        printl "Minimum Four Activate Window Delay (tFAW)",
1134                tns3(((($bytes->[28] & 15) << 8) + $bytes->[29]) * $mtb);
1135
1136# miscellaneous stuff
1137        prints "Optional Features";
1138
1139        my $volts = "1.5V";
1140        if ($bytes->[6] & 1) {
1141                $volts .= " tolerant";
1142        }
1143        if ($bytes->[6] & 2) {
1144                $volts .= ", 1.35V ";
1145        }
1146        if ($bytes->[6] & 4) {
1147                $volts .= ", 1.2X V";
1148        }
1149        printl "Operable voltages", $volts;
1150        printl "RZQ/6 supported?", ($bytes->[30] & 1) ? "Yes" : "No";
1151        printl "RZQ/7 supported?", ($bytes->[30] & 2) ? "Yes" : "No";
1152        printl "DLL-Off Mode supported?", ($bytes->[30] & 128) ? "Yes" : "No";
1153        printl "Operating temperature range", sprintf "0-%dC",
1154                ($bytes->[31] & 1) ? 95 : 85;
1155        printl "Refresh Rate in extended temp range",
1156                ($bytes->[31] & 2) ? "2X" : "1X";
1157        printl "Auto Self-Refresh?", ($bytes->[31] & 4) ? "Yes" : "No";
1158        printl "On-Die Thermal Sensor readout?",
1159                ($bytes->[31] & 8) ? "Yes" : "No";
1160        printl "Partial Array Self-Refresh?",
1161                ($bytes->[31] & 128) ? "Yes" : "No";
1162        printl "Thermal Sensor Accuracy",
1163                ($bytes->[32] & 128) ? sprintf($bytes->[32] & 127) :
1164                                        "Not implemented";
1165        printl "SDRAM Device Type",
1166                ($bytes->[33] & 128) ? sprintf($bytes->[33] & 127) :
1167                                        "Standard Monolithic";
1168        if ($bytes->[3] >= 1 && $bytes->[3] <= 6) {
1169
1170                prints "Physical Characteristics";
1171                printl "Module Height (mm)", ($bytes->[60] & 31) + 15;
1172                printl "Module Thickness (mm)", sprintf("%d front, %d back",
1173                                                ($bytes->[61] & 15) + 1, 
1174                                                (($bytes->[61] >> 4) & 15) +1);
1175                printl "Module Width (mm)", ($bytes->[3] <= 2) ? 133.5 :
1176                                        ($bytes->[3] == 3) ? 67.6 : "TBD";
1177
1178                my $alphabet = "ABCDEFGHJKLMNPRTUVWY";
1179                my $ref = $bytes->[62] & 31;
1180                my $ref_card;
1181                if ($ref == 31) {
1182                        $ref_card = "ZZ";
1183                } else {
1184                        if ($bytes->[62] & 128) {
1185                                $ref += 31;
1186                        }
1187                        if ($ref < length $alphabet) {
1188                                $ref_card = substr $alphabet, $ref, 1;
1189                        } else {
1190                                my $ref1 = int($ref / (length $alphabet));
1191                                $ref -= (length $alphabet) * $ref1;
1192                                $ref_card = (substr $alphabet, $ref1, 1) .
1193                                            (substr $alphabet, $ref, 1);
1194                        }
1195                }
1196                printl "Module Reference Card", $ref_card;
1197        }
1198        if ($bytes->[3] == 1 || $bytes->[3] == 5) {
1199                prints "Registered DIMM";
1200
1201                my @rows = ("Undefined", 1, 2, 4);
1202                printl "# DRAM Rows", $rows[($bytes->[63] >> 2) & 3];
1203                printl "# Registers", $rows[$bytes->[63] & 3];
1204                printl "Register manufacturer",
1205                        manufacturer_ddr3($bytes->[65], $bytes->[66]);
1206                printl "Register device type",
1207                                (($bytes->[68] & 7) == 0) ? "SSTE32882" :
1208                                        "Undefined";
1209                printl "Register revision", sprintf("0x%.2X", $bytes->[67]);
1210                printl "Heat spreader characteristics",
1211                                ($bytes->[64] < 128) ? "Not incorporated" :
1212                                        sprintf("%.2X", ($bytes->[64] & 127));
1213                my $regs;
1214                for (my $i = 0; $i < 8; $i++) {
1215                        $regs = sprintf("SSTE32882 RC%d/RC%d",
1216                                        $i * 2, $i * 2 + 1);
1217                        printl $regs, sprintf("%.2X", $bytes->[$i + 69]);
1218                }
1219        }
1220}
1221
1222# Parameter: EEPROM bytes 0-127 (using 4-5)
1223sub decode_direct_rambus($)
1224{
1225        my $bytes = shift;
1226
1227#size computation
1228        prints "Memory Characteristics";
1229
1230        my $ii;
1231
1232        $ii = ($bytes->[4] & 0x0f) + ($bytes->[4] >> 4) + ($bytes->[5] & 0x07) - 13;
1233
1234        if ($ii > 0 && $ii < 16) {
1235                printl "Size", (1 << $ii) . " MB";
1236        } else {
1237                printl "INVALID SIZE", sprintf("0x%02x, 0x%02x",
1238                                               $bytes->[4], $bytes->[5]);
1239        }
1240}
1241
1242# Parameter: EEPROM bytes 0-127 (using 3-5)
1243sub decode_rambus($)
1244{
1245        my $bytes = shift;
1246
1247#size computation
1248        prints "Memory Characteristics";
1249
1250        my $ii;
1251
1252        $ii = ($bytes->[3] & 0x0f) + ($bytes->[3] >> 4) + ($bytes->[5] & 0x07) - 13;
1253
1254        if ($ii > 0 && $ii < 16) {
1255                printl "Size", (1 << $ii) . " MB";
1256        } else {
1257                printl "INVALID SIZE", sprintf("0x%02x, 0x%02x",
1258                                               $bytes->[3], $bytes->[5]);
1259        }
1260}
1261
1262%decode_callback = (
1263        "SDR SDRAM"     => \&decode_sdr_sdram,
1264        "DDR SDRAM"     => \&decode_ddr_sdram,
1265        "DDR2 SDRAM"    => \&decode_ddr2_sdram,
1266        "DDR3 SDRAM"    => \&decode_ddr3_sdram,
1267        "Direct Rambus" => \&decode_direct_rambus,
1268        "Rambus"        => \&decode_rambus,
1269);
1270
1271# Parameter: Manufacturing year/week bytes
1272sub manufacture_date($$)
1273{
1274        my ($year, $week) = @_;
1275
1276        # In theory the year and week are in BCD format, but
1277        # this is not always true in practice :(
1278        if (($year & 0xf0) <= 0x90 && ($year & 0x0f) <= 0x09
1279         && ($week & 0xf0) <= 0x90 && ($week & 0x0f) <= 0x09) {
1280                # Note that this heuristic will break in year 2080
1281                return sprintf("%d%02X-W%02X\n",
1282                                $year >= 0x80 ? 19 : 20, $year, $week);
1283        } else {
1284                return sprintf("0x%02X%02X\n", $year, $week);
1285        }
1286}
1287
1288# Parameter: EEPROM bytes 0-175 (using 117-149)
1289sub decode_ddr3_mfg_data($)
1290{
1291        my $bytes = shift;
1292
1293        prints "Manufacturer Data";
1294
1295        printl "Module Manufacturer",
1296                manufacturer_ddr3($bytes->[117], $bytes->[118]);
1297
1298        printl "DRAM Manufacturer",
1299                manufacturer_ddr3($bytes->[148], $bytes->[149]);
1300
1301        my $l = "Manufacturing Location";
1302        my $temp = (chr($bytes->[119]) =~ m/^[\w\d]$/) ? chr($bytes->[119])
1303              : sprintf("0x%.2X", $bytes->[119]);
1304        printl $l, $temp;
1305
1306        $l = "Manufacturing Date";
1307        printl $l, manufacture_date($bytes->[120], $bytes->[121]);
1308
1309        $l = "Assembly Serial Number";
1310        $temp = sprintf("0x%02X%02X%02X%02X\n", $bytes->[122], $bytes->[123],
1311                $bytes->[124], $bytes->[125]);
1312        printl $l, $temp;
1313
1314        $l = "Part Number";
1315        $temp = "";
1316        for (my $i = 128; $i <= 145; $i++) {
1317                $temp .= chr($bytes->[$i]);
1318        };
1319        printl $l, $temp;
1320
1321        $l = "Revision";
1322        $temp = sprintf("0x%02X%02X\n", $bytes->[146], $bytes->[147]);
1323        printl $l, $temp;
1324}
1325
1326# Parameter: EEPROM bytes 0-127 (using 64-98)
1327sub decode_manufacturing_information($)
1328{
1329        my $bytes = shift;
1330        my ($l, $temp, $extra);
1331
1332        prints "Manufacturing Information";
1333
1334        $l = "Manufacturer";
1335        # $extra is a reference to an array containing up to
1336        # 7 extra bytes from the Manufacturer field. Sometimes
1337        # these bytes are filled with interesting data.
1338        ($temp, $extra) = manufacturer(@{$bytes}[64..71]);
1339        printl $l, $temp;
1340        $l = "Custom Manufacturer Data";
1341        $temp = manufacturer_data(@{$extra});
1342        printl $l, $temp if defined $temp;
1343
1344        if (spd_written($bytes->[72])) {
1345                # Try the location code as ASCII first, as earlier specifications
1346                # suggested this. As newer specifications don't mention it anymore,
1347                # we still fall back to binary.
1348                $l = "Manufacturing Location Code";
1349                $temp = (chr($bytes->[72]) =~ m/^[\w\d]$/) ? chr($bytes->[72])
1350                      : sprintf("0x%.2X", $bytes->[72]);
1351                printl $l, $temp;
1352        }
1353
1354        $l = "Part Number";
1355        $temp = part_number(@{$bytes}[73..90]);
1356        printl $l, $temp;
1357
1358        if (spd_written(@{$bytes}[91..92])) {
1359                $l = "Revision Code";
1360                $temp = sprintf("0x%02X%02X\n", @{$bytes}[91..92]);
1361                printl $l, $temp;
1362        }
1363
1364        if (spd_written(@{$bytes}[93..94])) {
1365                $l = "Manufacturing Date";
1366                printl $l, manufacture_date($bytes->[93], $bytes->[94]);
1367        }
1368
1369        if (spd_written(@{$bytes}[95..98])) {
1370                $l = "Assembly Serial Number";
1371                $temp = sprintf("0x%02X%02X%02X%02X\n",
1372                                @{$bytes}[95..98]);
1373                printl $l, $temp;
1374        }
1375}
1376
1377# Parameter: EEPROM bytes 0-127 (using 126-127)
1378sub decode_intel_spec_freq($)
1379{
1380        my $bytes = shift;
1381        my ($l, $temp);
1382
1383        prints "Intel Specification";
1384
1385        $l = "Frequency";
1386        if ($bytes->[126] == 0x66) { $temp = "66MHz\n"; }
1387        elsif ($bytes->[126] == 100) { $temp = "100MHz or 133MHz\n"; }
1388        elsif ($bytes->[126] == 133) { $temp = "133MHz\n"; }
1389        else { $temp = "Undefined!\n"; }
1390        printl $l, $temp;
1391
1392        $l = "Details for 100MHz Support";
1393        $temp = "";
1394        if ($bytes->[127] & 1) { $temp .= "Intel Concurrent Auto-precharge\n"; }
1395        if ($bytes->[127] & 2) { $temp .= "CAS Latency = 2\n"; }
1396        if ($bytes->[127] & 4) { $temp .= "CAS Latency = 3\n"; }
1397        if ($bytes->[127] & 8) { $temp .= "Junction Temp A (100 degrees C)\n"; }
1398        else { $temp .= "Junction Temp B (90 degrees C)\n"; }
1399        if ($bytes->[127] & 16) { $temp .= "CLK 3 Connected\n"; }
1400        if ($bytes->[127] & 32) { $temp .= "CLK 2 Connected\n"; }
1401        if ($bytes->[127] & 64) { $temp .= "CLK 1 Connected\n"; }
1402        if ($bytes->[127] & 128) { $temp .= "CLK 0 Connected\n"; }
1403        if (($bytes->[127] & 192) == 192) { $temp .= "Double-sided DIMM\n"; }
1404        elsif (($bytes->[127] & 192) != 0) { $temp .= "Single-sided DIMM\n"; }
1405        printl $l, $temp;
1406}
1407
1408# Read various hex dump style formats: hexdump, hexdump -C, i2cdump, eeprog
1409# note that normal 'hexdump' format on a little-endian system byte-swaps
1410# words, using hexdump -C is better.
1411sub read_hexdump($)
1412{
1413        my $addr = 0;
1414        my $repstart = 0;
1415        my @bytes;
1416        my $header = 1;
1417        my $word = 0;
1418
1419        # Look in the cache first
1420        return @{$hexdump_cache{$_[0]}} if exists $hexdump_cache{$_[0]};
1421
1422        open F, '<', $_[0] or die "Unable to open: $_[0]";
1423        while (<F>) {
1424                chomp;
1425                if (/^\*$/) {
1426                        $repstart = $addr;
1427                        next;
1428                }
1429                /^(?:0000 )?([a-f\d]{2,8}):?\s+((:?[a-f\d]{4}\s*){8}|(:?[a-f\d]{2}\s*){16})/i ||
1430                /^(?:0000 )?([a-f\d]{2,8}):?\s*$/i;
1431                next if (!defined $1 && $header);               # skip leading unparsed lines
1432
1433                defined $1 or die "Unable to parse input";
1434                $header = 0;
1435
1436                $addr = hex $1;
1437                if ($repstart) {
1438                        @bytes[$repstart .. ($addr-1)] =
1439                                (@bytes[($repstart-16)..($repstart-1)]) x (($addr-$repstart)/16);
1440                        $repstart = 0;
1441                }
1442                last unless defined $2;
1443                foreach (split(/\s+/, $2)) {
1444                        if (/^(..)(..)$/) {
1445                                $word |= 1;
1446                                if ($use_hexdump eq LITTLEENDIAN) {
1447                                        $bytes[$addr++] = hex($2);
1448                                        $bytes[$addr++] = hex($1);
1449                                } else {
1450                                        $bytes[$addr++] = hex($1);
1451                                        $bytes[$addr++] = hex($2);
1452                                }
1453                        } else {
1454                                $bytes[$addr++] = hex($_);
1455                        }
1456                }
1457        }
1458        close F;
1459        $header and die "Unable to parse any data from hexdump '$_[0]'";
1460        $word and printc "Using $use_hexdump 16-bit hex dump";
1461
1462        # Cache the data for later use
1463        $hexdump_cache{$_[0]} = \@bytes;
1464        return @bytes;
1465}
1466
1467# Returns the (total, used) number of bytes in the EEPROM,
1468# assuming it is a non-Rambus SPD EEPROM.
1469sub spd_sizes($)
1470{
1471        my $bytes = shift;
1472
1473        if ($bytes->[2] >= 9) {
1474                # For FB-DIMM and newer, decode number of bytes written
1475                my $spd_len = ($bytes->[0] >> 4) & 7;
1476                my $size = 64 << ($bytes->[0] & 15);
1477                if ($spd_len == 0) {
1478                        return ($size, 128);
1479                } elsif ($spd_len == 1) {
1480                        return ($size, 176);
1481                } elsif ($spd_len == 2) {
1482                        return ($size, 256);
1483                } else {
1484                        return (64, 64);
1485                }
1486        } else {
1487                my $size;
1488                if ($bytes->[1] <= 14) {
1489                        $size = 1 << $bytes->[1];
1490                } elsif ($bytes->[1] == 0) {
1491                        $size = "RFU";
1492                } else { $size = "ERROR!" }
1493
1494                return ($size, ($bytes->[0] < 64) ? 64 : $bytes->[0]);
1495        }
1496}
1497
1498# Read bytes from SPD-EEPROM
1499# Note: offset must be a multiple of 16!
1500sub readspd($$$)
1501{
1502        my ($offset, $size, $dimm_i) = @_;
1503        my @bytes;
1504        if ($use_hexdump) {
1505                @bytes = read_hexdump($dimm_i);
1506                return @bytes[$offset..($offset + $size - 1)];
1507        } elsif ($use_sysfs) {
1508                # Kernel 2.6 with sysfs
1509                sysopen(HANDLE, "/sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom", O_RDONLY)
1510                        or die "Cannot open /sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom";
1511                binmode HANDLE;
1512                sysseek(HANDLE, $offset, SEEK_SET);
1513                sysread(HANDLE, my $eeprom, $size);
1514                close HANDLE;
1515                @bytes = unpack("C*", $eeprom);
1516        } else {
1517                # Kernel 2.4 with procfs
1518                for my $i (0 .. ($size-1)/16) {
1519                        my $hexoff = sprintf('%02x', $offset + $i * 16);
1520                        push @bytes, split(" ", `cat /proc/sys/dev/sensors/$dimm_i/$hexoff`);
1521                }
1522        }
1523        return @bytes;
1524}
1525
1526# Calculate and verify checksum of first 63 bytes
1527sub checksum($)
1528{
1529        my $bytes = shift;
1530        my $dimm_checksum = 0;
1531        local $_;
1532
1533        $dimm_checksum += $bytes->[$_] foreach (0 .. 62);
1534        $dimm_checksum &= 0xff;
1535
1536        return ("EEPROM Checksum of bytes 0-62",
1537                ($bytes->[63] == $dimm_checksum) ? 1 : 0,
1538                sprintf('0x%02X', $bytes->[63]),
1539                sprintf('0x%02X', $dimm_checksum));
1540}
1541
1542# Calculate and verify CRC
1543sub check_crc($)
1544{
1545        my $bytes = shift;
1546        my $crc = 0;
1547        my $crc_cover = $bytes->[0] & 0x80 ? 116 : 125;
1548        my $crc_ptr = 0;
1549        my $crc_bit;
1550
1551        while ($crc_ptr <= $crc_cover) {
1552                $crc = $crc ^ ($bytes->[$crc_ptr] << 8);
1553                for ($crc_bit = 0; $crc_bit < 8; $crc_bit++) {
1554                        if ($crc & 0x8000) {
1555                                $crc = ($crc << 1) ^ 0x1021;
1556                        } else {
1557                                $crc = $crc << 1
1558                        }
1559                }
1560                $crc_ptr++;
1561        }
1562        $crc &= 0xffff;
1563
1564        my $dimm_crc = ($bytes->[127] << 8) | $bytes->[126];
1565        return ("EEPROM CRC of bytes 0-$crc_cover",
1566                ($dimm_crc == $crc) ? 1 : 0,
1567                sprintf("0x%04X", $dimm_crc),
1568                sprintf("0x%04X", $crc));
1569}
1570
1571# Parse command-line
1572foreach (@ARGV) {
1573        if ($_ eq '-h' || $_ eq '--help') {
1574                print "Usage: $0 [-c] [-f [-b]] [-x|-X file [files..]]\n",
1575                        "       $0 -h\n\n",
1576                        "  -f, --format            Print nice html output\n",
1577                        "  -b, --bodyonly          Don't print html header\n",
1578                        "                          (useful for postprocessing the output)\n",
1579                        "  -c, --checksum          Decode completely even if checksum fails\n",
1580                        "  -x,                     Read data from hexdump files\n",
1581                        "  -X,                     Same as -x except treat multibyte hex\n",
1582                        "                          data as little endian\n",
1583                        "  -h, --help              Display this usage summary\n";
1584                print <<"EOF";
1585
1586Hexdumps can be the output from hexdump, hexdump -C, i2cdump, eeprog and
1587likely many other progams producing hex dumps of one kind or another.  Note
1588that the default output of "hexdump" will be byte-swapped on little-endian
1589systems and you must use -X instead of -x, otherwise the dump will not be
1590parsed correctly.  It is better to use "hexdump -C", which is not ambiguous.
1591EOF
1592                exit;
1593        }
1594
1595        if ($_ eq '-f' || $_ eq '--format') {
1596                $opt_html = 1;
1597                next;
1598        }
1599        if ($_ eq '-b' || $_ eq '--bodyonly') {
1600                $opt_bodyonly = 1;
1601                next;
1602        }
1603        if ($_ eq '-c' || $_ eq '--checksum') {
1604                $opt_igncheck = 1;
1605                next;
1606        }
1607        if ($_ eq '-x') {
1608                $use_hexdump = BIGENDIAN;
1609                next;
1610        }
1611        if ($_ eq '-X') {
1612                $use_hexdump = LITTLEENDIAN;
1613                next;
1614        }
1615
1616        if (m/^-/) {
1617                print STDERR "Unrecognized option $_\n";
1618                exit;
1619        }
1620
1621        push @dimm_list, $_ if $use_hexdump;
1622}
1623
1624if ($opt_html && !$opt_bodyonly) {
1625        print "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
1626              "<html><head>\n",
1627                  "\t<meta HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">\n",
1628                  "\t<title>PC DIMM Serial Presence Detect Tester/Decoder Output</title>\n",
1629                  "</head><body>\n";
1630}
1631
1632printc "decode-dimms version $revision";
1633printh 'Memory Serial Presence Detect Decoder',
1634'By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner,
1635Jean Delvare, Trent Piepho and others';
1636
1637
1638my $dimm_count = 0;
1639my $dir;
1640if (!$use_hexdump) {
1641        if ($use_sysfs) { $dir = '/sys/bus/i2c/drivers/eeprom'; }
1642        else { $dir = '/proc/sys/dev/sensors'; }
1643        if (-d $dir) {
1644                @dimm_list = split(/\s+/, `ls $dir`);
1645        } elsif (! -d '/sys/module/eeprom') {
1646                print "No EEPROM found, are you sure the eeprom module is loaded?\n";
1647                exit;
1648        }
1649}
1650
1651for my $i ( 0 .. $#dimm_list ) {
1652        $_ = $dimm_list[$i];
1653        if (($use_sysfs && /^\d+-\d+$/)
1654         || (!$use_sysfs && /^eeprom-/)
1655         || $use_hexdump) {
1656                my @bytes = readspd(0, 128, $dimm_list[$i]);
1657                my $is_rambus = $bytes[0] < 4;          # Simple heuristic
1658                my ($l, $chk_valid, $chk_spd, $chk_calc);
1659                if ($is_rambus || $bytes[2] < 9) {
1660                        ($l, $chk_valid, $chk_spd, $chk_calc) =
1661                                checksum(\@bytes);
1662                } else {
1663                        ($l, $chk_valid, $chk_spd, $chk_calc) =
1664                                check_crc(\@bytes);
1665                }
1666
1667                next unless $chk_valid || $opt_igncheck;
1668                $dimm_count++;
1669
1670                print "<b><u>" if $opt_html;
1671                printl2 "\n\nDecoding EEPROM",
1672                        $use_hexdump ? $dimm_list[$i] : ($use_sysfs ?
1673                        "/sys/bus/i2c/drivers/eeprom/$dimm_list[$i]" :
1674                        "/proc/sys/dev/sensors/$dimm_list[$i]");
1675                print "</u></b>" if $opt_html;
1676                print "<table border=1>\n" if $opt_html;
1677                if (!$use_hexdump) {
1678                        if (($use_sysfs && /^[^-]+-([^-]+)$/)
1679                         || (!$use_sysfs && /^[^-]+-[^-]+-[^-]+-([^-]+)$/)) {
1680                                my $dimm_num = $1 - 49;
1681                                printl "Guessing DIMM is in", "bank $dimm_num";
1682                        }
1683                }
1684
1685# Decode first 3 bytes (0-2)
1686                prints "SPD EEPROM Information";
1687
1688                printl $l, ($chk_valid ?
1689                        sprintf("OK (%s)", $chk_calc) :
1690                        sprintf("Bad\n(found %s, calculated %s)\n",
1691                                $chk_spd, $chk_calc));
1692
1693                my $temp;
1694                if ($is_rambus) {
1695                        if ($bytes[0] == 1) { $temp = "0.7"; }
1696                        elsif ($bytes[0] == 2) { $temp = "1.0"; }
1697                        elsif ($bytes[0] == 0) { $temp = "Invalid"; }
1698                        else { $temp = "Reserved"; }
1699                        printl "SPD Revision", $temp;
1700                } else {
1701                        my ($spd_size, $spd_used) = spd_sizes(\@bytes);
1702                        printl "# of bytes written to SDRAM EEPROM", $spd_used;
1703                        printl "Total number of bytes in EEPROM", $spd_size;
1704
1705                        # If there's more data than what we've read, let's
1706                        # read it now.  DDR3 will need this data.
1707                        if ($spd_used > @bytes) {
1708                                push (@bytes,
1709                                      readspd(@bytes, $spd_used - @bytes,
1710                                              $dimm_list[$i]));
1711                        }
1712                }
1713
1714                $l = "Fundamental Memory type";
1715                my $type = sprintf("Unknown (0x%02x)", $bytes[2]);
1716                if ($is_rambus) {
1717                        if ($bytes[2] == 1) { $type = "Direct Rambus"; }
1718                        elsif ($bytes[2] == 17) { $type = "Rambus"; }
1719                } else {
1720                        my @type_list = (
1721                                "Reserved", "FPM DRAM",         # 0, 1
1722                                "EDO", "Pipelined Nibble",      # 2, 3
1723                                "SDR SDRAM", "Multiplexed ROM", # 4, 5
1724                                "DDR SGRAM", "DDR SDRAM",       # 6, 7
1725                                "DDR2 SDRAM", "FB-DIMM",        # 8, 9
1726                                "FB-DIMM Probe", "DDR3 SDRAM",  # 10, 11
1727                        );
1728                        if ($bytes[2] < @type_list) {
1729                                $type = $type_list[$bytes[2]];
1730                        }
1731                }
1732                printl $l, $type;
1733
1734# Decode next 61 bytes (3-63, depend on memory type)
1735                $decode_callback{$type}->(\@bytes)
1736                        if exists $decode_callback{$type};
1737
1738                if ($type eq "DDR3 SDRAM") {
1739                        # Decode DDR3-specific manufacturing data in bytes
1740                        # 117-149
1741                        decode_ddr3_mfg_data(\@bytes)
1742                } else {
1743                        # Decode next 35 bytes (64-98, common to most
1744                        # memory types)
1745                        decode_manufacturing_information(\@bytes);
1746                }
1747
1748# Next 27 bytes (99-125) are manufacturer specific, can't decode
1749
1750# Last 2 bytes (126-127) are reserved, Intel used them as an extension
1751                if ($type eq "SDR SDRAM") {
1752                        decode_intel_spec_freq(\@bytes);
1753                }
1754
1755                print "</table>\n" if $opt_html;
1756        }
1757}
1758printl2 "\n\nNumber of SDRAM DIMMs detected and decoded", $dimm_count;
1759
1760print "</body></html>\n" if ($opt_html && !$opt_bodyonly);
Note: See TracBrowser for help on using the browser.