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

Revision 5554, 57.8 KB (checked in by khali, 4 years ago)

Clean-up manufacturing data decoding functions.

  • 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",
1282                                $year >= 0x80 ? 19 : 20, $year, $week);
1283        } else {
1284                return sprintf("0x%02X%02X", $year, $week);
1285        }
1286}
1287
1288sub printl_mfg_location_code($)
1289{
1290        my $code = shift;
1291        my $letter = chr($code);
1292
1293        return unless spd_written($code);
1294        # Try the location code as ASCII first, as earlier specifications
1295        # suggested this. As newer specifications don't mention it anymore,
1296        # we still fall back to binary.
1297        printl("Manufacturing Location Code",
1298               $letter =~ m/^[\w\d]$/ ? $letter : sprintf("0x%.2X", $code));
1299}
1300
1301sub printl_mfg_assembly_serial(@)
1302{
1303        return unless spd_written(@_);
1304        printl("Assembly Serial Number", sprintf("0x%02X%02X%02X%02X", @_));
1305}
1306
1307# Parameter: EEPROM bytes 0-175 (using 117-149)
1308sub decode_ddr3_mfg_data($)
1309{
1310        my $bytes = shift;
1311
1312        prints("Manufacturer Data");
1313
1314        printl("Module Manufacturer",
1315               manufacturer_ddr3($bytes->[117], $bytes->[118]));
1316
1317        if (spd_written(@{$bytes}[148..149])) {
1318                printl("DRAM Manufacturer",
1319                       manufacturer_ddr3($bytes->[148], $bytes->[149]));
1320        }
1321
1322        printl_mfg_location_code($bytes->[119]);
1323
1324        if (spd_written(@{$bytes}[120..121])) {
1325                printl("Manufacturing Date",
1326                       manufacture_date($bytes->[120], $bytes->[121]));
1327        }
1328
1329        printl_mfg_assembly_serial(@{$bytes}[122..125]);
1330
1331        printl("Part Number", part_number(@{$bytes}[128..145]));
1332
1333        if (spd_written(@{$bytes}[146..147])) {
1334                printl("Revision Code",
1335                       sprintf("0x%02X%02X", $bytes->[146], $bytes->[147]));
1336        }
1337}
1338
1339# Parameter: EEPROM bytes 0-127 (using 64-98)
1340sub decode_manufacturing_information($)
1341{
1342        my $bytes = shift;
1343        my ($temp, $extra);
1344
1345        prints("Manufacturing Information");
1346
1347        # $extra is a reference to an array containing up to
1348        # 7 extra bytes from the Manufacturer field. Sometimes
1349        # these bytes are filled with interesting data.
1350        ($temp, $extra) = manufacturer(@{$bytes}[64..71]);
1351        printl("Manufacturer", $temp);
1352        $temp = manufacturer_data(@{$extra});
1353        printl("Custom Manufacturer Data", $temp) if defined $temp;
1354
1355        printl_mfg_location_code($bytes->[72]);
1356
1357        printl("Part Number", part_number(@{$bytes}[73..90]));
1358
1359        if (spd_written(@{$bytes}[91..92])) {
1360                printl("Revision Code",
1361                       sprintf("0x%02X%02X", @{$bytes}[91..92]));
1362        }
1363
1364        if (spd_written(@{$bytes}[93..94])) {
1365                printl("Manufacturing Date",
1366                       manufacture_date($bytes->[93], $bytes->[94]));
1367        }
1368
1369        printl_mfg_assembly_serial(@{$bytes}[95..98]);
1370}
1371
1372# Parameter: EEPROM bytes 0-127 (using 126-127)
1373sub decode_intel_spec_freq($)
1374{
1375        my $bytes = shift;
1376        my ($l, $temp);
1377
1378        prints "Intel Specification";
1379
1380        $l = "Frequency";
1381        if ($bytes->[126] == 0x66) { $temp = "66MHz\n"; }
1382        elsif ($bytes->[126] == 100) { $temp = "100MHz or 133MHz\n"; }
1383        elsif ($bytes->[126] == 133) { $temp = "133MHz\n"; }
1384        else { $temp = "Undefined!\n"; }
1385        printl $l, $temp;
1386
1387        $l = "Details for 100MHz Support";
1388        $temp = "";
1389        if ($bytes->[127] & 1) { $temp .= "Intel Concurrent Auto-precharge\n"; }
1390        if ($bytes->[127] & 2) { $temp .= "CAS Latency = 2\n"; }
1391        if ($bytes->[127] & 4) { $temp .= "CAS Latency = 3\n"; }
1392        if ($bytes->[127] & 8) { $temp .= "Junction Temp A (100 degrees C)\n"; }
1393        else { $temp .= "Junction Temp B (90 degrees C)\n"; }
1394        if ($bytes->[127] & 16) { $temp .= "CLK 3 Connected\n"; }
1395        if ($bytes->[127] & 32) { $temp .= "CLK 2 Connected\n"; }
1396        if ($bytes->[127] & 64) { $temp .= "CLK 1 Connected\n"; }
1397        if ($bytes->[127] & 128) { $temp .= "CLK 0 Connected\n"; }
1398        if (($bytes->[127] & 192) == 192) { $temp .= "Double-sided DIMM\n"; }
1399        elsif (($bytes->[127] & 192) != 0) { $temp .= "Single-sided DIMM\n"; }
1400        printl $l, $temp;
1401}
1402
1403# Read various hex dump style formats: hexdump, hexdump -C, i2cdump, eeprog
1404# note that normal 'hexdump' format on a little-endian system byte-swaps
1405# words, using hexdump -C is better.
1406sub read_hexdump($)
1407{
1408        my $addr = 0;
1409        my $repstart = 0;
1410        my @bytes;
1411        my $header = 1;
1412        my $word = 0;
1413
1414        # Look in the cache first
1415        return @{$hexdump_cache{$_[0]}} if exists $hexdump_cache{$_[0]};
1416
1417        open F, '<', $_[0] or die "Unable to open: $_[0]";
1418        while (<F>) {
1419                chomp;
1420                if (/^\*$/) {
1421                        $repstart = $addr;
1422                        next;
1423                }
1424                /^(?:0000 )?([a-f\d]{2,8}):?\s+((:?[a-f\d]{4}\s*){8}|(:?[a-f\d]{2}\s*){16})/i ||
1425                /^(?:0000 )?([a-f\d]{2,8}):?\s*$/i;
1426                next if (!defined $1 && $header);               # skip leading unparsed lines
1427
1428                defined $1 or die "Unable to parse input";
1429                $header = 0;
1430
1431                $addr = hex $1;
1432                if ($repstart) {
1433                        @bytes[$repstart .. ($addr-1)] =
1434                                (@bytes[($repstart-16)..($repstart-1)]) x (($addr-$repstart)/16);
1435                        $repstart = 0;
1436                }
1437                last unless defined $2;
1438                foreach (split(/\s+/, $2)) {
1439                        if (/^(..)(..)$/) {
1440                                $word |= 1;
1441                                if ($use_hexdump eq LITTLEENDIAN) {
1442                                        $bytes[$addr++] = hex($2);
1443                                        $bytes[$addr++] = hex($1);
1444                                } else {
1445                                        $bytes[$addr++] = hex($1);
1446                                        $bytes[$addr++] = hex($2);
1447                                }
1448                        } else {
1449                                $bytes[$addr++] = hex($_);
1450                        }
1451                }
1452        }
1453        close F;
1454        $header and die "Unable to parse any data from hexdump '$_[0]'";
1455        $word and printc "Using $use_hexdump 16-bit hex dump";
1456
1457        # Cache the data for later use
1458        $hexdump_cache{$_[0]} = \@bytes;
1459        return @bytes;
1460}
1461
1462# Returns the (total, used) number of bytes in the EEPROM,
1463# assuming it is a non-Rambus SPD EEPROM.
1464sub spd_sizes($)
1465{
1466        my $bytes = shift;
1467
1468        if ($bytes->[2] >= 9) {
1469                # For FB-DIMM and newer, decode number of bytes written
1470                my $spd_len = ($bytes->[0] >> 4) & 7;
1471                my $size = 64 << ($bytes->[0] & 15);
1472                if ($spd_len == 0) {
1473                        return ($size, 128);
1474                } elsif ($spd_len == 1) {
1475                        return ($size, 176);
1476                } elsif ($spd_len == 2) {
1477                        return ($size, 256);
1478                } else {
1479                        return (64, 64);
1480                }
1481        } else {
1482                my $size;
1483                if ($bytes->[1] <= 14) {
1484                        $size = 1 << $bytes->[1];
1485                } elsif ($bytes->[1] == 0) {
1486                        $size = "RFU";
1487                } else { $size = "ERROR!" }
1488
1489                return ($size, ($bytes->[0] < 64) ? 64 : $bytes->[0]);
1490        }
1491}
1492
1493# Read bytes from SPD-EEPROM
1494# Note: offset must be a multiple of 16!
1495sub readspd($$$)
1496{
1497        my ($offset, $size, $dimm_i) = @_;
1498        my @bytes;
1499        if ($use_hexdump) {
1500                @bytes = read_hexdump($dimm_i);
1501                return @bytes[$offset..($offset + $size - 1)];
1502        } elsif ($use_sysfs) {
1503                # Kernel 2.6 with sysfs
1504                sysopen(HANDLE, "/sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom", O_RDONLY)
1505                        or die "Cannot open /sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom";
1506                binmode HANDLE;
1507                sysseek(HANDLE, $offset, SEEK_SET);
1508                sysread(HANDLE, my $eeprom, $size);
1509                close HANDLE;
1510                @bytes = unpack("C*", $eeprom);
1511        } else {
1512                # Kernel 2.4 with procfs
1513                for my $i (0 .. ($size-1)/16) {
1514                        my $hexoff = sprintf('%02x', $offset + $i * 16);
1515                        push @bytes, split(" ", `cat /proc/sys/dev/sensors/$dimm_i/$hexoff`);
1516                }
1517        }
1518        return @bytes;
1519}
1520
1521# Calculate and verify checksum of first 63 bytes
1522sub checksum($)
1523{
1524        my $bytes = shift;
1525        my $dimm_checksum = 0;
1526        local $_;
1527
1528        $dimm_checksum += $bytes->[$_] foreach (0 .. 62);
1529        $dimm_checksum &= 0xff;
1530
1531        return ("EEPROM Checksum of bytes 0-62",
1532                ($bytes->[63] == $dimm_checksum) ? 1 : 0,
1533                sprintf('0x%02X', $bytes->[63]),
1534                sprintf('0x%02X', $dimm_checksum));
1535}
1536
1537# Calculate and verify CRC
1538sub check_crc($)
1539{
1540        my $bytes = shift;
1541        my $crc = 0;
1542        my $crc_cover = $bytes->[0] & 0x80 ? 116 : 125;
1543        my $crc_ptr = 0;
1544        my $crc_bit;
1545
1546        while ($crc_ptr <= $crc_cover) {
1547                $crc = $crc ^ ($bytes->[$crc_ptr] << 8);
1548                for ($crc_bit = 0; $crc_bit < 8; $crc_bit++) {
1549                        if ($crc & 0x8000) {
1550                                $crc = ($crc << 1) ^ 0x1021;
1551                        } else {
1552                                $crc = $crc << 1
1553                        }
1554                }
1555                $crc_ptr++;
1556        }
1557        $crc &= 0xffff;
1558
1559        my $dimm_crc = ($bytes->[127] << 8) | $bytes->[126];
1560        return ("EEPROM CRC of bytes 0-$crc_cover",
1561                ($dimm_crc == $crc) ? 1 : 0,
1562                sprintf("0x%04X", $dimm_crc),
1563                sprintf("0x%04X", $crc));
1564}
1565
1566# Parse command-line
1567foreach (@ARGV) {
1568        if ($_ eq '-h' || $_ eq '--help') {
1569                print "Usage: $0 [-c] [-f [-b]] [-x|-X file [files..]]\n",
1570                        "       $0 -h\n\n",
1571                        "  -f, --format            Print nice html output\n",
1572                        "  -b, --bodyonly          Don't print html header\n",
1573                        "                          (useful for postprocessing the output)\n",
1574                        "  -c, --checksum          Decode completely even if checksum fails\n",
1575                        "  -x,                     Read data from hexdump files\n",
1576                        "  -X,                     Same as -x except treat multibyte hex\n",
1577                        "                          data as little endian\n",
1578                        "  -h, --help              Display this usage summary\n";
1579                print <<"EOF";
1580
1581Hexdumps can be the output from hexdump, hexdump -C, i2cdump, eeprog and
1582likely many other progams producing hex dumps of one kind or another.  Note
1583that the default output of "hexdump" will be byte-swapped on little-endian
1584systems and you must use -X instead of -x, otherwise the dump will not be
1585parsed correctly.  It is better to use "hexdump -C", which is not ambiguous.
1586EOF
1587                exit;
1588        }
1589
1590        if ($_ eq '-f' || $_ eq '--format') {
1591                $opt_html = 1;
1592                next;
1593        }
1594        if ($_ eq '-b' || $_ eq '--bodyonly') {
1595                $opt_bodyonly = 1;
1596                next;
1597        }
1598        if ($_ eq '-c' || $_ eq '--checksum') {
1599                $opt_igncheck = 1;
1600                next;
1601        }
1602        if ($_ eq '-x') {
1603                $use_hexdump = BIGENDIAN;
1604                next;
1605        }
1606        if ($_ eq '-X') {
1607                $use_hexdump = LITTLEENDIAN;
1608                next;
1609        }
1610
1611        if (m/^-/) {
1612                print STDERR "Unrecognized option $_\n";
1613                exit;
1614        }
1615
1616        push @dimm_list, $_ if $use_hexdump;
1617}
1618
1619if ($opt_html && !$opt_bodyonly) {
1620        print "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
1621              "<html><head>\n",
1622                  "\t<meta HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">\n",
1623                  "\t<title>PC DIMM Serial Presence Detect Tester/Decoder Output</title>\n",
1624                  "</head><body>\n";
1625}
1626
1627printc "decode-dimms version $revision";
1628printh 'Memory Serial Presence Detect Decoder',
1629'By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner,
1630Jean Delvare, Trent Piepho and others';
1631
1632
1633my $dimm_count = 0;
1634my $dir;
1635if (!$use_hexdump) {
1636        if ($use_sysfs) { $dir = '/sys/bus/i2c/drivers/eeprom'; }
1637        else { $dir = '/proc/sys/dev/sensors'; }
1638        if (-d $dir) {
1639                @dimm_list = split(/\s+/, `ls $dir`);
1640        } elsif (! -d '/sys/module/eeprom') {
1641                print "No EEPROM found, are you sure the eeprom module is loaded?\n";
1642                exit;
1643        }
1644}
1645
1646for my $i ( 0 .. $#dimm_list ) {
1647        $_ = $dimm_list[$i];
1648        if (($use_sysfs && /^\d+-\d+$/)
1649         || (!$use_sysfs && /^eeprom-/)
1650         || $use_hexdump) {
1651                my @bytes = readspd(0, 128, $dimm_list[$i]);
1652                my $is_rambus = $bytes[0] < 4;          # Simple heuristic
1653                my ($l, $chk_valid, $chk_spd, $chk_calc);
1654                if ($is_rambus || $bytes[2] < 9) {
1655                        ($l, $chk_valid, $chk_spd, $chk_calc) =
1656                                checksum(\@bytes);
1657                } else {
1658                        ($l, $chk_valid, $chk_spd, $chk_calc) =
1659                                check_crc(\@bytes);
1660                }
1661
1662                next unless $chk_valid || $opt_igncheck;
1663                $dimm_count++;
1664
1665                print "<b><u>" if $opt_html;
1666                printl2 "\n\nDecoding EEPROM",
1667                        $use_hexdump ? $dimm_list[$i] : ($use_sysfs ?
1668                        "/sys/bus/i2c/drivers/eeprom/$dimm_list[$i]" :
1669                        "/proc/sys/dev/sensors/$dimm_list[$i]");
1670                print "</u></b>" if $opt_html;
1671                print "<table border=1>\n" if $opt_html;
1672                if (!$use_hexdump) {
1673                        if (($use_sysfs && /^[^-]+-([^-]+)$/)
1674                         || (!$use_sysfs && /^[^-]+-[^-]+-[^-]+-([^-]+)$/)) {
1675                                my $dimm_num = $1 - 49;
1676                                printl "Guessing DIMM is in", "bank $dimm_num";
1677                        }
1678                }
1679
1680# Decode first 3 bytes (0-2)
1681                prints "SPD EEPROM Information";
1682
1683                printl $l, ($chk_valid ?
1684                        sprintf("OK (%s)", $chk_calc) :
1685                        sprintf("Bad\n(found %s, calculated %s)\n",
1686                                $chk_spd, $chk_calc));
1687
1688                my $temp;
1689                if ($is_rambus) {
1690                        if ($bytes[0] == 1) { $temp = "0.7"; }
1691                        elsif ($bytes[0] == 2) { $temp = "1.0"; }
1692                        elsif ($bytes[0] == 0) { $temp = "Invalid"; }
1693                        else { $temp = "Reserved"; }
1694                        printl "SPD Revision", $temp;
1695                } else {
1696                        my ($spd_size, $spd_used) = spd_sizes(\@bytes);
1697                        printl "# of bytes written to SDRAM EEPROM", $spd_used;
1698                        printl "Total number of bytes in EEPROM", $spd_size;
1699
1700                        # If there's more data than what we've read, let's
1701                        # read it now.  DDR3 will need this data.
1702                        if ($spd_used > @bytes) {
1703                                push (@bytes,
1704                                      readspd(@bytes, $spd_used - @bytes,
1705                                              $dimm_list[$i]));
1706                        }
1707                }
1708
1709                $l = "Fundamental Memory type";
1710                my $type = sprintf("Unknown (0x%02x)", $bytes[2]);
1711                if ($is_rambus) {
1712                        if ($bytes[2] == 1) { $type = "Direct Rambus"; }
1713                        elsif ($bytes[2] == 17) { $type = "Rambus"; }
1714                } else {
1715                        my @type_list = (
1716                                "Reserved", "FPM DRAM",         # 0, 1
1717                                "EDO", "Pipelined Nibble",      # 2, 3
1718                                "SDR SDRAM", "Multiplexed ROM", # 4, 5
1719                                "DDR SGRAM", "DDR SDRAM",       # 6, 7
1720                                "DDR2 SDRAM", "FB-DIMM",        # 8, 9
1721                                "FB-DIMM Probe", "DDR3 SDRAM",  # 10, 11
1722                        );
1723                        if ($bytes[2] < @type_list) {
1724                                $type = $type_list[$bytes[2]];
1725                        }
1726                }
1727                printl $l, $type;
1728
1729# Decode next 61 bytes (3-63, depend on memory type)
1730                $decode_callback{$type}->(\@bytes)
1731                        if exists $decode_callback{$type};
1732
1733                if ($type eq "DDR3 SDRAM") {
1734                        # Decode DDR3-specific manufacturing data in bytes
1735                        # 117-149
1736                        decode_ddr3_mfg_data(\@bytes)
1737                } else {
1738                        # Decode next 35 bytes (64-98, common to most
1739                        # memory types)
1740                        decode_manufacturing_information(\@bytes);
1741                }
1742
1743# Next 27 bytes (99-125) are manufacturer specific, can't decode
1744
1745# Last 2 bytes (126-127) are reserved, Intel used them as an extension
1746                if ($type eq "SDR SDRAM") {
1747                        decode_intel_spec_freq(\@bytes);
1748                }
1749
1750                print "</table>\n" if $opt_html;
1751        }
1752}
1753printl2 "\n\nNumber of SDRAM DIMMs detected and decoded", $dimm_count;
1754
1755print "</body></html>\n" if ($opt_html && !$opt_bodyonly);
Note: See TracBrowser for help on using the browser.