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

Revision 5160, 48.2 KB (checked in by khali, 5 years ago)

Drop $opt_body, name was confusing and we can easily do without it.
Update header comment, drop redundant command line interface documentation.

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