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

Revision 5548, 51.3 KB (checked in by khali, 4 years ago)

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