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

Revision 5156, 46.3 KB (checked in by khali, 5 years ago)

More coding-style fixes.

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