root/i2c-tools/trunk/eeprom/decode-dimms.pl @ 5150

Revision 5150, 43.3 KB (checked in by khali, 5 years ago)

Hard-code program name in header comment.

  • 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., 675 Mass Ave, Cambridge, MA 02139, USA.
23#
24#
25# The eeprom driver must be loaded. For kernels older than 2.6.0, the
26# eeprom driver can be found in the lm-sensors package.
27#
28# use the following command line switches
29#  -f, --format            print nice html output
30#  -b, --bodyonly          don't print html header
31#                          (useful for postprocessing the output)
32#  -c, --checksum          decode completely even if checksum fails
33#  -h, --help              display this usage summary
34#
35# References:
36# PC SDRAM Serial Presence
37# Detect (SPD) Specification, Intel,
38# 1997,1999, Rev 1.2B
39#
40# Jedec Standards 4.1.x & 4.5.x
41# http://www.jedec.org
42#
43
44require 5.004;
45
46use strict;
47use POSIX;
48use Fcntl qw(:DEFAULT :seek);
49use vars qw($opt_html $opt_body $opt_bodyonly $opt_igncheck $use_sysfs
50            @vendors %decode_callback $revision);
51
52$revision = '$Revision$ ($Date$)';
53$revision =~ s/\$\w+: (.*?) \$/$1/g;
54$revision =~ s/ \([^()]*\)//;
55
56@vendors = (
57["AMD", "AMI", "Fairchild", "Fujitsu",
58 "GTE", "Harris", "Hitachi", "Inmos",
59 "Intel", "I.T.T.", "Intersil", "Monolithic Memories",
60 "Mostek", "Freescale (former Motorola)", "National", "NEC",
61 "RCA", "Raytheon", "Conexant (Rockwell)", "Seeq",
62 "NXP (former Signetics, Philips Semi.)", "Synertek", "Texas Instruments", "Toshiba",
63 "Xicor", "Zilog", "Eurotechnique", "Mitsubishi",
64 "Lucent (AT&T)", "Exel", "Atmel", "SGS/Thomson",
65 "Lattice Semi.", "NCR", "Wafer Scale Integration", "IBM",
66 "Tristar", "Visic", "Intl. CMOS Technology", "SSSI",
67 "MicrochipTechnology", "Ricoh Ltd.", "VLSI", "Micron Technology",
68 "Hyundai Electronics", "OKI Semiconductor", "ACTEL", "Sharp",
69 "Catalyst", "Panasonic", "IDT", "Cypress",
70 "DEC", "LSI Logic", "Zarlink (former Plessey)", "UTMC",
71 "Thinking Machine", "Thomson CSF", "Integrated CMOS (Vertex)", "Honeywell",
72 "Tektronix", "Sun Microsystems", "SST", "ProMos/Mosel Vitelic",
73 "Infineon (former Siemens)", "Macronix", "Xerox", "Plus Logic",
74 "SunDisk", "Elan Circuit Tech.", "European Silicon Str.", "Apple Computer",
75 "Xilinx", "Compaq", "Protocol Engines", "SCI",
76 "Seiko Instruments", "Samsung", "I3 Design System", "Klic",
77 "Crosspoint Solutions", "Alliance Semiconductor", "Tandem", "Hewlett-Packard",
78 "Intg. Silicon Solutions", "Brooktree", "New Media", "MHS Electronic",
79 "Performance Semi.", "Winbond Electronic", "Kawasaki Steel", "Bright Micro",
80 "TECMAR", "Exar", "PCMCIA", "LG Semi (former Goldstar)",
81 "Northern Telecom", "Sanyo", "Array Microsystems", "Crystal Semiconductor",
82 "Analog Devices", "PMC-Sierra", "Asparix", "Convex Computer",
83 "Quality Semiconductor", "Nimbus Technology", "Transwitch", "Micronas (ITT Intermetall)",
84 "Cannon", "Altera", "NEXCOM", "QUALCOMM",
85 "Sony", "Cray Research", "AMS(Austria Micro)", "Vitesse",
86 "Aster Electronics", "Bay Networks (Synoptic)", "Zentrum or ZMD", "TRW",
87 "Thesys", "Solbourne Computer", "Allied-Signal", "Dialog",
88 "Media Vision", "Level One Communication"],
89["Cirrus Logic", "National Instruments", "ILC Data Device", "Alcatel Mietec",
90 "Micro Linear", "Univ. of NC", "JTAG Technologies", "BAE Systems",
91 "Nchip", "Galileo Tech", "Bestlink Systems", "Graychip",
92 "GENNUM", "VideoLogic", "Robert Bosch", "Chip Express",
93 "DATARAM", "United Microelec Corp.", "TCSI", "Smart Modular",
94 "Hughes Aircraft", "Lanstar Semiconductor", "Qlogic", "Kingston",
95 "Music Semi", "Ericsson Components", "SpaSE", "Eon Silicon Devices",
96 "Programmable Micro Corp", "DoD", "Integ. Memories Tech.", "Corollary Inc.",
97 "Dallas Semiconductor", "Omnivision", "EIV(Switzerland)", "Novatel Wireless",
98 "Zarlink (former Mitel)", "Clearpoint", "Cabletron", "STEC (former Silicon Technology)",
99 "Vanguard", "Hagiwara Sys-Com", "Vantis", "Celestica",
100 "Century", "Hal Computers", "Rohm Company Ltd.", "Juniper Networks",
101 "Libit Signal Processing", "Mushkin Enhanced Memory", "Tundra Semiconductor", "Adaptec Inc.",
102 "LightSpeed Semi.", "ZSP Corp.", "AMIC Technology", "Adobe Systems",
103 "Dynachip", "PNY Electronics", "Newport Digital", "MMC Networks",
104 "T Square", "Seiko Epson", "Broadcom", "Viking Components",
105 "V3 Semiconductor", "Flextronics (former Orbit)", "Suwa Electronics", "Transmeta",
106 "Micron CMS", "American Computer & Digital Components Inc", "Enhance 3000 Inc", "Tower Semiconductor",
107 "CPU Design", "Price Point", "Maxim Integrated Product", "Tellabs",
108 "Centaur Technology", "Unigen Corporation", "Transcend Information", "Memory Card Technology",
109 "CKD Corporation Ltd.", "Capital Instruments, Inc.", "Aica Kogyo, Ltd.", "Linvex Technology",
110 "MSC Vertriebs GmbH", "AKM Company, Ltd.", "Dynamem, Inc.", "NERA ASA",
111 "GSI Technology", "Dane-Elec (C Memory)", "Acorn Computers", "Lara Technology",
112 "Oak Technology, Inc.", "Itec Memory", "Tanisys Technology", "Truevision",
113 "Wintec Industries", "Super PC Memory", "MGV Memory", "Galvantech",
114 "Gadzoox Nteworks", "Multi Dimensional Cons.", "GateField", "Integrated Memory System",
115 "Triscend", "XaQti", "Goldenram", "Clear Logic",
116 "Cimaron Communications", "Nippon Steel Semi. Corp.", "Advantage Memory", "AMCC",
117 "LeCroy", "Yamaha Corporation", "Digital Microwave", "NetLogic Microsystems",
118 "MIMOS Semiconductor", "Advanced Fibre", "BF Goodrich Data.", "Epigram",
119 "Acbel Polytech Inc.", "Apacer Technology", "Admor Memory", "FOXCONN",
120 "Quadratics Superconductor", "3COM"],
121["Camintonn Corporation", "ISOA Incorporated", "Agate Semiconductor", "ADMtek Incorporated",
122 "HYPERTEC", "Adhoc Technologies", "MOSAID Technologies", "Ardent Technologies",
123 "Switchcore", "Cisco Systems, Inc.", "Allayer Technologies", "WorkX AG",
124 "Oasis Semiconductor", "Novanet Semiconductor", "E-M Solutions", "Power General",
125 "Advanced Hardware Arch.", "Inova Semiconductors GmbH", "Telocity", "Delkin Devices",
126 "Symagery Microsystems", "C-Port Corporation", "SiberCore Technologies", "Southland Microsystems",
127 "Malleable Technologies", "Kendin Communications", "Great Technology Microcomputer", "Sanmina Corporation",
128 "HADCO Corporation", "Corsair", "Actrans System Inc.", "ALPHA Technologies",
129 "Silicon Laboratories, Inc. (Cygnal)", "Artesyn Technologies", "Align Manufacturing", "Peregrine Semiconductor",
130 "Chameleon Systems", "Aplus Flash Technology", "MIPS Technologies", "Chrysalis ITS",
131 "ADTEC Corporation", "Kentron Technologies", "Win Technologies", "Tachyon Semiconductor (former ASIC Designs Inc.)",
132 "Extreme Packet Devices", "RF Micro Devices", "Siemens AG", "Sarnoff Corporation",
133 "Itautec Philco SA", "Radiata Inc.", "Benchmark Elect. (AVEX)", "Legend",
134 "SpecTek Incorporated", "Hi/fn", "Enikia Incorporated", "SwitchOn Networks",
135 "AANetcom Incorporated", "Micro Memory Bank", "ESS Technology", "Virata Corporation",
136 "Excess Bandwidth", "West Bay Semiconductor", "DSP Group", "Newport Communications",
137 "Chip2Chip Incorporated", "Phobos Corporation", "Intellitech Corporation", "Nordic VLSI ASA",
138 "Ishoni Networks", "Silicon Spice", "Alchemy Semiconductor", "Agilent Technologies",
139 "Centillium Communications", "W.L. Gore", "HanBit Electronics", "GlobeSpan",
140 "Element 14", "Pycon", "Saifun Semiconductors", "Sibyte, Incorporated",
141 "MetaLink Technologies", "Feiya Technology", "I & C Technology", "Shikatronics",
142 "Elektrobit", "Megic", "Com-Tier", "Malaysia Micro Solutions",
143 "Hyperchip", "Gemstone Communications", "Anadigm (former Anadyne)", "3ParData",
144 "Mellanox Technologies", "Tenx Technologies", "Helix AG", "Domosys",
145 "Skyup Technology", "HiNT Corporation", "Chiaro", "MDT Technologies GmbH (former MCI Computer GMBH)",
146 "Exbit Technology A/S", "Integrated Technology Express", "AVED Memory", "Legerity",
147 "Jasmine Networks", "Caspian Networks", "nCUBE", "Silicon Access Networks",
148 "FDK Corporation", "High Bandwidth Access", "MultiLink Technology", "BRECIS",
149 "World Wide Packets", "APW", "Chicory Systems", "Xstream Logic",
150 "Fast-Chip", "Zucotto Wireless", "Realchip", "Galaxy Power",
151 "eSilicon", "Morphics Technology", "Accelerant Networks", "Silicon Wave",
152 "SandCraft", "Elpida"],
153["Solectron", "Optosys Technologies", "Buffalo (former Melco)", "TriMedia Technologies",
154 "Cyan Technologies", "Global Locate", "Optillion", "Terago Communications",
155 "Ikanos Communications", "Princeton Technology", "Nanya Technology", "Elite Flash Storage",
156 "Mysticom", "LightSand Communications", "ATI Technologies", "Agere Systems",
157 "NeoMagic", "AuroraNetics", "Golden Empire", "Mushkin",
158 "Tioga Technologies", "Netlist", "TeraLogic", "Cicada Semiconductor",
159 "Centon Electronics", "Tyco Electronics", "Magis Works", "Zettacom",
160 "Cogency Semiconductor", "Chipcon AS", "Aspex Technology", "F5 Networks",
161 "Programmable Silicon Solutions", "ChipWrights", "Acorn Networks", "Quicklogic",
162 "Kingmax Semiconductor", "BOPS", "Flasys", "BitBlitz Communications",
163 "eMemory Technology", "Procket Networks", "Purple Ray", "Trebia Networks",
164 "Delta Electronics", "Onex Communications", "Ample Communications", "Memory Experts Intl",
165 "Astute Networks", "Azanda Network Devices", "Dibcom", "Tekmos",
166 "API NetWorks", "Bay Microsystems", "Firecron Ltd", "Resonext Communications",
167 "Tachys Technologies", "Equator Technology", "Concept Computer", "SILCOM",
168 "3Dlabs", "c't Magazine", "Sanera Systems", "Silicon Packets",
169 "Viasystems Group", "Simtek", "Semicon Devices Singapore", "Satron Handelsges",
170 "Improv Systems", "INDUSYS GmbH", "Corrent", "Infrant Technologies",
171 "Ritek Corp", "empowerTel Networks", "Hypertec", "Cavium Networks",
172 "PLX Technology", "Massana Design", "Intrinsity", "Valence Semiconductor",
173 "Terawave Communications", "IceFyre Semiconductor", "Primarion", "Picochip Designs Ltd",
174 "Silverback Systems", "Jade Star Technologies", "Pijnenburg Securealink",
175 "TakeMS International AG", "Cambridge Silicon Radio",
176 "Swissbit", "Nazomi Communications", "eWave System",
177 "Rockwell Collins", "Picocel Co., Ltd.", "Alphamosaic Ltd", "Sandburst",
178 "SiCon Video", "NanoAmp Solutions", "Ericsson Technology", "PrairieComm",
179 "Mitac International", "Layer N Networks", "MtekVision", "Allegro Networks",
180 "Marvell Semiconductors", "Netergy Microelectronic", "NVIDIA", "Internet Machines",
181 "Peak Electronics", "Litchfield Communication", "Accton Technology", "Teradiant Networks",
182 "Europe Technologies", "Cortina Systems", "RAM Components", "Raqia Networks",
183 "ClearSpeed", "Matsushita Battery", "Xelerated", "SimpleTech",
184 "Utron Technology", "Astec International", "AVM gmbH", "Redux Communications",
185 "Dot Hill Systems", "TeraChip"],
186["T-RAM Incorporated", "Innovics Wireless", "Teknovus", "KeyEye Communications",
187 "Runcom Technologies", "RedSwitch", "Dotcast", "Silicon Mountain Memory",
188 "Signia Technologies", "Pixim", "Galazar Networks", "White Electronic Designs",
189 "Patriot Scientific", "Neoaxiom Corporation", "3Y Power Technology", "Europe Technologies",
190 "Potentia Power Systems", "C-guys Incorporated", "Digital Communications Technology Incorporated", "Silicon-Based Technology",
191 "Fulcrum Microsystems", "Positivo Informatica Ltd", "XIOtech Corporation", "PortalPlayer",
192 "Zhiying Software", "Direct2Data", "Phonex Broadband", "Skyworks Solutions",
193 "Entropic Communications", "Pacific Force Technology", "Zensys A/S", "Legend Silicon Corp.",
194 "sci-worx GmbH", "SMSC (former Oasis Silicon Systems)", "Renesas Technology", "Raza Microelectronics",
195 "Phyworks", "MediaTek", "Non-cents Productions", "US Modular",
196 "Wintegra Ltd", "Mathstar", "StarCore", "Oplus Technologies",
197 "Mindspeed", "Just Young Computer", "Radia Communications", "OCZ",
198 "Emuzed", "LOGIC Devices", "Inphi Corporation", "Quake Technologies",
199 "Vixel", "SolusTek", "Kongsberg Maritime", "Faraday Technology",
200 "Altium Ltd.", "Insyte", "ARM Ltd.", "DigiVision",
201 "Vativ Technologies", "Endicott Interconnect Technologies", "Pericom", "Bandspeed",
202 "LeWiz Communications", "CPU Technology", "Ramaxel Technology", "DSP Group",
203 "Axis Communications", "Legacy Electronics", "Chrontel", "Powerchip Semiconductor",
204 "MobilEye Technologies", "Excel Semiconductor", "A-DATA Technology", "VirtualDigm",
205 "G Skill Intl", "Quanta Computer", "Yield Microelectronics", "Afa Technologies",
206 "KINGBOX Technology Co. Ltd.", "Ceva", "iStor Networks", "Advance Modules",
207 "Microsoft", "Open-Silicon", "Goal Semiconductor", "ARC International",
208 "Simmtec", "Metanoia", "Key Stream", "Lowrance Electronics",
209 "Adimos", "SiGe Semiconductor", "Fodus Communications", "Credence Systems Corp.",
210 "Genesis Microchip Inc.", "Vihana, Inc.", "WIS Technologies", "GateChange Technologies",
211 "High Density Devices AS", "Synopsys", "Gigaram", "Enigma Semiconductor Inc.",
212 "Century Micro Inc.", "Icera Semiconductor", "Mediaworks Integrated Systems", "O'Neil Product Development",
213 "Supreme Top Technology Ltd.", "MicroDisplay Corporation", "Team Group Inc.", "Sinett Corporation",
214 "Toshiba Corporation", "Tensilica", "SiRF Technology", "Bacoc Inc.",
215 "SMaL Camera Technologies", "Thomson SC", "Airgo Networks", "Wisair Ltd.",
216 "SigmaTel", "Arkados", "Compete IT gmbH Co. KG", "Eudar Technology Inc.",
217 "Focus Enhancements", "Xyratex"],
218["Specular Networks", "Patriot Memory", "U-Chip Technology Corp.", "Silicon Optix",
219 "Greenfield Networks", "CompuRAM GmbH", "Stargen, Inc.", "NetCell Corporation",
220 "Excalibrus Technologies Ltd", "SCM Microsystems", "Xsigo Systems, Inc.", "CHIPS & Systems Inc",
221 "Tier 1 Multichip Solutions", "CWRL Labs", "Teradici", "Gigaram, Inc.",
222 "g2 Microsystems", "PowerFlash Semiconductor", "P.A. Semi, Inc.", "NovaTech Solutions, S.A.",
223 "c2 Microsystems, Inc.", "Level5 Networks", "COS Memory AG", "Innovasic Semiconductor",
224 "02IC Co. Ltd", "Tabula, Inc.", "Crucial Technology", "Chelsio Communications",
225 "Solarflare Communications", "Xambala Inc.", "EADS Astrium", "ATO Semicon Co. Ltd.",
226 "Imaging Works, Inc.", "Astute Networks, Inc.", "Tzero", "Emulex",
227 "Power-One", "Pulse~LINK Inc.", "Hon Hai Precision Industry", "White Rock Networks Inc.",
228 "Telegent Systems USA, Inc.", "Atrua Technologies, Inc.", "Acbel Polytech Inc.",
229 "eRide Inc.","ULi Electronics Inc.", "Magnum Semiconductor Inc.", "neoOne Technology, Inc.",
230 "Connex Technology, Inc.", "Stream Processors, Inc.", "Focus Enhancements", "Telecis Wireless, Inc.",
231 "uNav Microelectronics", "Tarari, Inc.", "Ambric, Inc.", "Newport Media, Inc.", "VMTS",
232 "Enuclia Semiconductor, Inc.", "Virtium Technology Inc.", "Solid State System Co., Ltd.", "Kian Tech LLC",
233 "Artimi", "Power Quotient International", "Avago Technologies", "ADTechnology", "Sigma Designs",
234 "SiCortex, Inc.", "Ventura Technology Group", "eASIC", "M.H.S. SAS", "Micro Star International", 
235 "Rapport Inc.", "Makway International", "Broad Reach Engineering Co.", 
236 "Semiconductor Mfg Intl Corp", "SiConnect", "FCI USA Inc.", "Validity Sensors", 
237 "Coney Technology Co. Ltd.", "Spans Logic", "Neterion Inc.", "Qimonda",
238 "New Japan Radio Co. Ltd.", "Velogix", "Montalvo Systems", "iVivity Inc.", "Walton Chaintech",
239 "AENEON", "Lorom Industrial Co. Ltd.", "Radiospire Networks", "Sensio Technologies, Inc.",
240 "Nethra Imaging", "Hexon Technology Pte Ltd", "CompuStocx (CSX)", "Methode Electronics, Inc.",
241 "Connect One Ltd.", "Opulan Technologies", "Septentrio NV", "Goldenmars Technology Inc.",
242 "Kreton Corporation", "Cochlear Ltd.", "Altair Semiconductor", "NetEffect, Inc.",
243 "Spansion, Inc.", "Taiwan Semiconductor Mfg", "Emphany Systems Inc.",
244 "ApaceWave Technologies", "Mobilygen Corporation", "Tego", "Cswitch Corporation",
245 "Haier (Beijing) IC Design Co.", "MetaRAM", "Axel Electronics Co. Ltd.", "Tilera Corporation",
246 "Aquantia", "Vivace Semiconductor", "Redpine Signals", "Octalica", "InterDigital Communications",
247 "Avant Technology", "Asrock, Inc.", "Availink", "Quartics, Inc.", "Element CXI",
248 "Innovaciones Microelectronicas", "VeriSilicon Microelectronics", "W5 Networks"],
249["MOVEKING", "Mavrix Technology, Inc.", "CellGuide Ltd.", "Faraday Technology",
250 "Diablo Technologies, Inc.", "Jennic", "Octasic", "Molex Incorporated", "3Leaf Networks",
251 "Bright Micron Technology", "Netxen", "NextWave Broadband Inc.", "DisplayLink", "ZMOS Technology",
252 "Tec-Hill", "Multigig, Inc.", "Amimon", "Euphonic Technologies, Inc.", "BRN Phoenix",
253 "InSilica", "Ember Corporation", "Avexir Technologies Corporation", "Echelon Corporation",
254 "Edgewater Computer Systems", "XMOS Semiconductor Ltd.", "GENUSION, Inc.", "Memory Corp NV",
255 "SiliconBlue Technologies", "Rambus Inc."]);
256
257$use_sysfs = -d '/sys/bus';
258
259# We consider that no data was written to this area of the SPD EEPROM if
260# all bytes read 0x00 or all bytes read 0xff
261sub spd_written(@)
262{
263        my $all_00 = 1;
264        my $all_ff = 1;
265       
266        foreach my $b (@_) {
267                $all_00 = 0 unless $b == 0x00;
268                $all_ff = 0 unless $b == 0xff;
269                return 1 unless $all_00 or $all_ff;
270        }
271
272        return 0;
273}
274
275sub parity($)
276{
277        my $n = shift;
278        my $parity = 0;
279
280        while ($n) {
281                $parity++ if ($n & 1);
282                $n >>= 1;
283        }
284
285        return ($parity & 1);
286}
287
288sub manufacturer(@)
289{
290        my @bytes = @_;
291        my $ai = 0;
292        my $first;
293
294        return ("Undefined", []) unless spd_written(@bytes);
295       
296        while (defined($first = shift(@bytes)) && $first == 0x7F) {
297                $ai++;
298        }
299
300        return ("Invalid", []) unless defined $first;
301        return ("Invalid", [$first, @bytes]) if parity($first) != 1;
302        return ("Unknown", \@bytes) unless (($first & 0x7F) - 1 <= $vendors[$ai]);
303
304        return ($vendors[$ai][($first & 0x7F) - 1], \@bytes);
305}
306
307sub manufacturer_data(@)
308{
309        my $hex = "";
310        my $asc = "";
311
312        return unless spd_written(@_);
313
314        foreach my $byte (@_) {
315                $hex .= sprintf("\%02X ", $byte);
316                $asc .= ($byte >= 32 && $byte < 127) ? chr($byte) : '?';
317        }
318
319        return "$hex(\"$asc\")";
320}
321
322sub part_number(@)
323{
324        my $asc = "";
325        my $byte;
326
327        while (defined ($byte = shift) && $byte >= 32 && $byte < 127) {
328                $asc .= chr($byte);
329        }
330
331        return ($asc eq "") ? "Undefined" : $asc;
332}
333
334sub printl ($$) # print a line w/ label and value
335{
336        my ($label, $value) = @_;
337        if ($opt_html) {
338                $label =~ s/</\&lt;/sg;
339                $label =~ s/>/\&gt;/sg;
340                $label =~ s/\n/<br>\n/sg;
341                $value =~ s/</\&lt;/sg;
342                $value =~ s/>/\&gt;/sg;
343                $value =~ s/\n/<br>\n/sg;
344                print "<tr><td valign=top>$label</td><td>$value</td></tr>\n";
345        } else {
346                my @values = split /\n/, $value;
347                printf "%-47s %s\n", $label, shift @values;
348                printf "%-47s %s\n", "", $_ foreach (@values);
349        }
350}
351
352sub printl2 ($$) # print a line w/ label and value (outside a table)
353{
354        my ($label, $value) = @_;
355        if ($opt_html) {
356                $label =~ s/</\&lt;/sg;
357                $label =~ s/>/\&gt;/sg;
358                $label =~ s/\n/<br>\n/sg;
359                $value =~ s/</\&lt;/sg;
360                $value =~ s/>/\&gt;/sg;
361                $value =~ s/\n/<br>\n/sg;
362        }
363        print "$label: $value\n";
364}
365
366sub prints ($) # print seperator w/ given text
367{
368        my ($label) = @_;
369        if ($opt_html) {
370                $label =~ s/</\&lt;/sg;
371                $label =~ s/>/\&gt;/sg;
372                $label =~ s/\n/<br>\n/sg;
373                print "<tr><td align=center colspan=2><b>$label</b></td></tr>\n";
374        } else {
375                print "\n---=== $label ===---\n";
376        }
377}
378
379sub printh ($$) # print header w/ given text
380{
381        my ($header, $sub) = @_;
382        if ($opt_html) {
383                $header =~ s/</\&lt;/sg;
384                $header =~ s/>/\&gt;/sg;
385                $header =~ s/\n/<br>\n/sg;
386                $sub =~ s/</\&lt;/sg;
387                $sub =~ s/>/\&gt;/sg;
388                $sub =~ s/\n/<br>\n/sg;
389                print "<h1>$header</h1>\n";
390                print "<p>$sub</p>\n";
391        } else {
392                print "\n$header\n$sub\n";
393        }
394}
395
396sub printc ($) # print comment
397{
398        my ($comment) = @_;
399        if ($opt_html) {
400                $comment =~ s/</\&lt;/sg;
401                $comment =~ s/>/\&gt;/sg;
402                $comment =~ s/\n/<br>\n/sg;
403                print "<!-- $comment -->\n";
404        } else {
405                print "# $comment\n";
406        }
407}
408
409# Parameter: bytes 0-63
410sub decode_sdr_sdram($)
411{
412        my $bytes = shift;
413        my ($l, $temp);
414
415# SPD revision
416        printl "SPD Revision", $bytes->[62];
417
418#size computation
419
420        prints "Memory Characteristics";
421
422        my $k=0;
423        my $ii=0;
424       
425        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
426        if (($bytes->[5] <= 8) && ($bytes->[17] <= 8)) {
427                 $k = $bytes->[5] * $bytes->[17];
428        }
429       
430        if($ii > 0 && $ii <= 12 && $k > 0) {
431                printl "Size", ((1 << $ii) * $k) . " MB"; }
432        else { 
433                printl "INVALID SIZE", $bytes->[3] . "," . $bytes->[4] . "," .
434                                       $bytes->[5] . "," . $bytes->[17];
435        }
436
437        my @cas;
438        for ($ii = 0; $ii < 7; $ii++) {
439                push(@cas, $ii + 1) if ($bytes->[18] & (1 << $ii));
440        }
441
442        my $trcd;
443        my $trp;
444        my $tras;
445        my $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
446
447        $trcd =$bytes->[29];
448        $trp =$bytes->[27];;
449        $tras =$bytes->[30];
450
451        printl "tCL-tRCD-tRP-tRAS",
452                $cas[$#cas] . "-" .
453                ceil($trcd/$ctime) . "-" .
454                ceil($trp/$ctime) . "-" .
455                ceil($tras/$ctime);
456
457        $l = "Number of Row Address Bits";
458        if ($bytes->[3] == 0) { printl $l, "Undefined!"; }
459        elsif ($bytes->[3] == 1) { printl $l, "1/16"; }
460        elsif ($bytes->[3] == 2) { printl $l, "2/17"; }
461        elsif ($bytes->[3] == 3) { printl $l, "3/18"; }
462        else { printl $l, $bytes->[3]; }
463
464        $l = "Number of Col Address Bits";
465        if ($bytes->[4] == 0) { printl $l, "Undefined!"; }
466        elsif ($bytes->[4] == 1) { printl $l, "1/16"; }
467        elsif ($bytes->[4] == 2) { printl $l, "2/17"; }
468        elsif ($bytes->[4] == 3) { printl $l, "3/18"; }
469        else { printl $l, $bytes->[4]; }
470
471        $l = "Number of Module Rows";
472        if ($bytes->[5] == 0 ) { printl $l, "Undefined!"; }
473        else { printl $l, $bytes->[5]; }
474
475        $l = "Data Width";
476        if ($bytes->[7] > 1) {
477                printl $l, "Undefined!"
478        } else {
479                $temp = ($bytes->[7] * 256) + $bytes->[6];
480                printl $l, $temp;
481        }
482
483        $l = "Module Interface Signal Levels";
484        if ($bytes->[8] == 0) { printl $l, "5.0 Volt/TTL"; }
485        elsif ($bytes->[8] == 1) { printl $l, "LVTTL"; }
486        elsif ($bytes->[8] == 2) { printl $l, "HSTL 1.5"; }
487        elsif ($bytes->[8] == 3) { printl $l, "SSTL 3.3"; }
488        elsif ($bytes->[8] == 4) { printl $l, "SSTL 2.5"; }
489        elsif ($bytes->[8] == 255) { printl $l, "New Table"; }
490        else { printl $l, "Undefined!"; }
491
492        $l = "Module Configuration Type";
493        if ($bytes->[11] == 0) { printl $l, "No Parity"; }
494        elsif ($bytes->[11] == 1) { printl $l, "Parity"; }
495        elsif ($bytes->[11] == 2) { printl $l, "ECC"; }
496        else { printl $l, "Undefined!"; }
497
498        $l = "Refresh Type";
499        if ($bytes->[12] > 126) { printl $l, "Self Refreshing"; }
500        else { printl $l, "Not Self Refreshing"; }
501
502        $l = "Refresh Rate";
503        $temp = $bytes->[12] & 0x7f;
504        if ($temp == 0) { printl $l, "Normal (15.625 us)"; }
505        elsif ($temp == 1) { printl $l, "Reduced (3.9 us)"; }
506        elsif ($temp == 2) { printl $l, "Reduced (7.8 us)"; }
507        elsif ($temp == 3) { printl $l, "Extended (31.3 us)"; }
508        elsif ($temp == 4) { printl $l, "Extended (62.5 us)"; }
509        elsif ($temp == 5) { printl $l, "Extended (125 us)"; }
510        else { printl $l, "Undefined!"; }
511
512        $l = "Primary SDRAM Component Bank Config";
513        if ($bytes->[13] > 126) { printl $l, "Bank2 = 2 x Bank1"; }
514        else { printl $l, "No Bank2 OR Bank2 = Bank1 width"; }
515
516        $l = "Primary SDRAM Component Widths";
517        $temp = $bytes->[13] & 0x7f;
518        if ($temp == 0) { printl $l, "Undefined!\n"; }
519        else { printl $l, $temp; }
520
521        $l = "Error Checking SDRAM Component Bank Config";
522        if ($bytes->[14] > 126) { printl $l, "Bank2 = 2 x Bank1"; }
523        else { printl $l, "No Bank2 OR Bank2 = Bank1 width"; }
524
525        $l = "Error Checking SDRAM Component Widths";
526        $temp = $bytes->[14] & 0x7f;
527        if ($temp == 0) { printl $l, "Undefined!"; }
528        else { printl $l, $temp; }
529
530        $l = "Min Clock Delay for Back to Back Random Access";
531        if ($bytes->[15] == 0) { printl $l, "Undefined!"; }
532        else { printl $l, $bytes->[15]; }
533
534        $l = "Burst lengths supported";
535        my @array;
536        for ($ii = 0; $ii < 4; $ii++) {
537                push(@array, 1 << $ii) if ($bytes->[16] & (1 << $ii));
538        }
539        push(@array, "Page") if ($bytes->[16] & 128);
540        if (@array) { $temp = join ', ', @array; }
541        else { $temp = "None"; }
542        printl $l, $temp;
543
544        $l = "Number of Device Banks";
545        if ($bytes->[17] == 0) { printl $l, "Undefined/Reserved!"; }
546        else { printl $l, $bytes->[17]; }
547
548        $l = "Supported CAS Latencies";
549        if (@cas) { $temp = join ', ', @cas; }
550        else { $temp = "None"; }
551        printl $l, $temp;
552
553        $l = "Supported CS Latencies";
554        @array = ();
555        for ($ii = 0; $ii < 7; $ii++) {
556                push(@array, $ii) if ($bytes->[19] & (1 << $ii));
557        }
558        if (@array) { $temp = join ', ', @array; }
559        else { $temp = "None"; }
560        printl $l, $temp;
561
562        $l = "Supported WE Latencies";
563        @array = ();
564        for ($ii = 0; $ii < 7; $ii++) {
565                push(@array, $ii) if ($bytes->[20] & (1 << $ii));
566        }
567        if (@array) { $temp = join ', ', @array; }
568        else { $temp = "None"; }
569        printl $l, $temp;
570
571        if (@cas >= 1) {
572                $l = "Cycle Time (CAS ".$cas[$#cas].")";
573                printl $l, "$ctime ns";
574
575                $l = "Access Time (CAS ".$cas[$#cas].")";
576                $temp = ($bytes->[10] >> 4) + ($bytes->[10] & 0xf) * 0.1;
577                printl $l, "$temp ns";
578        }
579
580        if (@cas >= 2 && spd_written(@$bytes[23..24])) {
581                $l = "Cycle Time (CAS ".$cas[$#cas-1].")";
582                $temp = $bytes->[23] >> 4;
583                if ($temp == 0) { printl $l, "Undefined!"; }
584                else {
585                        if ($temp < 4 ) { $temp=$temp + 15; }
586                        printl $l, $temp + (($bytes->[23] & 0xf) * 0.1) . " ns";
587                }
588
589                $l = "Access Time (CAS ".$cas[$#cas-1].")";
590                $temp = $bytes->[24] >> 4;
591                if ($temp == 0) { printl $l, "Undefined!"; }
592                else {
593                        if ($temp < 4 ) { $temp=$temp + 15; }
594                        printl $l, $temp + (($bytes->[24] & 0xf) * 0.1) . " ns";
595                }
596        }
597
598        if (@cas >= 3 && spd_written(@$bytes[25..26])) {
599                $l = "Cycle Time (CAS ".$cas[$#cas-2].")";
600                $temp = $bytes->[25] >> 2;
601                if ($temp == 0) { printl $l, "Undefined!"; }
602                else { printl $l, $temp + ($bytes->[25] & 0x3) * 0.25 . " ns"; }
603
604                $l = "Access Time (CAS ".$cas[$#cas-2].")";
605                $temp = $bytes->[26] >> 2;
606                if ($temp == 0) { printl $l, "Undefined!"; }
607                else { printl $l, $temp + ($bytes->[26] & 0x3) * 0.25 . " ns"; }
608        }
609
610        $l = "SDRAM Module Attributes";
611        $temp = "";
612        if ($bytes->[21] & 1) { $temp .= "Buffered Address/Control Inputs\n"; }
613        if ($bytes->[21] & 2) { $temp .= "Registered Address/Control Inputs\n"; }
614        if ($bytes->[21] & 4) { $temp .= "On card PLL (clock)\n"; }
615        if ($bytes->[21] & 8) { $temp .= "Buffered DQMB Inputs\n"; }
616        if ($bytes->[21] & 16) { $temp .= "Registered DQMB Inputs\n"; }
617        if ($bytes->[21] & 32) { $temp .= "Differential Clock Input\n"; }
618        if ($bytes->[21] & 64) { $temp .= "Redundant Row Address\n"; }
619        if ($bytes->[21] & 128) { $temp .= "Undefined (bit 7)\n"; }
620        if ($bytes->[21] == 0) { $temp .= "(None Reported)\n"; }
621        printl $l, $temp;
622
623        $l = "SDRAM Device Attributes (General)";
624        $temp = "";
625        if ($bytes->[22] & 1) { $temp .= "Supports Early RAS# Recharge\n"; }
626        if ($bytes->[22] & 2) { $temp .= "Supports Auto-Precharge\n"; }
627        if ($bytes->[22] & 4) { $temp .= "Supports Precharge All\n"; }
628        if ($bytes->[22] & 8) { $temp .= "Supports Write1/Read Burst\n"; }
629        if ($bytes->[22] & 16) { $temp .= "Lower VCC Tolerance: 5%\n"; }
630        else { $temp .= "Lower VCC Tolerance: 10%\n"; }
631        if ($bytes->[22] & 32) { $temp .= "Upper VCC Tolerance: 5%\n"; }
632        else { $temp .= "Upper VCC Tolerance: 10%\n"; }
633        if ($bytes->[22] & 64) { $temp .= "Undefined (bit 6)\n"; }
634        if ($bytes->[22] & 128) { $temp .= "Undefined (bit 7)\n"; }
635        printl $l, $temp;
636
637        $l = "Minimum Row Precharge Time";
638        if ($bytes->[27] == 0) { printl $l, "Undefined!"; }
639        else { printl $l, "$bytes->[27] ns"; }
640
641        $l = "Row Active to Row Active Min";
642        if ($bytes->[28] == 0) { printl $l, "Undefined!"; }
643        else { printl $l, "$bytes->[28] ns"; }
644
645        $l = "RAS to CAS Delay";
646        if ($bytes->[29] == 0) { printl $l, "Undefined!"; }
647        else { printl $l, "$bytes->[29] ns"; }
648
649        $l = "Min RAS Pulse Width";
650        if ($bytes->[30] == 0) { printl $l, "Undefined!"; }
651        else { printl $l, "$bytes->[30] ns"; }
652
653        $l = "Row Densities";
654        $temp = "";
655        if ($bytes->[31] & 1) { $temp .= "4 MByte\n"; }
656        if ($bytes->[31] & 2) { $temp .= "8 MByte\n"; }
657        if ($bytes->[31] & 4) { $temp .= "16 MByte\n"; }
658        if ($bytes->[31] & 8) { $temp .= "32 MByte\n"; }
659        if ($bytes->[31] & 16) { $temp .= "64 MByte\n"; }
660        if ($bytes->[31] & 32) { $temp .= "128 MByte\n"; }
661        if ($bytes->[31] & 64) { $temp .= "256 MByte\n"; }
662        if ($bytes->[31] & 128) { $temp .= "512 MByte\n"; }
663        if ($bytes->[31] == 0) { $temp .= "(Undefined! -- None Reported!)\n"; }
664        printl $l, $temp;
665
666        if (($bytes->[32] & 0xf) <= 9) {
667                $l = "Command and Address Signal Setup Time";
668                $temp = (($bytes->[32] & 0x7f) >> 4) + ($bytes->[32] & 0xf) * 0.1;
669                printl $l, (($bytes->[32] >> 7) ? -$temp : $temp) . " ns";
670        }
671
672        if (($bytes->[33] & 0xf) <= 9) {
673                $l = "Command and Address Signal Hold Time";
674                $temp = (($bytes->[33] & 0x7f) >> 4) + ($bytes->[33] & 0xf) * 0.1;
675                printl $l, (($bytes->[33] >> 7) ? -$temp : $temp) . " ns";
676        }
677
678        if (($bytes->[34] & 0xf) <= 9) {
679                $l = "Data Signal Setup Time";
680                $temp = (($bytes->[34] & 0x7f) >> 4) + ($bytes->[34] & 0xf) * 0.1;
681                printl $l, (($bytes->[34] >> 7) ? -$temp : $temp) . " ns";
682        }
683
684        if (($bytes->[35] & 0xf) <= 9) {
685                $l = "Data Signal Hold Time";
686                $temp = (($bytes->[35] & 0x7f) >> 4) + ($bytes->[35] & 0xf) * 0.1;
687                printl $l, (($bytes->[35] >> 7) ? -$temp : $temp) . " ns";
688        }
689}
690
691# Parameter: bytes 0-63
692sub decode_ddr_sdram($)
693{
694        my $bytes = shift;
695        my ($l, $temp);
696
697# SPD revision
698        if ($bytes->[62] != 0xff) {
699                printl "SPD Revision", ($bytes->[62] >> 4) . "." .
700                                       ($bytes->[62] & 0xf);
701        }
702
703# speed
704        prints "Memory Characteristics";
705
706        $l = "Maximum module speed";
707        $temp = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
708        my $ddrclk = 2 * (1000 / $temp);
709        my $tbits = ($bytes->[7] * 256) + $bytes->[6];
710        if (($bytes->[11] == 2) || ($bytes->[11] == 1)) { $tbits = $tbits - 8; }
711        my $pcclk = int ($ddrclk * $tbits / 8);
712        $pcclk += 100 if ($pcclk % 100) >= 50; # Round properly
713        $pcclk = $pcclk - ($pcclk % 100);
714        $ddrclk = int ($ddrclk);
715        printl $l, "${ddrclk}MHz (PC${pcclk})";
716
717#size computation
718        my $k=0;
719        my $ii=0;
720       
721        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
722        if (($bytes->[5] <= 8) && ($bytes->[17] <= 8)) {
723                 $k = $bytes->[5] * $bytes->[17];
724        }
725       
726        if($ii > 0 && $ii <= 12 && $k > 0) {
727                printl "Size", ((1 << $ii) * $k) . " MB"; }
728        else { 
729                printl "INVALID SIZE", $bytes->[3] . ", " . $bytes->[4] . ", " .
730                                       $bytes->[5] . ", " . $bytes->[17];
731        }
732
733        my $highestCAS = 0;
734        my %cas;
735        for ($ii = 0; $ii < 7; $ii++) {
736                if ($bytes->[18] & (1 << $ii)) {
737                        $highestCAS = 1+$ii*0.5;
738                        $cas{$highestCAS}++;
739                }
740        }
741
742        my $trcd;
743        my $trp;
744        my $tras;
745        my $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
746
747        $trcd =($bytes->[29] >> 2)+(($bytes->[29] & 3)*0.25);
748        $trp =($bytes->[27] >> 2)+(($bytes->[27] & 3)*0.25);
749        $tras = $bytes->[30];
750
751        printl "tCL-tRCD-tRP-tRAS",
752                $highestCAS . "-" .
753                ceil($trcd/$ctime) . "-" .
754                ceil($trp/$ctime) . "-" .
755                ceil($tras/$ctime);
756
757# latencies
758        if (keys %cas) { $temp = join ', ', sort { $b <=> $a } keys %cas; }
759        else { $temp = "None"; }
760        printl "Supported CAS Latencies", $temp;
761
762        my @array;
763        for ($ii = 0; $ii < 7; $ii++) {
764                push(@array, $ii) if ($bytes->[19] & (1 << $ii));
765        }
766        if (@array) { $temp = join ', ', @array; }
767        else { $temp = "None"; }
768        printl "Supported CS Latencies", $temp;
769
770        @array = ();
771        for ($ii = 0; $ii < 7; $ii++) {
772                push(@array, $ii) if ($bytes->[20] & (1 << $ii));
773        }
774        if (@array) { $temp = join ', ', @array; }
775        else { $temp = "None"; }
776        printl "Supported WE Latencies", $temp;
777
778# timings
779        if (exists $cas{$highestCAS}) {
780                printl "Minimum Cycle Time (CAS $highestCAS)",
781                       "$ctime ns";
782
783                printl "Maximum Access Time (CAS $highestCAS)",
784                       (($bytes->[10] >> 4) * 0.1 + ($bytes->[10] & 0xf) * 0.01) . " ns";
785        }
786
787        if (exists $cas{$highestCAS-0.5} && spd_written(@$bytes[23..24])) {
788                printl "Minimum Cycle Time (CAS ".($highestCAS-0.5).")",
789                       (($bytes->[23] >> 4) + ($bytes->[23] & 0xf) * 0.1) . " ns";
790
791                printl "Maximum Access Time (CAS ".($highestCAS-0.5).")",
792                       (($bytes->[24] >> 4) * 0.1 + ($bytes->[24] & 0xf) * 0.01) . " ns";
793        }
794
795        if (exists $cas{$highestCAS-1} && spd_written(@$bytes[25..26])) {
796                printl "Minimum Cycle Time (CAS ".($highestCAS-1).")",
797                       (($bytes->[25] >> 4) + ($bytes->[25] & 0xf) * 0.1) . " ns";
798
799                printl "Maximum Access Time (CAS ".($highestCAS-1).")",
800                       (($bytes->[26] >> 4) * 0.1 + ($bytes->[26] & 0xf) * 0.01) . " ns";
801        }
802
803# module attributes
804        if ($bytes->[47] & 0x03) {
805                if (($bytes->[47] & 0x03) == 0x01) { $temp = "1.125\" to 1.25\""; }
806                elsif (($bytes->[47] & 0x03) == 0x02) { $temp = "1.7\""; }
807                elsif (($bytes->[47] & 0x03) == 0x03) { $temp = "Other"; }
808                printl "Module Height", $temp;
809        }
810}
811
812sub ddr2_sdram_ctime($)
813{
814        my $byte = shift;
815        my $ctime;
816       
817        $ctime = $byte >> 4;
818        if (($byte & 0xf) <= 9) { $ctime += ($byte & 0xf) * 0.1; }
819        elsif (($byte & 0xf) == 10) { $ctime += 0.25; }
820        elsif (($byte & 0xf) == 11) { $ctime += 0.33; }
821        elsif (($byte & 0xf) == 12) { $ctime += 0.66; }
822        elsif (($byte & 0xf) == 13) { $ctime += 0.75; }
823
824        return $ctime;
825}
826
827sub ddr2_sdram_atime($)
828{
829        my $byte = shift;
830        my $atime;
831       
832        $atime = ($byte >> 4) * 0.1 + ($byte & 0xf) * 0.01;
833
834        return $atime;
835}
836
837# Parameter: bytes 0-63
838sub decode_ddr2_sdram($)
839{
840        my $bytes = shift;
841        my ($l, $temp);
842        my $ctime;
843
844# SPD revision
845        if ($bytes->[62] != 0xff) {
846                printl "SPD Revision", ($bytes->[62] >> 4) . "." .
847                                       ($bytes->[62] & 0xf);
848        }
849
850# speed
851        prints "Memory Characteristics";
852
853        $l = "Maximum module speed";
854        $ctime = ddr2_sdram_ctime($bytes->[9]);
855        my $ddrclk = 2 * (1000 / $ctime);
856        my $tbits = ($bytes->[7] * 256) + $bytes->[6];
857        if ($bytes->[11] & 0x03) { $tbits = $tbits - 8; }
858        my $pcclk = int ($ddrclk * $tbits / 8);
859        # Round down to comply with Jedec
860        $pcclk = $pcclk - ($pcclk % 100);
861        $ddrclk = int ($ddrclk);
862        printl $l, "${ddrclk}MHz (PC2-${pcclk})";
863
864#size computation
865        my $k=0;
866        my $ii=0;
867
868        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
869        $k = (($bytes->[5] & 0x7) + 1) * $bytes->[17];
870       
871        if($ii > 0 && $ii <= 12 && $k > 0) {
872                printl "Size", ((1 << $ii) * $k) . " MB"; 
873        } else {
874                printl "INVALID SIZE", $bytes->[3] . "," . $bytes->[4] . "," .
875                                       $bytes->[5] . "," . $bytes->[17];
876        }
877
878        my $highestCAS = 0;
879        my %cas;
880        for ($ii = 2; $ii < 7; $ii++) {
881                if ($bytes->[18] & (1 << $ii)) {
882                        $highestCAS = $ii;
883                        $cas{$highestCAS}++;
884                }
885        }
886
887        my $trcd;
888        my $trp;
889        my $tras;
890       
891        $trcd =($bytes->[29] >> 2)+(($bytes->[29] & 3)*0.25);
892        $trp =($bytes->[27] >> 2)+(($bytes->[27] & 3)*0.25);
893        $tras =$bytes->[30];
894
895        printl "tCL-tRCD-tRP-tRAS",
896                $highestCAS . "-" .
897                ceil($trcd/$ctime) . "-" .
898                ceil($trp/$ctime) . "-" .
899                ceil($tras/$ctime);
900
901# latencies
902        if (keys %cas) { $temp = join ', ', sort { $b <=> $a } keys %cas; }
903        else { $temp = "None"; }
904        printl "Supported CAS Latencies", $temp;
905
906# timings
907        if (exists $cas{$highestCAS}) {
908                printl "Minimum Cycle Time (CAS $highestCAS)",
909                       "$ctime ns";
910                printl "Maximum Access Time (CAS $highestCAS)",
911                       ddr2_sdram_atime($bytes->[10]) . " ns";
912        }
913
914        if (exists $cas{$highestCAS-1} && spd_written(@$bytes[23..24])) {
915                printl "Minimum Cycle Time (CAS ".($highestCAS-1).")",
916                       ddr2_sdram_ctime($bytes->[23]) . " ns";
917                printl "Maximum Access Time (CAS ".($highestCAS-1).")",
918                       ddr2_sdram_atime($bytes->[24]) . " ns";
919        }
920
921        if (exists $cas{$highestCAS-2} && spd_written(@$bytes[25..26])) {
922                printl "Minimum Cycle Time (CAS ".($highestCAS-2).")",
923                       ddr2_sdram_ctime($bytes->[25]) . " ns";
924                printl "Maximum Access Time (CAS ".($highestCAS-2).")",
925                       ddr2_sdram_atime($bytes->[26]) . " ns";
926        }
927}
928
929# Parameter: bytes 0-63
930sub decode_direct_rambus($)
931{
932        my $bytes = shift;
933
934#size computation
935        prints "Memory Characteristics";
936
937        my $ii;
938
939        $ii = ($bytes->[4] & 0x0f) + ($bytes->[4] >> 4) + ($bytes->[5] & 0x07) - 13;
940       
941        if ($ii > 0 && $ii < 16) {
942                printl "Size", (1 << $ii) . " MB";
943        } else {
944                printl "INVALID SIZE", sprintf("0x%02x, 0x%02x",
945                                               $bytes->[4], $bytes->[5]);
946        }
947}
948
949# Parameter: bytes 0-63
950sub decode_rambus($)
951{
952        my $bytes = shift;
953
954#size computation
955        prints "Memory Characteristics";
956
957        my $ii;
958       
959        $ii = ($bytes->[3] & 0x0f) + ($bytes->[3] >> 4) + ($bytes->[5] & 0x07) - 13;
960       
961        if ($ii > 0 && $ii < 16) {
962                printl "Size", (1 << $ii) . " MB";
963        } else {
964                printl "INVALID SIZE", sprintf("0x%02x, 0x%02x",
965                                               $bytes->[3], $bytes->[5]);
966        }
967}
968
969%decode_callback = (
970        "SDR SDRAM"     => \&decode_sdr_sdram,
971        "DDR SDRAM"     => \&decode_ddr_sdram,
972        "DDR2 SDRAM"    => \&decode_ddr2_sdram,
973        "Direct Rambus" => \&decode_direct_rambus,
974        "Rambus"        => \&decode_rambus,
975);
976
977# Parameter: bytes 64-127
978sub decode_intel_spec_freq($)
979{
980        my $bytes = shift;
981        my ($l, $temp);
982
983        prints "Intel Specification";
984
985        $l = "Frequency";
986        if ($bytes->[62] == 0x66) { $temp = "66MHz\n"; }
987        elsif ($bytes->[62] == 100) { $temp = "100MHz or 133MHz\n"; }
988        elsif ($bytes->[62] == 133) { $temp = "133MHz\n"; }
989        else { $temp = "Undefined!\n"; }
990        printl $l, $temp;
991
992        $l = "Details for 100MHz Support";
993        $temp="";
994        if ($bytes->[63] & 1) { $temp .= "Intel Concurrent Auto-precharge\n"; }
995        if ($bytes->[63] & 2) { $temp .= "CAS Latency = 2\n"; }
996        if ($bytes->[63] & 4) { $temp .= "CAS Latency = 3\n"; }
997        if ($bytes->[63] & 8) { $temp .= "Junction Temp A (100 degrees C)\n"; }
998        else { $temp .= "Junction Temp B (90 degrees C)\n"; }
999        if ($bytes->[63] & 16) { $temp .= "CLK 3 Connected\n"; }
1000        if ($bytes->[63] & 32) { $temp .= "CLK 2 Connected\n"; }
1001        if ($bytes->[63] & 64) { $temp .= "CLK 1 Connected\n"; }
1002        if ($bytes->[63] & 128) { $temp .= "CLK 0 Connected\n"; }
1003        if (($bytes->[63] & 192) == 192) { $temp .= "Double-sided DIMM\n"; }
1004        elsif (($bytes->[63] & 192) != 0) { $temp .= "Single-sided DIMM\n"; }
1005        printl $l, $temp;
1006}
1007
1008sub readspd64 ($$) { # reads 64 bytes from SPD-EEPROM
1009        my ($offset, $dimm_i) = @_;
1010        my @bytes;
1011        if ($use_sysfs) {
1012                # Kernel 2.6 with sysfs
1013                sysopen(HANDLE, "/sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom", O_RDONLY)
1014                        or die "Cannot open /sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom";
1015                binmode HANDLE;
1016                sysseek(HANDLE, $offset, SEEK_SET);
1017                sysread(HANDLE, my $eeprom, 64);
1018                close HANDLE;
1019                @bytes = unpack("C64", $eeprom);
1020        } else {
1021                # Kernel 2.4 with procfs
1022                for my $i (0 .. 3) {
1023                        my $hexoff = sprintf('%02x', $offset + $i * 16);
1024                        push @bytes, split(" ", `cat /proc/sys/dev/sensors/$dimm_i/$hexoff`);
1025                }
1026        }
1027        return @bytes;
1028}
1029
1030for (@ARGV) {
1031    if (/-h/) {
1032                print "Usage: $0 [-c] [-f [-b]]\n",
1033                        "       $0 -h\n\n",
1034                        "  -f, --format            print nice html output\n",
1035                        "  -b, --bodyonly          don't print html header\n",
1036                        "                          (useful for postprocessing the output)\n",
1037                        "  -c, --checksum          decode completely even if checksum fails\n",
1038                        "  -h, --help              display this usage summary\n";
1039                exit;
1040    }
1041    $opt_html = 1 if (/-f/);
1042    $opt_bodyonly = 1 if (/-b/);
1043    $opt_igncheck = 1 if (/-c/);
1044}
1045$opt_body = $opt_html && ! $opt_bodyonly;
1046
1047if ($opt_body)
1048{
1049        print "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
1050              "<html><head>\n",
1051                  "\t<meta HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">\n",
1052                  "\t<title>PC DIMM Serial Presence Detect Tester/Decoder Output</title>\n",
1053                  "</head><body>\n";
1054}
1055
1056printc "decode-dimms version $revision";
1057printh 'Memory Serial Presence Detect Decoder',
1058'By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner,
1059Jean Delvare and others';
1060
1061
1062my $dimm_count=0;
1063my @dimm_list;
1064my $dir;
1065if ($use_sysfs) { $dir = '/sys/bus/i2c/drivers/eeprom'; }
1066else { $dir = '/proc/sys/dev/sensors'; }
1067if (-d $dir) {
1068        @dimm_list = split(/\s+/, `ls $dir`);
1069} elsif (! -d '/sys/module/eeprom') {
1070        print "No EEPROM found, are you sure the eeprom module is loaded?\n";
1071        exit;
1072}
1073
1074for my $i ( 0 .. $#dimm_list ) {
1075        $_=$dimm_list[$i];
1076        if (($use_sysfs && /^\d+-\d+$/)
1077         || (!$use_sysfs && /^eeprom-/)) {
1078                my @bytes = readspd64(0, $dimm_list[$i]);
1079                my $dimm_checksum = 0;
1080                $dimm_checksum += $bytes[$_] foreach (0 .. 62);
1081                $dimm_checksum &= 0xff;
1082
1083                next unless $bytes[63] == $dimm_checksum || $opt_igncheck;
1084                $dimm_count++;
1085
1086                print "<b><u>" if $opt_html;
1087                printl2 "\n\nDecoding EEPROM", ($use_sysfs ?
1088                        "/sys/bus/i2c/drivers/eeprom/$dimm_list[$i]" :
1089                        "/proc/sys/dev/sensors/$dimm_list[$i]");
1090                print "</u></b>" if $opt_html;
1091                print "<table border=1>\n" if $opt_html;
1092                if (($use_sysfs && /^[^-]+-([^-]+)$/)
1093                 || (!$use_sysfs && /^[^-]+-[^-]+-[^-]+-([^-]+)$/)) {
1094                        my $dimm_num=$1 - 49;
1095                        printl "Guessing DIMM is in", "bank $dimm_num";
1096                }
1097
1098# Decode first 3 bytes (0-2)
1099                prints "SPD EEPROM Information";
1100
1101                my $l = "EEPROM Checksum of bytes 0-62";
1102                printl $l, ($bytes[63] == $dimm_checksum ?
1103                        sprintf("OK (0x%.2X)", $bytes[63]):
1104                        sprintf("Bad\n(found 0x%.2X, calculated 0x%.2X)\n",
1105                                $bytes[63], $dimm_checksum));
1106
1107                # Simple heuristic to detect Rambus
1108                my $is_rambus = $bytes[0] < 4;
1109                my $temp;
1110                if ($is_rambus) {
1111                        if ($bytes[0] == 1) { $temp = "0.7"; }
1112                        elsif ($bytes[0] == 2) { $temp = "1.0"; }
1113                        elsif ($bytes[0] == 0 || $bytes[0] == 255) { $temp = "Invalid"; }
1114                        else { $temp = "Reserved"; }
1115                        printl "SPD Revision", $temp;
1116                } else {
1117                        printl "# of bytes written to SDRAM EEPROM",
1118                               $bytes[0];
1119                }
1120
1121                $l = "Total number of bytes in EEPROM";
1122                if ($bytes[1] <= 14) {
1123                        printl $l, 2**$bytes[1];
1124                } elsif ($bytes[1] == 0) {
1125                        printl $l, "RFU"; 
1126                } else { printl $l, "ERROR!"; }
1127
1128                $l = "Fundamental Memory type";
1129                my $type = "Unknown";
1130                if ($is_rambus) {
1131                        if ($bytes[2] == 1) { $type = "Direct Rambus"; }
1132                        elsif ($bytes[2] == 17) { $type = "Rambus"; }
1133                } else {
1134                        if ($bytes[2] == 1) { $type = "FPM DRAM"; }
1135                        elsif ($bytes[2] == 2) { $type = "EDO"; }
1136                        elsif ($bytes[2] == 3) { $type = "Pipelined Nibble"; }
1137                        elsif ($bytes[2] == 4) { $type = "SDR SDRAM"; }
1138                        elsif ($bytes[2] == 5) { $type = "Multiplexed ROM"; }
1139                        elsif ($bytes[2] == 6) { $type = "DDR SGRAM"; }
1140                        elsif ($bytes[2] == 7) { $type = "DDR SDRAM"; }
1141                        elsif ($bytes[2] == 8) { $type = "DDR2 SDRAM"; }
1142                }
1143                printl $l, $type;
1144
1145# Decode next 61 bytes (3-63, depend on memory type)
1146                $decode_callback{$type}->(\@bytes)
1147                        if exists $decode_callback{$type};
1148
1149# Decode next 35 bytes (64-98, common to all memory types)
1150                prints "Manufacturing Information";
1151
1152                @bytes = readspd64(64, $dimm_list[$i]);
1153               
1154                $l = "Manufacturer";
1155                # $extra is a reference to an array containing up to
1156                # 7 extra bytes from the Manufacturer field. Sometimes
1157                # these bytes are filled with interesting data.
1158                ($temp, my $extra) = manufacturer(@bytes[0..7]);
1159                printl $l, $temp;
1160                $l = "Custom Manufacturer Data";
1161                $temp = manufacturer_data(@{$extra});
1162                printl $l, $temp if defined $temp;
1163               
1164                if (spd_written($bytes[8])) {
1165                        # Try the location code as ASCII first, as earlier specifications
1166                        # suggested this. As newer specifications don't mention it anymore,
1167                        # we still fall back to binary.
1168                        $l = "Manufacturing Location Code";
1169                        $temp = (chr($bytes[8]) =~ m/^[\w\d]$/) ? chr($bytes[8])
1170                              : sprintf("0x%.2X", $bytes[8]);
1171                        printl $l, $temp;
1172                }
1173               
1174                $l = "Part Number";
1175                $temp = part_number(@bytes[9..26]);
1176                printl $l, $temp;
1177               
1178                if (spd_written(@bytes[27..28])) {
1179                        $l = "Revision Code";
1180                        $temp = sprintf("0x%02X%02X\n", @bytes[27..28]);
1181                        printl $l, $temp;
1182                }
1183               
1184                if (spd_written(@bytes[29..30])) {
1185                        $l = "Manufacturing Date";
1186                        # In theory the year and week are in BCD format, but
1187                        # this is not always true in practice :(
1188                        if (($bytes[29] & 0xf0) <= 0x90
1189                         && ($bytes[29] & 0x0f) <= 0x09
1190                         && ($bytes[30] & 0xf0) <= 0x90
1191                         && ($bytes[30] & 0x0f) <= 0x09) {
1192                                # Note that this heuristic will break in year 2080
1193                                $temp = sprintf("%d%02X-W%02X\n",
1194                                                $bytes[29] >= 0x80 ? 19 : 20,
1195                                                @bytes[29..30]);
1196                        } else {
1197                                $temp = sprintf("0x%02X%02X\n",
1198                                                @bytes[29..30]);
1199                        }
1200                        printl $l, $temp;
1201                }
1202               
1203                if (spd_written(@bytes[31..34])) {
1204                        $l = "Assembly Serial Number";
1205                        $temp = sprintf("0x%02X%02X%02X%02X\n",
1206                                        @bytes[31..34]);
1207                        printl $l, $temp;
1208                }
1209
1210# Next 27 bytes (99-125) are manufacturer specific, can't decode
1211
1212# Last 2 bytes (126-127) are reserved, Intel used them as an extension
1213                if ($type eq "SDR SDRAM") {
1214                        decode_intel_spec_freq(\@bytes);
1215                }
1216               
1217                print "</table>\n" if $opt_html;
1218        }
1219}
1220printl2 "\n\nNumber of SDRAM DIMMs detected and decoded", $dimm_count;
1221
1222print "</body></html>\n" if $opt_body;
Note: See TracBrowser for help on using the browser.