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

Revision 5161, 48.4 KB (checked in by khali, 5 years ago)

Cache the parsed hexdumps so that each dump file is only parsed once.

  • 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 %hexdump_cache);
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        # Look in the cache first
1108        return @{$hexdump_cache{$_[0]}} if exists $hexdump_cache{$_[0]};
1109
1110        open F, '<', $_[0] or die "Unable to open: $_[0]";
1111        while (<F>) {
1112                chomp;
1113                if (/^\*$/) {
1114                        $repstart = $addr;
1115                        next;
1116                }
1117                /^(?:0000 )?([a-f\d]{2,8}):?\s+((:?[a-f\d]{4}\s*){8}|(:?[a-f\d]{2}\s*){16})/i ||
1118                /^(?:0000 )?([a-f\d]{2,8}):?\s*$/i;
1119                next if (!defined $1 && $header);               # skip leading unparsed lines
1120
1121                defined $1 or die "Unable to parse input";
1122                $header = 0;
1123
1124                $addr = hex $1;
1125                if ($repstart) {
1126                        @bytes[$repstart .. ($addr-1)] =
1127                                (@bytes[($repstart-16)..($repstart-1)]) x (($addr-$repstart)/16);
1128                        $repstart = 0;
1129                }
1130                last unless defined $2;
1131                foreach (split(/\s+/, $2)) {
1132                        if (/^(..)(..)$/) {
1133                                $word |= 1;
1134                                $bytes[$addr++] = hex($1);
1135                                $bytes[$addr++] = hex($2);
1136                        } else {
1137                                $bytes[$addr++] = hex($_);
1138                        }
1139                }
1140        }
1141        close F;
1142        $header and die "Unable to parse any data from hexdump '$_[0]'";
1143        $word and printc "Warning: Assuming big-endian order 16-bit hex dump";
1144
1145        # Cache the data for later use
1146        $hexdump_cache{$_[0]} = \@bytes;
1147        return @bytes;
1148}
1149
1150sub readspd64($$) # reads 64 bytes from SPD-EEPROM
1151{
1152        my ($offset, $dimm_i) = @_;
1153        my @bytes;
1154        if ($use_hexdump) {
1155                @bytes = read_hexdump($dimm_i);
1156                return @bytes[$offset..($offset+63)];
1157        } elsif ($use_sysfs) {
1158                # Kernel 2.6 with sysfs
1159                sysopen(HANDLE, "/sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom", O_RDONLY)
1160                        or die "Cannot open /sys/bus/i2c/drivers/eeprom/$dimm_i/eeprom";
1161                binmode HANDLE;
1162                sysseek(HANDLE, $offset, SEEK_SET);
1163                sysread(HANDLE, my $eeprom, 64);
1164                close HANDLE;
1165                @bytes = unpack("C64", $eeprom);
1166        } else {
1167                # Kernel 2.4 with procfs
1168                for my $i (0 .. 3) {
1169                        my $hexoff = sprintf('%02x', $offset + $i * 16);
1170                        push @bytes, split(" ", `cat /proc/sys/dev/sensors/$dimm_i/$hexoff`);
1171                }
1172        }
1173        return @bytes;
1174}
1175
1176# Parse command-line
1177foreach (@ARGV) {
1178        if ($_ eq '-h' || $_ eq '--help') {
1179                print "Usage: $0 [-c] [-f [-b]] [-x file [files..]]\n",
1180                        "       $0 -h\n\n",
1181                        "  -f, --format            Print nice html output\n",
1182                        "  -b, --bodyonly          Don't print html header\n",
1183                        "                          (useful for postprocessing the output)\n",
1184                        "  -c, --checksum          Decode completely even if checksum fails\n",
1185                        "  -x,                     Read data from hexdump files\n",
1186                        "  -h, --help              Display this usage summary\n";
1187                print <<"EOF";
1188
1189Hexdumps can be the output from hexdump, hexdump -C, i2cdump, eeprog and
1190likely many other progams producing hex dumps of one kind or another.  Note
1191that the default output of "hexdump" will be byte-swapped on little-endian
1192systems and will therefore not be parsed correctly.  It is better to use
1193"hexdump -C", which is not ambiguous.
1194EOF
1195                exit;
1196        }
1197
1198        if ($_ eq '-f' || $_ eq '--format') {
1199                $opt_html = 1;
1200                next;
1201        }
1202        if ($_ eq '-b' || $_ eq '--bodyonly') {
1203                $opt_bodyonly = 1;
1204                next;
1205        }
1206        if ($_ eq '-c' || $_ eq '--checksum') {
1207                $opt_igncheck = 1;
1208                next;
1209        }
1210        if ($_ eq '-x') {
1211                $use_hexdump = 1;
1212                next;
1213        }
1214
1215        if (m/^-/) {
1216                print STDERR "Unrecognized option $_\n";
1217                exit;
1218        }
1219
1220        push @dimm_list, $_ if $use_hexdump;
1221}
1222
1223if ($opt_html && !$opt_bodyonly) {
1224        print "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
1225              "<html><head>\n",
1226                  "\t<meta HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=iso-8859-1\">\n",
1227                  "\t<title>PC DIMM Serial Presence Detect Tester/Decoder Output</title>\n",
1228                  "</head><body>\n";
1229}
1230
1231printc "decode-dimms version $revision";
1232printh 'Memory Serial Presence Detect Decoder',
1233'By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner,
1234Jean Delvare, Trent Piepho and others';
1235
1236
1237my $dimm_count = 0;
1238my $dir;
1239if (!$use_hexdump) {
1240        if ($use_sysfs) { $dir = '/sys/bus/i2c/drivers/eeprom'; }
1241        else { $dir = '/proc/sys/dev/sensors'; }
1242        if (-d $dir) {
1243                @dimm_list = split(/\s+/, `ls $dir`);
1244        } elsif (! -d '/sys/module/eeprom') {
1245                print "No EEPROM found, are you sure the eeprom module is loaded?\n";
1246                exit;
1247        }
1248}
1249
1250for my $i ( 0 .. $#dimm_list ) {
1251        $_ = $dimm_list[$i];
1252        if (($use_sysfs && /^\d+-\d+$/)
1253         || (!$use_sysfs && /^eeprom-/)
1254         || $use_hexdump) {
1255                my @bytes = readspd64(0, $dimm_list[$i]);
1256                my $dimm_checksum = 0;
1257                $dimm_checksum += $bytes[$_] foreach (0 .. 62);
1258                $dimm_checksum &= 0xff;
1259
1260                next unless $bytes[63] == $dimm_checksum || $opt_igncheck;
1261                $dimm_count++;
1262
1263                print "<b><u>" if $opt_html;
1264                printl2 "\n\nDecoding EEPROM",
1265                        $use_hexdump ? $dimm_list[$i] : ($use_sysfs ?
1266                        "/sys/bus/i2c/drivers/eeprom/$dimm_list[$i]" :
1267                        "/proc/sys/dev/sensors/$dimm_list[$i]");
1268                print "</u></b>" if $opt_html;
1269                print "<table border=1>\n" if $opt_html;
1270                if (!$use_hexdump) {
1271                        if (($use_sysfs && /^[^-]+-([^-]+)$/)
1272                         || (!$use_sysfs && /^[^-]+-[^-]+-[^-]+-([^-]+)$/)) {
1273                                my $dimm_num = $1 - 49;
1274                                printl "Guessing DIMM is in", "bank $dimm_num";
1275                        }
1276                }
1277
1278# Decode first 3 bytes (0-2)
1279                prints "SPD EEPROM Information";
1280
1281                my $l = "EEPROM Checksum of bytes 0-62";
1282                printl $l, ($bytes[63] == $dimm_checksum ?
1283                        sprintf("OK (0x%.2X)", $bytes[63]):
1284                        sprintf("Bad\n(found 0x%.2X, calculated 0x%.2X)\n",
1285                                $bytes[63], $dimm_checksum));
1286
1287                # Simple heuristic to detect Rambus
1288                my $is_rambus = $bytes[0] < 4;
1289                my $temp;
1290                if ($is_rambus) {
1291                        if ($bytes[0] == 1) { $temp = "0.7"; }
1292                        elsif ($bytes[0] == 2) { $temp = "1.0"; }
1293                        elsif ($bytes[0] == 0 || $bytes[0] == 255) { $temp = "Invalid"; }
1294                        else { $temp = "Reserved"; }
1295                        printl "SPD Revision", $temp;
1296                } else {
1297                        printl "# of bytes written to SDRAM EEPROM",
1298                               $bytes[0];
1299                }
1300
1301                $l = "Total number of bytes in EEPROM";
1302                if ($bytes[1] <= 14) {
1303                        printl $l, 2**$bytes[1];
1304                } elsif ($bytes[1] == 0) {
1305                        printl $l, "RFU";
1306                } else { printl $l, "ERROR!"; }
1307
1308                $l = "Fundamental Memory type";
1309                my $type = "Unknown";
1310                if ($is_rambus) {
1311                        if ($bytes[2] == 1) { $type = "Direct Rambus"; }
1312                        elsif ($bytes[2] == 17) { $type = "Rambus"; }
1313                } else {
1314                        if ($bytes[2] == 1) { $type = "FPM DRAM"; }
1315                        elsif ($bytes[2] == 2) { $type = "EDO"; }
1316                        elsif ($bytes[2] == 3) { $type = "Pipelined Nibble"; }
1317                        elsif ($bytes[2] == 4) { $type = "SDR SDRAM"; }
1318                        elsif ($bytes[2] == 5) { $type = "Multiplexed ROM"; }
1319                        elsif ($bytes[2] == 6) { $type = "DDR SGRAM"; }
1320                        elsif ($bytes[2] == 7) { $type = "DDR SDRAM"; }
1321                        elsif ($bytes[2] == 8) { $type = "DDR2 SDRAM"; }
1322                }
1323                printl $l, $type;
1324
1325# Decode next 61 bytes (3-63, depend on memory type)
1326                $decode_callback{$type}->(\@bytes)
1327                        if exists $decode_callback{$type};
1328
1329# Decode next 35 bytes (64-98, common to all memory types)
1330                prints "Manufacturing Information";
1331
1332                @bytes = readspd64(64, $dimm_list[$i]);
1333
1334                $l = "Manufacturer";
1335                # $extra is a reference to an array containing up to
1336                # 7 extra bytes from the Manufacturer field. Sometimes
1337                # these bytes are filled with interesting data.
1338                ($temp, my $extra) = manufacturer(@bytes[0..7]);
1339                printl $l, $temp;
1340                $l = "Custom Manufacturer Data";
1341                $temp = manufacturer_data(@{$extra});
1342                printl $l, $temp if defined $temp;
1343
1344                if (spd_written($bytes[8])) {
1345                        # Try the location code as ASCII first, as earlier specifications
1346                        # suggested this. As newer specifications don't mention it anymore,
1347                        # we still fall back to binary.
1348                        $l = "Manufacturing Location Code";
1349                        $temp = (chr($bytes[8]) =~ m/^[\w\d]$/) ? chr($bytes[8])
1350                              : sprintf("0x%.2X", $bytes[8]);
1351                        printl $l, $temp;
1352                }
1353
1354                $l = "Part Number";
1355                $temp = part_number(@bytes[9..26]);
1356                printl $l, $temp;
1357
1358                if (spd_written(@bytes[27..28])) {
1359                        $l = "Revision Code";
1360                        $temp = sprintf("0x%02X%02X\n", @bytes[27..28]);
1361                        printl $l, $temp;
1362                }
1363
1364                if (spd_written(@bytes[29..30])) {
1365                        $l = "Manufacturing Date";
1366                        # In theory the year and week are in BCD format, but
1367                        # this is not always true in practice :(
1368                        if (($bytes[29] & 0xf0) <= 0x90
1369                         && ($bytes[29] & 0x0f) <= 0x09
1370                         && ($bytes[30] & 0xf0) <= 0x90
1371                         && ($bytes[30] & 0x0f) <= 0x09) {
1372                                # Note that this heuristic will break in year 2080
1373                                $temp = sprintf("%d%02X-W%02X\n",
1374                                                $bytes[29] >= 0x80 ? 19 : 20,
1375                                                @bytes[29..30]);
1376                        } else {
1377                                $temp = sprintf("0x%02X%02X\n",
1378                                                @bytes[29..30]);
1379                        }
1380                        printl $l, $temp;
1381                }
1382
1383                if (spd_written(@bytes[31..34])) {
1384                        $l = "Assembly Serial Number";
1385                        $temp = sprintf("0x%02X%02X%02X%02X\n",
1386                                        @bytes[31..34]);
1387                        printl $l, $temp;
1388                }
1389
1390# Next 27 bytes (99-125) are manufacturer specific, can't decode
1391
1392# Last 2 bytes (126-127) are reserved, Intel used them as an extension
1393                if ($type eq "SDR SDRAM") {
1394                        decode_intel_spec_freq(\@bytes);
1395                }
1396
1397                print "</table>\n" if $opt_html;
1398        }
1399}
1400printl2 "\n\nNumber of SDRAM DIMMs detected and decoded", $dimm_count;
1401
1402print "</body></html>\n" if ($opt_html && !$opt_bodyonly);
Note: See TracBrowser for help on using the browser.