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

Revision 5545, 50.6 KB (checked in by khali, 5 years ago)

One more Rambus fix.

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