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

Revision 5388, 48.8 KB (checked in by khali, 5 years ago)

Only export the ceil function from POSIX. By default, POSIX exports
everything, and this happens to generate warnings when using perl 5.10.0.
That's a perl bug, but we can still work around it and avoid potential
future issues of the same kind.

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