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

Revision 6224, 80.3 KB (checked in by khali, 2 months ago)

Update my e-mail address and copyright years.

  • 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-2013  Jean Delvare <jdelvare@suse.de>
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 File::Basename;
44use vars qw($opt_html $opt_bodyonly $opt_side_by_side $opt_merge
45            $opt_igncheck $use_sysfs $use_hexdump $sbs_col_width
46            @vendors %decode_callback $revision @dimm $current %hexdump_cache);
47
48use constant LITTLEENDIAN       => "little-endian";
49use constant BIGENDIAN          => "big-endian";
50
51$revision = '$Revision$ ($Date$)';
52$revision =~ s/\$\w+: (.*?) \$/$1/g;
53$revision =~ s/ \([^()]*\)//;
54
55@vendors = (
56["AMD", "AMI", "Fairchild", "Fujitsu",
57 "GTE", "Harris", "Hitachi", "Inmos",
58 "Intel", "I.T.T.", "Intersil", "Monolithic Memories",
59 "Mostek", "Freescale (former Motorola)", "National", "NEC",
60 "RCA", "Raytheon", "Conexant (Rockwell)", "Seeq",
61 "NXP (former Signetics, Philips Semi.)", "Synertek", "Texas Instruments", "Toshiba",
62 "Xicor", "Zilog", "Eurotechnique", "Mitsubishi",
63 "Lucent (AT&T)", "Exel", "Atmel", "SGS/Thomson",
64 "Lattice Semi.", "NCR", "Wafer Scale Integration", "IBM",
65 "Tristar", "Visic", "Intl. CMOS Technology", "SSSI",
66 "MicrochipTechnology", "Ricoh Ltd.", "VLSI", "Micron Technology",
67 "SK Hynix (former Hyundai Electronics)", "OKI Semiconductor", "ACTEL", "Sharp",
68 "Catalyst", "Panasonic", "IDT", "Cypress",
69 "DEC", "LSI Logic", "Zarlink (former Plessey)", "UTMC",
70 "Thinking Machine", "Thomson CSF", "Integrated CMOS (Vertex)", "Honeywell",
71 "Tektronix", "Oracle Corporation (former Sun Microsystems)", "Silicon Storage Technology", "ProMos/Mosel Vitelic",
72 "Infineon (former Siemens)", "Macronix", "Xerox", "Plus Logic",
73 "SunDisk", "Elan Circuit Tech.", "European Silicon Str.", "Apple Computer",
74 "Xilinx", "Compaq", "Protocol Engines", "SCI",
75 "Seiko Instruments", "Samsung", "I3 Design System", "Klic",
76 "Crosspoint Solutions", "Alliance Semiconductor", "Tandem", "Hewlett-Packard",
77 "Integrated Silicon Solutions", "Brooktree", "New Media", "MHS Electronic",
78 "Performance Semi.", "Winbond Electronic", "Kawasaki Steel", "Bright Micro",
79 "TECMAR", "Exar", "PCMCIA", "LG Semi (former Goldstar)",
80 "Northern Telecom", "Sanyo", "Array Microsystems", "Crystal Semiconductor",
81 "Analog Devices", "PMC-Sierra", "Asparix", "Convex Computer",
82 "Quality Semiconductor", "Nimbus Technology", "Transwitch", "Micronas (ITT Intermetall)",
83 "Cannon", "Altera", "NEXCOM", "QUALCOMM",
84 "Sony", "Cray Research", "AMS(Austria Micro)", "Vitesse",
85 "Aster Electronics", "Bay Networks (Synoptic)", "Zentrum or ZMD", "TRW",
86 "Thesys", "Solbourne Computer", "Allied-Signal", "Dialog",
87 "Media Vision", "Numonyx Corporation (former Level One Communication)"],
88["Cirrus Logic", "National Instruments", "ILC Data Device", "Alcatel Mietec",
89 "Micro Linear", "Univ. of NC", "JTAG Technologies", "BAE Systems",
90 "Nchip", "Galileo Tech", "Bestlink Systems", "Graychip",
91 "GENNUM", "VideoLogic", "Robert Bosch", "Chip Express",
92 "DATARAM", "United Microelec Corp.", "TCSI", "Smart Modular",
93 "Hughes Aircraft", "Lanstar Semiconductor", "Qlogic", "Kingston",
94 "Music Semi", "Ericsson Components", "SpaSE", "Eon Silicon Devices",
95 "Programmable Micro Corp", "DoD", "Integ. Memories Tech.", "Corollary Inc.",
96 "Dallas Semiconductor", "Omnivision", "EIV(Switzerland)", "Novatel Wireless",
97 "Zarlink (former Mitel)", "Clearpoint", "Cabletron", "STEC (former Silicon Technology)",
98 "Vanguard", "Hagiwara Sys-Com", "Vantis", "Celestica",
99 "Century", "Hal Computers", "Rohm Company Ltd.", "Juniper Networks",
100 "Libit Signal Processing", "Mushkin Enhanced Memory", "Tundra Semiconductor", "Adaptec Inc.",
101 "LightSpeed Semi.", "ZSP Corp.", "AMIC Technology", "Adobe Systems",
102 "Dynachip", "PNY Electronics", "Newport Digital", "MMC Networks",
103 "T Square", "Seiko Epson", "Broadcom", "Viking Components",
104 "V3 Semiconductor", "Flextronics (former Orbit)", "Suwa Electronics", "Transmeta",
105 "Micron CMS", "American Computer & Digital Components Inc", "Enhance 3000 Inc", "Tower Semiconductor",
106 "CPU Design", "Price Point", "Maxim Integrated Product", "Tellabs",
107 "Centaur Technology", "Unigen Corporation", "Transcend Information", "Memory Card Technology",
108 "CKD Corporation Ltd.", "Capital Instruments, Inc.", "Aica Kogyo, Ltd.", "Linvex Technology",
109 "MSC Vertriebs GmbH", "AKM Company, Ltd.", "Dynamem, Inc.", "NERA ASA",
110 "GSI Technology", "Dane-Elec (C Memory)", "Acorn Computers", "Lara Technology",
111 "Oak Technology, Inc.", "Itec Memory", "Tanisys Technology", "Truevision",
112 "Wintec Industries", "Super PC Memory", "MGV Memory", "Galvantech",
113 "Gadzoox Nteworks", "Multi Dimensional Cons.", "GateField", "Integrated Memory System",
114 "Triscend", "XaQti", "Goldenram", "Clear Logic",
115 "Cimaron Communications", "Nippon Steel Semi. Corp.", "Advantage Memory", "AMCC",
116 "LeCroy", "Yamaha Corporation", "Digital Microwave", "NetLogic Microsystems",
117 "MIMOS Semiconductor", "Advanced Fibre", "BF Goodrich Data.", "Epigram",
118 "Acbel Polytech Inc.", "Apacer Technology", "Admor Memory", "FOXCONN",
119 "Quadratics Superconductor", "3COM"],
120["Camintonn Corporation", "ISOA Incorporated", "Agate Semiconductor", "ADMtek Incorporated",
121 "HYPERTEC", "Adhoc Technologies", "MOSAID Technologies", "Ardent Technologies",
122 "Switchcore", "Cisco Systems, Inc.", "Allayer Technologies", "WorkX AG (Wichman)",
123 "Oasis Semiconductor", "Novanet Semiconductor", "E-M Solutions", "Power General",
124 "Advanced Hardware Arch.", "Inova Semiconductors GmbH", "Telocity", "Delkin Devices",
125 "Symagery Microsystems", "C-Port Corporation", "SiberCore Technologies", "Southland Microsystems",
126 "Malleable Technologies", "Kendin Communications", "Great Technology Microcomputer", "Sanmina Corporation",
127 "HADCO Corporation", "Corsair", "Actrans System Inc.", "ALPHA Technologies",
128 "Silicon Laboratories, Inc. (Cygnal)", "Artesyn Technologies", "Align Manufacturing", "Peregrine Semiconductor",
129 "Chameleon Systems", "Aplus Flash Technology", "MIPS Technologies", "Chrysalis ITS",
130 "ADTEC Corporation", "Kentron Technologies", "Win Technologies", "Tachyon Semiconductor (former ASIC Designs Inc.)",
131 "Extreme Packet Devices", "RF Micro Devices", "Siemens AG", "Sarnoff Corporation",
132 "Itautec SA (former Itautec Philco SA)", "Radiata Inc.", "Benchmark Elect. (AVEX)", "Legend",
133 "SpecTek Incorporated", "Hi/fn", "Enikia Incorporated", "SwitchOn Networks",
134 "AANetcom Incorporated", "Micro Memory Bank", "ESS Technology", "Virata Corporation",
135 "Excess Bandwidth", "West Bay Semiconductor", "DSP Group", "Newport Communications",
136 "Chip2Chip Incorporated", "Phobos Corporation", "Intellitech Corporation", "Nordic VLSI ASA",
137 "Ishoni Networks", "Silicon Spice", "Alchemy Semiconductor", "Agilent Technologies",
138 "Centillium Communications", "W.L. Gore", "HanBit Electronics", "GlobeSpan",
139 "Element 14", "Pycon", "Saifun Semiconductors", "Sibyte, Incorporated",
140 "MetaLink Technologies", "Feiya Technology", "I & C Technology", "Shikatronics",
141 "Elektrobit", "Megic", "Com-Tier", "Malaysia Micro Solutions",
142 "Hyperchip", "Gemstone Communications", "Anadigm (former Anadyne)", "3ParData",
143 "Mellanox Technologies", "Tenx Technologies", "Helix AG", "Domosys",
144 "Skyup Technology", "HiNT Corporation", "Chiaro", "MDT Technologies GmbH (former MCI Computer GMBH)",
145 "Exbit Technology A/S", "Integrated Technology Express", "AVED Memory", "Legerity",
146 "Jasmine Networks", "Caspian Networks", "nCUBE", "Silicon Access Networks",
147 "FDK Corporation", "High Bandwidth Access", "MultiLink Technology", "BRECIS",
148 "World Wide Packets", "APW", "Chicory Systems", "Xstream Logic",
149 "Fast-Chip", "Zucotto Wireless", "Realchip", "Galaxy Power",
150 "eSilicon", "Morphics Technology", "Accelerant Networks", "Silicon Wave",
151 "SandCraft", "Elpida"],
152["Solectron", "Optosys Technologies", "Buffalo (former Melco)", "TriMedia Technologies",
153 "Cyan Technologies", "Global Locate", "Optillion", "Terago Communications",
154 "Ikanos Communications", "Princeton Technology", "Nanya Technology", "Elite Flash Storage",
155 "Mysticom", "LightSand Communications", "ATI Technologies", "Agere Systems",
156 "NeoMagic", "AuroraNetics", "Golden Empire", "Mushkin",
157 "Tioga Technologies", "Netlist", "TeraLogic", "Cicada Semiconductor",
158 "Centon Electronics", "Tyco Electronics", "Magis Works", "Zettacom",
159 "Cogency Semiconductor", "Chipcon AS", "Aspex Technology", "F5 Networks",
160 "Programmable Silicon Solutions", "ChipWrights", "Acorn Networks", "Quicklogic",
161 "Kingmax Semiconductor", "BOPS", "Flasys", "BitBlitz Communications",
162 "eMemory Technology", "Procket Networks", "Purple Ray", "Trebia Networks",
163 "Delta Electronics", "Onex Communications", "Ample Communications", "Memory Experts Intl",
164 "Astute Networks", "Azanda Network Devices", "Dibcom", "Tekmos",
165 "API NetWorks", "Bay Microsystems", "Firecron Ltd", "Resonext Communications",
166 "Tachys Technologies", "Equator Technology", "Concept Computer", "SILCOM",
167 "3Dlabs", "c't Magazine", "Sanera Systems", "Silicon Packets",
168 "Viasystems Group", "Simtek", "Semicon Devices Singapore", "Satron Handelsges",
169 "Improv Systems", "INDUSYS GmbH", "Corrent", "Infrant Technologies",
170 "Ritek Corp", "empowerTel Networks", "Hypertec", "Cavium Networks",
171 "PLX Technology", "Massana Design", "Intrinsity", "Valence Semiconductor",
172 "Terawave Communications", "IceFyre Semiconductor", "Primarion", "Picochip Designs Ltd",
173 "Silverback Systems", "Jade Star Technologies", "Pijnenburg Securealink",
174 "takeMS - Ultron AG (former Memorysolution GmbH)", "Cambridge Silicon Radio",
175 "Swissbit", "Nazomi Communications", "eWave System",
176 "Rockwell Collins", "Picocel Co., Ltd.", "Alphamosaic Ltd", "Sandburst",
177 "SiCon Video", "NanoAmp Solutions", "Ericsson Technology", "PrairieComm",
178 "Mitac International", "Layer N Networks", "MtekVision", "Allegro Networks",
179 "Marvell Semiconductors", "Netergy Microelectronic", "NVIDIA", "Internet Machines",
180 "Peak Electronics", "Litchfield Communication", "Accton Technology", "Teradiant Networks",
181 "Scaleo Chip (former Europe Technologies)", "Cortina Systems", "RAM Components", "Raqia Networks",
182 "ClearSpeed", "Matsushita Battery", "Xelerated", "SimpleTech",
183 "Utron Technology", "Astec International", "AVM gmbH", "Redux Communications",
184 "Dot Hill Systems", "TeraChip"],
185["T-RAM Incorporated", "Innovics Wireless", "Teknovus", "KeyEye Communications",
186 "Runcom Technologies", "RedSwitch", "Dotcast", "Silicon Mountain Memory",
187 "Signia Technologies", "Pixim", "Galazar Networks", "White Electronic Designs",
188 "Patriot Scientific", "Neoaxiom Corporation", "3Y Power Technology", "Scaleo Chip (former Europe Technologies)",
189 "Potentia Power Systems", "C-guys Incorporated", "Digital Communications Technology Incorporated", "Silicon-Based Technology",
190 "Fulcrum Microsystems", "Positivo Informatica Ltd", "XIOtech Corporation", "PortalPlayer",
191 "Zhiying Software", "Parker Vision, Inc. (former Direct2Data)", "Phonex Broadband", "Skyworks Solutions",
192 "Entropic Communications", "Pacific Force Technology", "Zensys A/S", "Legend Silicon Corp.",
193 "sci-worx GmbH", "SMSC (former Oasis Silicon Systems)", "Renesas Electronics (former Renesas Technology)", "Raza Microelectronics",
194 "Phyworks", "MediaTek", "Non-cents Productions", "US Modular",
195 "Wintegra Ltd", "Mathstar", "StarCore", "Oplus Technologies",
196 "Mindspeed", "Just Young Computer", "Radia Communications", "OCZ",
197 "Emuzed", "LOGIC Devices", "Inphi Corporation", "Quake Technologies",
198 "Vixel", "SolusTek", "Kongsberg Maritime", "Faraday Technology",
199 "Altium Ltd.", "Insyte", "ARM Ltd.", "DigiVision",
200 "Vativ Technologies", "Endicott Interconnect Technologies", "Pericom", "Bandspeed",
201 "LeWiz Communications", "CPU Technology", "Ramaxel Technology", "DSP Group",
202 "Axis Communications", "Legacy Electronics", "Chrontel", "Powerchip Semiconductor",
203 "MobilEye Technologies", "Excel Semiconductor", "A-DATA Technology", "VirtualDigm",
204 "G Skill Intl", "Quanta Computer", "Yield Microelectronics", "Afa Technologies",
205 "KINGBOX Technology Co. Ltd.", "Ceva", "iStor Networks", "Advance Modules",
206 "Microsoft", "Open-Silicon", "Goal Semiconductor", "ARC International",
207 "Simmtec", "Metanoia", "Key Stream", "Lowrance Electronics",
208 "Adimos", "SiGe Semiconductor", "Fodus Communications", "Credence Systems Corp.",
209 "Genesis Microchip Inc.", "Vihana, Inc.", "WIS Technologies", "GateChange Technologies",
210 "High Density Devices AS", "Synopsys", "Gigaram", "Enigma Semiconductor Inc.",
211 "Century Micro Inc.", "Icera Semiconductor", "Mediaworks Integrated Systems", "O'Neil Product Development",
212 "Supreme Top Technology Ltd.", "MicroDisplay Corporation", "Team Group Inc.", "Sinett Corporation",
213 "Toshiba Corporation", "Tensilica", "SiRF Technology", "Bacoc Inc.",
214 "SMaL Camera Technologies", "Thomson SC", "Airgo Networks", "Wisair Ltd.",
215 "SigmaTel", "Arkados", "Compete IT gmbH Co. KG", "Eudar Technology Inc.",
216 "Focus Enhancements", "Xyratex"],
217["Specular Networks", "Patriot Memory", "U-Chip Technology Corp.", "Silicon Optix",
218 "Greenfield Networks", "CompuRAM GmbH", "Stargen, Inc.", "NetCell Corporation",
219 "Excalibrus Technologies Ltd", "SCM Microsystems", "Xsigo Systems, Inc.", "CHIPS & Systems Inc",
220 "Tier 1 Multichip Solutions", "CWRL Labs", "Teradici", "Gigaram, Inc.",
221 "g2 Microsystems", "PowerFlash Semiconductor", "P.A. Semi, Inc.", "NovaTech Solutions, S.A.",
222 "c2 Microsystems, Inc.", "Level5 Networks", "COS Memory AG", "Innovasic Semiconductor",
223 "02IC Co. Ltd", "Tabula, Inc.", "Crucial Technology", "Chelsio Communications",
224 "Solarflare Communications", "Xambala Inc.", "EADS Astrium", "Terra Semiconductor Inc. (former ATO Semicon Co. Ltd.)",
225 "Imaging Works, Inc.", "Astute Networks, Inc.", "Tzero", "Emulex",
226 "Power-One", "Pulse~LINK Inc.", "Hon Hai Precision Industry", "White Rock Networks Inc.",
227 "Telegent Systems USA, Inc.", "Atrua Technologies, Inc.", "Acbel Polytech Inc.",
228 "eRide Inc.","ULi Electronics Inc.", "Magnum Semiconductor Inc.", "neoOne Technology, Inc.",
229 "Connex Technology, Inc.", "Stream Processors, Inc.", "Focus Enhancements", "Telecis Wireless, Inc.",
230 "uNav Microelectronics", "Tarari, Inc.", "Ambric, Inc.", "Newport Media, Inc.", "VMTS",
231 "Enuclia Semiconductor, Inc.", "Virtium Technology Inc.", "Solid State System Co., Ltd.", "Kian Tech LLC",
232 "Artimi", "Power Quotient International", "Avago Technologies", "ADTechnology", "Sigma Designs",
233 "SiCortex, Inc.", "Ventura Technology Group", "eASIC", "M.H.S. SAS", "Micro Star International",
234 "Rapport Inc.", "Makway International", "Broad Reach Engineering Co.",
235 "Semiconductor Mfg Intl Corp", "SiConnect", "FCI USA Inc.", "Validity Sensors",
236 "Coney Technology Co. Ltd.", "Spans Logic", "Neterion Inc.", "Qimonda",
237 "New Japan Radio Co. Ltd.", "Velogix", "Montalvo Systems", "iVivity Inc.", "Walton Chaintech",
238 "AENEON", "Lorom Industrial Co. Ltd.", "Radiospire Networks", "Sensio Technologies, Inc.",
239 "Nethra Imaging", "Hexon Technology Pte Ltd", "CompuStocx (CSX)", "Methode Electronics, Inc.",
240 "Connect One Ltd.", "Opulan Technologies", "Septentrio NV", "Goldenmars Technology Inc.",
241 "Kreton Corporation", "Cochlear Ltd.", "Altair Semiconductor", "NetEffect, Inc.",
242 "Spansion, Inc.", "Taiwan Semiconductor Mfg", "Emphany Systems Inc.",
243 "ApaceWave Technologies", "Mobilygen Corporation", "Tego", "Cswitch Corporation",
244 "Haier (Beijing) IC Design Co.", "MetaRAM", "Axel Electronics Co. Ltd.", "Tilera Corporation",
245 "Aquantia", "Vivace Semiconductor", "Redpine Signals", "Octalica", "InterDigital Communications",
246 "Avant Technology", "Asrock, Inc.", "Availink", "Quartics, Inc.", "Element CXI",
247 "Innovaciones Microelectronicas", "VeriSilicon Microelectronics", "W5 Networks"],
248["MOVEKING", "Mavrix Technology, Inc.", "CellGuide Ltd.", "Faraday Technology",
249 "Diablo Technologies, Inc.", "Jennic", "Octasic", "Molex Incorporated", "3Leaf Networks",
250 "Bright Micron Technology", "Netxen", "NextWave Broadband Inc.", "DisplayLink", "ZMOS Technology",
251 "Tec-Hill", "Multigig, Inc.", "Amimon", "Euphonic Technologies, Inc.", "BRN Phoenix",
252 "InSilica", "Ember Corporation", "Avexir Technologies Corporation", "Echelon Corporation",
253 "Edgewater Computer Systems", "XMOS Semiconductor Ltd.", "GENUSION, Inc.", "Memory Corp NV",
254 "SiliconBlue Technologies", "Rambus Inc.", "Andes Technology Corporation", "Coronis Systems",
255 "Achronix Semiconductor", "Siano Mobile Silicon Ltd.", "Semtech Corporation", "Pixelworks Inc.",
256 "Gaisler Research AB", "Teranetics", "Toppan Printing Co. Ltd.", "Kingxcon",
257 "Silicon Integrated Systems", "I-O Data Device, Inc.", "NDS Americas Inc.", "Solomon Systech Limited",
258 "On Demand Microelectronics", "Amicus Wireless Inc.", "SMARDTV SNC", "Comsys Communication Ltd.",
259 "Movidia Ltd.", "Javad GNSS, Inc.", "Montage Technology Group", "Trident Microsystems", "Super Talent",
260 "Optichron, Inc.", "Future Waves UK Ltd.", "SiBEAM, Inc.", "Inicore, Inc.", "Virident Systems",
261 "M2000, Inc.", "ZeroG Wireless, Inc.", "Gingle Technology Co. Ltd.", "Space Micro Inc.", "Wilocity",
262 "Novafora, Inc.", "iKoa Corporation", "ASint Technology", "Ramtron", "Plato Networks Inc.",
263 "IPtronics AS", "Infinite-Memories", "Parade Technologies Inc.", "Dune Networks",
264 "GigaDevice Semiconductor", "Modu Ltd.", "CEITEC", "Northrop Grumman", "XRONET Corporation",
265 "Sicon Semiconductor AB", "Atla Electronics Co. Ltd.", "TOPRAM Technology", "Silego Technology Inc.",
266 "Kinglife", "Ability Industries Ltd.", "Silicon Power Computer & Communications",
267 "Augusta Technology, Inc.", "Nantronics Semiconductors", "Hilscher Gesellschaft", "Quixant Ltd.",
268 "Percello Ltd.", "NextIO Inc.", "Scanimetrics Inc.", "FS-Semi Company Ltd.", "Infinera Corporation",
269 "SandForce Inc.", "Lexar Media", "Teradyne Inc.", "Memory Exchange Corp.", "Suzhou Smartek Electronics",
270 "Avantium Corporation", "ATP Electronics Inc.", "Valens Semiconductor Ltd", "Agate Logic, Inc.",
271 "Netronome", "Zenverge, Inc.", "N-trig Ltd", "SanMax Technologies Inc.", "Contour Semiconductor Inc.",
272 "TwinMOS", "Silicon Systems, Inc.", "V-Color Technology Inc.", "Certicom Corporation", "JSC ICC Milandr",
273 "PhotoFast Global Inc.", "InnoDisk Corporation", "Muscle Power", "Energy Micro", "Innofidei",
274 "CopperGate Communications", "Holtek Semiconductor Inc.", "Myson Century, Inc.", "FIDELIX",
275 "Red Digital Cinema", "Densbits Technology", "Zempro", "MoSys", "Provigent", "Triad Semiconductor, Inc."],
276["Siklu Communication Ltd.", "A Force Manufacturing Ltd.", "Strontium", "Abilis Systems", "Siglead, Inc.",
277 "Ubicom, Inc.", "Unifosa Corporation", "Stretch, Inc.", "Lantiq Deutschland GmbH", "Visipro",
278 "EKMemory", "Microelectronics Institute ZTE", "Cognovo Ltd.", "Carry Technology Co. Ltd.", "Nokia",
279 "King Tiger Technology", "Sierra Wireless", "HT Micron", "Albatron Technology Co. Ltd.",
280 "Leica Geosystems AG", "BroadLight", "AEXEA", "ClariPhy Communications, Inc.", "Green Plug",
281 "Design Art Networks", "Mach Xtreme Technology Ltd.", "ATO Solutions Co. Ltd.", "Ramsta",
282 "Greenliant Systems, Ltd.", "Teikon", "Antec Hadron", "NavCom Technology, Inc.",
283 "Shanghai Fudan Microelectronics", "Calxeda, Inc.", "JSC EDC Electronics", "Kandit Technology Co. Ltd.",
284 "Ramos Technology", "Goldenmars Technology", "XeL Technology Inc.", "Newzone Corporation",
285 "ShenZhen MercyPower Tech", "Nanjing Yihuo Technology", "Nethra Imaging Inc.", "SiTel Semiconductor BV",
286 "SolidGear Corporation", "Topower Computer Ind Co Ltd.", "Wilocity", "Profichip GmbH",
287 "Gerad Technologies", "Ritek Corporation", "Gomos Technology Limited", "Memoright Corporation",
288 "D-Broad, Inc.", "HiSilicon Technologies", "Syndiant Inc.", "Enverv Inc.", "Cognex",
289 "Xinnova Technology Inc.", "Ultron AG", "Concord Idea Corporation", "AIM Corporation",
290 "Lifetime Memory Products", "Ramsway", "Recore Systems BV", "Haotian Jinshibo Science Tech",
291 "Being Advanced Memory", "Adesto Technologies", "Giantec Semiconductor, Inc.", "HMD Electronics AG",
292 "Gloway International (HK)", "Kingcore", "Anucell Technology Holding",
293 "Accord Software & Systems Pvt. Ltd.", "Active-Semi Inc.", "Denso Corporation", "TLSI Inc.",
294 "Shenzhen Daling Electronic Co. Ltd.", "Mustang", "Orca Systems", "Passif Semiconductor",
295 "GigaDevice Semiconductor (Beijing) Inc.", "Memphis Electronic", "Beckhoff Automation GmbH",
296 "Harmony Semiconductor Corp (former ProPlus Design Solutions)", "Air Computers SRL", "TMT Memory",
297 "Eorex Corporation", "Xingtera", "Netsol", "Bestdon Technology Co. Ltd.", "Baysand Inc.",
298 "Uroad Technology Co. Ltd. (former Triple Grow Industrial Ltd.)", "Wilk Elektronik S.A.",
299 "AAI", "Harman", "Berg Microelectronics Inc.", "ASSIA, Inc.", "Visiontek Products LLC",
300 "OCMEMORY", "Welink Solution Inc.", "Shark Gaming", "Avalanche Technology",
301 "R&D Center ELVEES OJSC", "KingboMars Technology Co. Ltd.",
302 "High Bridge Solutions Industria Eletronica", "Transcend Technology Co. Ltd.",
303 "Everspin Technologies", "Hon-Hai Precision", "Smart Storage Systems", "Toumaz Group",
304 "Zentel Electronics Corporation", "Panram International Corporation",
305 "Silicon Space Technology"]
306);
307
308$use_sysfs = -d '/sys/bus';
309
310# We consider that no data was written to this area of the SPD EEPROM if
311# all bytes read 0x00 or all bytes read 0xff
312sub spd_written(@)
313{
314        my $all_00 = 1;
315        my $all_ff = 1;
316
317        foreach my $b (@_) {
318                $all_00 = 0 unless $b == 0x00;
319                $all_ff = 0 unless $b == 0xff;
320                return 1 unless $all_00 or $all_ff;
321        }
322
323        return 0;
324}
325
326sub parity($)
327{
328        my $n = shift;
329        my $parity = 0;
330
331        while ($n) {
332                $parity++ if ($n & 1);
333                $n >>= 1;
334        }
335
336        return ($parity & 1);
337}
338
339# New encoding format (as of DDR3) for manufacturer just has a count of
340# leading 0x7F rather than all the individual bytes.  The count bytes includes
341# parity!
342sub manufacturer_ddr3($$)
343{
344        my ($count, $code) = @_;
345        my $manufacturer;
346
347        return "Invalid" if parity($code) != 1;
348        return "Unknown" if ($code & 0x7F) - 1 > $vendors[$count & 0x7F];
349        $manufacturer = $vendors[$count & 0x7F][($code & 0x7F) - 1];
350        $manufacturer =~ s/ \(former .*\)$// if $opt_side_by_side;
351        $manufacturer .= "? (Invalid parity)" if parity($count) != 1;
352        return $manufacturer;
353}
354
355sub manufacturer(@)
356{
357        my @bytes = @_;
358        my $ai = 0;
359        my $first;
360
361        return ("Undefined", []) unless spd_written(@bytes);
362
363        while (defined($first = shift(@bytes)) && $first == 0x7F) {
364                $ai++;
365        }
366
367        return ("Invalid", []) unless defined $first;
368        return ("Invalid", [$first, @bytes]) if parity($first) != 1;
369        if (parity($ai) == 0) {
370                $ai |= 0x80;
371        }
372        return (manufacturer_ddr3($ai, $first), \@bytes);
373}
374
375sub manufacturer_data(@)
376{
377        my $hex = "";
378        my $asc = "";
379
380        return unless spd_written(@_);
381
382        foreach my $byte (@_) {
383                $hex .= sprintf("\%02X ", $byte);
384                $asc .= ($byte >= 32 && $byte < 127) ? chr($byte) : '?';
385        }
386
387        return "$hex(\"$asc\")";
388}
389
390sub part_number(@)
391{
392        my $asc = "";
393        my $byte;
394
395        while (defined ($byte = shift) && $byte >= 32 && $byte < 127) {
396                $asc .= chr($byte);
397        }
398
399        return ($asc eq "") ? "Undefined" : $asc;
400}
401
402sub cas_latencies(@)
403{
404        return "None" unless @_;
405        return join ', ', map("${_}T", sort { $b <=> $a } @_);
406}
407
408# Real printing functions
409
410sub html_encode($)
411{
412        my $text = shift;
413        $text =~ s/</\&lt;/sg;
414        $text =~ s/>/\&gt;/sg;
415        $text =~ s/ degrees C/\&deg;C/sg;
416        $text =~ s/\n/<br\/>\n/sg;
417        return $text;
418}
419
420sub same_values(@)
421{
422        my $value = shift;
423        while (@_) {
424                return 0 unless $value eq shift;
425        }
426        return 1;
427}
428
429sub real_printl($$) # print a line w/ label and values
430{
431        my ($label, @values) = @_;
432        local $_;
433        my $same_values = same_values(@values);
434
435        # If all values are N/A, don't bother printing
436        return if $values[0] eq "N/A" and $same_values;
437
438        if ($opt_html) {
439                $label = html_encode($label);
440                @values = map { html_encode($_) } @values;
441                print "<tr><td style=\"vertical-align: top;\">$label</td>";
442                if (!$opt_merge) {
443                        print "<td>$_</td>" foreach @values;
444                } elsif ($same_values) {
445                        print "<td colspan=\"".(scalar @values)."\">$values[0]</td>";
446                } else {
447                        # For HTML output, merge adjacent cells even if
448                        # the whole line cannot be merged.
449                        my $colcnt = 0;
450                        while (@values) {
451                                $colcnt++;
452                                my $value = shift @values;
453                                next if (@values && $value eq $values[0]);
454                                print "<td" . ($colcnt > 1 ? " colspan=\"$colcnt\"" : "") .">$value</td>";
455                                $colcnt = 0;
456                        }
457                }
458                print "</tr>\n";
459        } else {
460                if ($opt_merge && $same_values) {
461                        splice(@values, 1);
462                }
463
464                my $format = "%-47s".(("  %-".$sbs_col_width."s") x (scalar @values - 1))."  %s\n";
465                my $maxl = 0; # Keep track of the max number of lines
466
467                # It's a bit tricky because each value may span over more than
468                # one line. We can easily extract the values per column, but
469                # we need them per line at printing time. So we have to
470                # prepare a 2D array with all the individual string fragments.
471                my ($col, @lines);
472                for ($col = 0; $col < @values; $col++) {
473                        my @cells = split /\n/, $values[$col];
474                        $maxl = @cells if @cells > $maxl;
475                        for (my $l = 0; $l < @cells; $l++) {
476                                $lines[$l]->[$col] = $cells[$l];
477                        }
478                }
479
480                # Also make sure there are no holes in the array
481                for (my $l = 0; $l < $maxl; $l++) {
482                        for ($col = 0; $col < @values; $col++) {
483                                $lines[$l]->[$col] = ""
484                                        if not defined $lines[$l]->[$col];
485                        }
486                }
487
488                printf $format, $label, @{shift @lines};
489                printf $format, "", @{$_} foreach (@lines);
490        }
491}
492
493sub printl2 # print a line w/ label and value (outside a table)
494{
495        my ($label, $value, $style) = @_;
496        if ($opt_html) {
497                $label = html_encode($label);
498                $value = html_encode($value);
499                print "<p", (defined $style ? " style=\"$style\"" : ""), ">";
500        }
501        print "$label: $value\n";
502        print "</p>\n" if $opt_html;
503}
504
505sub real_prints($) # print separator w/ given text
506{
507        my ($label, $ncol) = @_;
508        $ncol = 1 unless $ncol;
509        if ($opt_html) {
510                $label = html_encode($label);
511                print "<tr><td style=\"font-weight: bold; text-align: center;\" colspan=\"".(1+$ncol)."\">$label</td></tr>\n";
512        } else {
513                print "\n---=== $label ===---\n";
514        }
515}
516
517sub printh($$) # print header w/ given text
518{
519        my ($header, $sub) = @_;
520        if ($opt_html) {
521                $header = html_encode($header);
522                $sub = html_encode($sub);
523                print "<h1>$header</h1>\n";
524                print "<p>$sub</p>\n";
525        } else {
526                print "\n$header\n$sub\n";
527        }
528}
529
530sub printc($) # print comment
531{
532        my ($comment) = @_;
533        if ($opt_html) {
534                $comment = html_encode($comment);
535                print "<!-- $comment -->\n";
536        } else {
537                print "# $comment\n";
538        }
539}
540
541# Fake printing functions
542# These don't actually print anything, instead they store the desired
543# output for later processing.
544
545sub printl($$) # print a line w/ label and value
546{
547        my @output = (\&real_printl, @_);
548        push @{$dimm[$current]->{output}}, \@output;
549}
550
551sub printl_cond($$$) # same as printl but conditional
552{
553        my ($cond, $label, $value) = @_;
554        return unless $cond || $opt_side_by_side;
555        printl($label, $cond ? $value : "N/A");
556}
557
558sub prints($) # print separator w/ given text
559{
560        my @output = (\&real_prints, @_);
561        push @{$dimm[$current]->{output}}, \@output;
562}
563
564# Helper functions
565
566sub tns1($) # print a time in ns, with 1 decimal digit
567{
568        return sprintf("%.1f ns", $_[0]);
569}
570
571sub tns($) # print a time in ns, with 2 decimal digits
572{
573        return sprintf("%3.2f ns", $_[0]);
574}
575
576sub tns3($) # print a time in ns, with 3 decimal digits
577{
578        return sprintf("%.3f ns", $_[0]);
579}
580
581sub value_or_undefined
582{
583        my ($value, $unit) = @_;
584        return "Undefined!" unless $value;
585        $value .= " $unit" if defined $unit;
586        return $value;
587}
588
589# Common to SDR, DDR and DDR2 SDRAM
590sub sdram_voltage_interface_level($)
591{
592        my @levels = (
593                "TTL (5V tolerant)",            #  0
594                "LVTTL (not 5V tolerant)",      #  1
595                "HSTL 1.5V",                    #  2
596                "SSTL 3.3V",                    #  3
597                "SSTL 2.5V",                    #  4
598                "SSTL 1.8V",                    #  5
599        );
600       
601        return ($_[0] < @levels) ? $levels[$_[0]] : "Undefined!";
602}
603
604# Common to SDR, DDR and DDR2 SDRAM
605sub sdram_module_configuration_type($)
606{
607        my $byte = $_[0] & 0x07;
608        my @edc;
609
610        return "No Parity" if $byte == 0;
611
612        # Data ECC includes Data Parity so don't print both
613        push @edc, "Data Parity" if ($byte & 0x03) == 0x01;
614        push @edc, "Data ECC" if ($byte & 0x02);
615        # New in DDR2 specification
616        push @edc, "Address/Command Parity" if ($byte & 0x04);
617
618        return join ", ", @edc;
619}
620
621# Parameter: EEPROM bytes 0-127 (using 3-62)
622sub decode_sdr_sdram($)
623{
624        my $bytes = shift;
625        my $temp;
626        my ($ctime, $ctime1, $ctime2, $ctime_min);
627
628# SPD revision
629        # Starting with SPD revision 1.2, this byte is encoded in BCD
630        printl("SPD Revision", $bytes->[62] < 0x12 ? $bytes->[62] :
631                ($bytes->[62] >> 4) . "." . ($bytes->[62] & 0xf));
632
633#size computation
634
635        prints("Memory Characteristics");
636
637        my $k = 0;
638        my $ii = 0;
639
640        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
641        if (($bytes->[5] <= 8) && ($bytes->[17] <= 8)) {
642                 $k = $bytes->[5] * $bytes->[17];
643        }
644
645        if ($ii > 0 && $ii <= 12 && $k > 0) {
646                printl("Size", ((1 << $ii) * $k) . " MB");
647        } else {
648                printl("Size", "INVALID: " . $bytes->[3] . "," . $bytes->[4] . "," .
649                               $bytes->[5] . "," . $bytes->[17]);
650        }
651
652        my @cas;
653        for ($ii = 0; $ii < 7; $ii++) {
654                push(@cas, $ii + 1) if ($bytes->[18] & (1 << $ii));
655        }
656
657        my $trcd;
658        my $trp;
659        my $tras;
660        $ctime_min = $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
661
662        $trcd = $bytes->[29];
663        $trp = $bytes->[27];
664        $tras = $bytes->[30];
665
666        printl("tCL-tRCD-tRP-tRAS",
667                $cas[$#cas] . "-" .
668                ceil($trcd/$ctime) . "-" .
669                ceil($trp/$ctime) . "-" .
670                ceil($tras/$ctime));
671
672        if ($bytes->[3] == 0) { $temp = "Undefined!"; }
673        elsif ($bytes->[3] == 1) { $temp = "1/16"; }
674        elsif ($bytes->[3] == 2) { $temp = "2/17"; }
675        elsif ($bytes->[3] == 3) { $temp = "3/18"; }
676        else { $temp = $bytes->[3]; }
677        printl("Number of Row Address Bits", $temp);
678
679        if ($bytes->[4] == 0) { $temp = "Undefined!"; }
680        elsif ($bytes->[4] == 1) { $temp = "1/16"; }
681        elsif ($bytes->[4] == 2) { $temp = "2/17"; }
682        elsif ($bytes->[4] == 3) { $temp = "3/18"; }
683        else { $temp = $bytes->[4]; }
684        printl("Number of Col Address Bits", $temp);
685
686        printl("Number of Module Rows", value_or_undefined($bytes->[5]));
687
688        if ($bytes->[7] > 1) { $temp = "Undefined!"; }
689        else { $temp = ($bytes->[7] * 256) + $bytes->[6]; }
690        printl("Data Width", $temp);
691
692        printl("Voltage Interface Level",
693               sdram_voltage_interface_level($bytes->[8]));
694
695        printl("Module Configuration Type",
696               sdram_module_configuration_type($bytes->[11]));
697
698        printl("Refresh Rate", ddr2_refresh_rate($bytes->[12]));
699
700        if ($bytes->[13] & 0x80) { $temp = "Bank2 = 2 x Bank1"; }
701        else { $temp = "No Bank2 OR Bank2 = Bank1 width"; }
702        printl("Primary SDRAM Component Bank Config", $temp);
703        printl("Primary SDRAM Component Widths",
704               value_or_undefined($bytes->[13] & 0x7f));
705
706        if ($bytes->[14] & 0x80) { $temp = "Bank2 = 2 x Bank1"; }
707        else { $temp = "No Bank2 OR Bank2 = Bank1 width"; }
708        printl("Error Checking SDRAM Component Bank Config", $temp);
709        printl("Error Checking SDRAM Component Widths",
710               value_or_undefined($bytes->[14] & 0x7f));
711
712        printl("Min Clock Delay for Back to Back Random Access",
713               value_or_undefined($bytes->[15]));
714
715        my @array;
716        for ($ii = 0; $ii < 4; $ii++) {
717                push(@array, 1 << $ii) if ($bytes->[16] & (1 << $ii));
718        }
719        push(@array, "Page") if ($bytes->[16] & 128);
720        if (@array) { $temp = join ', ', @array; }
721        else { $temp = "None"; }
722        printl("Supported Burst Lengths", $temp);
723
724        printl("Number of Device Banks",
725               value_or_undefined($bytes->[17]));
726
727        printl("Supported CAS Latencies", cas_latencies(@cas));
728
729        @array = ();
730        for ($ii = 0; $ii < 7; $ii++) {
731                push(@array, $ii) if ($bytes->[19] & (1 << $ii));
732        }
733        if (@array) { $temp = join ', ', @array; }
734        else { $temp = "None"; }
735        printl("Supported CS Latencies", $temp);
736
737        @array = ();
738        for ($ii = 0; $ii < 7; $ii++) {
739                push(@array, $ii) if ($bytes->[20] & (1 << $ii));
740        }
741        if (@array) { $temp = join ', ', @array; }
742        else { $temp = "None"; }
743        printl("Supported WE Latencies", $temp);
744
745        my ($cycle_time, $access_time);
746
747        if (@cas >= 1) {
748                $cycle_time = "$ctime ns at CAS ".$cas[$#cas];
749
750                $temp = ($bytes->[10] >> 4) + ($bytes->[10] & 0xf) * 0.1;
751                $access_time = "$temp ns at CAS ".$cas[$#cas];
752        }
753
754        if (@cas >= 2 && spd_written(@$bytes[23..24])) {
755                $temp = $bytes->[23] >> 4;
756                if ($temp == 0) { $temp = "Undefined!"; }
757                else {
758                        $temp += 15 if $temp < 4;
759                        $temp += ($bytes->[23] & 0xf) * 0.1;
760                        $ctime1 = $temp;
761                }
762                $cycle_time .= "\n$temp ns at CAS ".$cas[$#cas-1];
763
764                $temp = $bytes->[24] >> 4;
765                if ($temp == 0) { $temp = "Undefined!"; }
766                else {
767                        $temp += 15 if $temp < 4;
768                        $temp += ($bytes->[24] & 0xf) * 0.1;
769                }
770                $access_time .= "\n$temp ns at CAS ".$cas[$#cas-1];
771        }
772
773        if (@cas >= 3 && spd_written(@$bytes[25..26])) {
774                $temp = $bytes->[25] >> 2;
775                if ($temp == 0) { $temp = "Undefined!"; }
776                else {
777                        $temp += ($bytes->[25] & 0x3) * 0.25;
778                        $ctime2 = $temp;
779                }
780                $cycle_time .= "\n$temp ns at CAS ".$cas[$#cas-2];
781
782                $temp = $bytes->[26] >> 2;
783                if ($temp == 0) { $temp = "Undefined!"; }
784                else {
785                        $temp += ($bytes->[26] & 0x3) * 0.25;
786                }
787                $access_time .= "\n$temp ns at CAS ".$cas[$#cas-2];
788        }
789
790        printl_cond(defined $cycle_time, "Cycle Time", $cycle_time);
791        printl_cond(defined $access_time, "Access Time", $access_time);
792
793        prints("Attributes");
794        $temp = "";
795        if ($bytes->[21] & 1) { $temp .= "Buffered Address/Control Inputs\n"; }
796        if ($bytes->[21] & 2) { $temp .= "Registered Address/Control Inputs\n"; }
797        if ($bytes->[21] & 4) { $temp .= "On card PLL (clock)\n"; }
798        if ($bytes->[21] & 8) { $temp .= "Buffered DQMB Inputs\n"; }
799        if ($bytes->[21] & 16) { $temp .= "Registered DQMB Inputs\n"; }
800        if ($bytes->[21] & 32) { $temp .= "Differential Clock Input\n"; }
801        if ($bytes->[21] & 64) { $temp .= "Redundant Row Address\n"; }
802        if ($bytes->[21] & 128) { $temp .= "Undefined (bit 7)\n"; }
803        printl_cond($bytes->[21], "SDRAM Module Attributes", $temp);
804
805# standard DDR speeds
806        prints("Timings at Standard Speeds");
807        foreach $ctime (7.5, 10, 15) {
808                my $best_cas;
809
810                # Find min CAS latency at this speed
811                if (defined $ctime2 && $ctime >= $ctime2) {
812                        $best_cas = $cas[$#cas-2];
813                } elsif (defined $ctime1 && $ctime >= $ctime1) {
814                        $best_cas = $cas[$#cas-1];
815                } else {
816                        $best_cas = $cas[$#cas];
817                }
818
819                printl_cond($ctime >= $ctime_min,
820                            "tCL-tRCD-tRP-tRAS as PC" . int(1000 / $ctime),
821                            ddr_core_timings($best_cas, $ctime,
822                                             $trcd, $trp, $tras));
823        }
824
825        $temp = "";
826        if ($bytes->[22] & 1) { $temp .= "Supports Early RAS# Recharge\n"; }
827        if ($bytes->[22] & 2) { $temp .= "Supports Auto-Precharge\n"; }
828        if ($bytes->[22] & 4) { $temp .= "Supports Precharge All\n"; }
829        if ($bytes->[22] & 8) { $temp .= "Supports Write1/Read Burst\n"; }
830        if ($bytes->[22] & 16) { $temp .= "Lower VCC Tolerance: 5%\n"; }
831        else { $temp .= "Lower VCC Tolerance: 10%\n"; }
832        if ($bytes->[22] & 32) { $temp .= "Upper VCC Tolerance: 5%\n"; }
833        else { $temp .= "Upper VCC Tolerance: 10%\n"; }
834        if ($bytes->[22] & 64) { $temp .= "Undefined (bit 6)\n"; }
835        if ($bytes->[22] & 128) { $temp .= "Undefined (bit 7)\n"; }
836        printl("SDRAM Device Attributes (General)", $temp);
837
838        prints("Timing Parameters");
839        printl("Minimum Row Precharge Time",
840               value_or_undefined($bytes->[27], "ns"));
841
842        printl("Row Active to Row Active Min",
843               value_or_undefined($bytes->[28], "ns"));
844
845        printl("RAS to CAS Delay",
846               value_or_undefined($bytes->[29], "ns"));
847
848        printl("Min RAS Pulse Width",
849               value_or_undefined($bytes->[30], "ns"));
850
851        $temp = "";
852        if ($bytes->[31] & 1) { $temp .= "4 MByte\n"; }
853        if ($bytes->[31] & 2) { $temp .= "8 MByte\n"; }
854        if ($bytes->[31] & 4) { $temp .= "16 MByte\n"; }
855        if ($bytes->[31] & 8) { $temp .= "32 MByte\n"; }
856        if ($bytes->[31] & 16) { $temp .= "64 MByte\n"; }
857        if ($bytes->[31] & 32) { $temp .= "128 MByte\n"; }
858        if ($bytes->[31] & 64) { $temp .= "256 MByte\n"; }
859        if ($bytes->[31] & 128) { $temp .= "512 MByte\n"; }
860        if ($bytes->[31] == 0) { $temp .= "(Undefined! -- None Reported!)\n"; }
861        printl("Row Densities", $temp);
862
863        $temp = (($bytes->[32] & 0x7f) >> 4) + ($bytes->[32] & 0xf) * 0.1;
864        printl_cond(($bytes->[32] & 0xf) <= 9,
865                    "Command and Address Signal Setup Time",
866                    (($bytes->[32] >> 7) ? -$temp : $temp) . " ns");
867
868        $temp = (($bytes->[33] & 0x7f) >> 4) + ($bytes->[33] & 0xf) * 0.1;
869        printl_cond(($bytes->[33] & 0xf) <= 9,
870                    "Command and Address Signal Hold Time",
871                    (($bytes->[33] >> 7) ? -$temp : $temp) . " ns");
872
873        $temp = (($bytes->[34] & 0x7f) >> 4) + ($bytes->[34] & 0xf) * 0.1;
874        printl_cond(($bytes->[34] & 0xf) <= 9, "Data Signal Setup Time",
875                    (($bytes->[34] >> 7) ? -$temp : $temp) . " ns");
876
877        $temp = (($bytes->[35] & 0x7f) >> 4) + ($bytes->[35] & 0xf) * 0.1;
878        printl_cond(($bytes->[35] & 0xf) <= 9, "Data Signal Hold Time",
879                    (($bytes->[35] >> 7) ? -$temp : $temp) . " ns");
880}
881
882sub as_ddr($$)
883{
884        my ($gen, $ctime) = @_;
885
886        return " as DDR" . ($gen == 1 ? "" : $gen) . "-" .
887               int(2000 / $ctime);
888}
889
890sub ddr_core_timings($$$$$)
891{
892        my ($cas, $ctime, $trcd, $trp, $tras) = @_;
893
894        return $cas . "-" . ceil($trcd/$ctime) . "-" . ceil($trp/$ctime) .
895                "-" . ceil($tras/$ctime);
896}
897
898# Parameter: EEPROM bytes 0-127 (using 3-62)
899sub decode_ddr_sdram($)
900{
901        my $bytes = shift;
902        my $temp;
903        my ($ctime, $ctime1, $ctime2, $ctime_min, $ctime_max);
904
905# SPD revision
906        printl_cond($bytes->[62] != 0xff, "SPD Revision",
907                    ($bytes->[62] >> 4) . "." . ($bytes->[62] & 0xf));
908
909# speed
910        prints("Memory Characteristics");
911
912        $ctime_min = $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
913        my $ddrclk = 2 * (1000 / $ctime);
914        my $tbits = ($bytes->[7] * 256) + $bytes->[6];
915        if (($bytes->[11] == 2) || ($bytes->[11] == 1)) { $tbits = $tbits - 8; }
916        my $pcclk = int ($ddrclk * $tbits / 8);
917        $pcclk += 100 if ($pcclk % 100) >= 50; # Round properly
918        $pcclk = $pcclk - ($pcclk % 100);
919        $ddrclk = int ($ddrclk);
920        printl("Maximum module speed", "$ddrclk MHz (PC${pcclk})");
921
922#size computation
923        my $k = 0;
924        my $ii = 0;
925
926        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
927        if (($bytes->[5] <= 8) && ($bytes->[17] <= 8)) {
928                 $k = $bytes->[5] * $bytes->[17];
929        }
930
931        if ($ii > 0 && $ii <= 12 && $k > 0) {
932                printl("Size", ((1 << $ii) * $k) . " MB");
933        } else {
934                printl("Size", "INVALID: " . $bytes->[3] . ", " . $bytes->[4] . ", " .
935                               $bytes->[5] . ", " . $bytes->[17]);
936        }
937
938        printl("Banks x Rows x Columns x Bits",
939               join(' x ', $bytes->[17], $bytes->[3], $bytes->[4], $bytes->[6]));
940        printl("Ranks", $bytes->[5]);
941
942        printl("Voltage Interface Level",
943               sdram_voltage_interface_level($bytes->[8]));
944
945        printl("Module Configuration Type",
946               sdram_module_configuration_type($bytes->[11]));
947
948        printl("Refresh Rate", ddr2_refresh_rate($bytes->[12]));
949
950        my $highestCAS = 0;
951        my %cas;
952        for ($ii = 0; $ii < 7; $ii++) {
953                if ($bytes->[18] & (1 << $ii)) {
954                        $highestCAS = 1+$ii*0.5;
955                        $cas{$highestCAS}++;
956                }
957        }
958
959        my $trcd;
960        my $trp;
961        my $tras;
962
963        $trcd = ($bytes->[29] >> 2) + (($bytes->[29] & 3) * 0.25);
964        $trp = ($bytes->[27] >> 2) + (($bytes->[27] & 3) * 0.25);
965        $tras = $bytes->[30];
966
967# latencies
968        printl("Supported CAS Latencies", cas_latencies(keys %cas));
969
970        my @array;
971        for ($ii = 0; $ii < 7; $ii++) {
972                push(@array, $ii) if ($bytes->[19] & (1 << $ii));
973        }
974        if (@array) { $temp = join ', ', @array; }
975        else { $temp = "None"; }
976        printl("Supported CS Latencies", $temp);
977
978        @array = ();
979        for ($ii = 0; $ii < 7; $ii++) {
980                push(@array, $ii) if ($bytes->[20] & (1 << $ii));
981        }
982        if (@array) { $temp = join ', ', @array; }
983        else { $temp = "None"; }
984        printl("Supported WE Latencies", $temp);
985
986# timings
987        my ($cycle_time, $access_time, $core_timings);
988
989        if (exists $cas{$highestCAS}) {
990                $core_timings = ddr_core_timings($highestCAS, $ctime,
991                        $trcd, $trp, $tras) . as_ddr(1, $ctime);
992
993                $cycle_time = "$ctime ns at CAS $highestCAS";
994                $access_time = (($bytes->[10] >> 4) * 0.1 + ($bytes->[10] & 0xf) * 0.01)
995                             . " ns at CAS $highestCAS";
996        }
997
998        if (exists $cas{$highestCAS-0.5} && spd_written(@$bytes[23..24])) {
999                $ctime1 = ($bytes->[23] >> 4) + ($bytes->[23] & 0xf) * 0.1;
1000                $core_timings .= "\n".ddr_core_timings($highestCAS-0.5, $ctime1,
1001                        $trcd, $trp, $tras) . as_ddr(1, $ctime1);
1002
1003                $cycle_time .= "\n$ctime1 ns at CAS ".($highestCAS-0.5);
1004                $access_time .= "\n".(($bytes->[24] >> 4) * 0.1 + ($bytes->[24] & 0xf) * 0.01)
1005                              . " ns at CAS ".($highestCAS-0.5);
1006        }
1007
1008        if (exists $cas{$highestCAS-1} && spd_written(@$bytes[25..26])) {
1009                $ctime2 = ($bytes->[25] >> 4) + ($bytes->[25] & 0xf) * 0.1,
1010                $core_timings .= "\n".ddr_core_timings($highestCAS-1, $ctime2,
1011                        $trcd, $trp, $tras) . as_ddr(1, $ctime2);
1012
1013                $cycle_time .= "\n$ctime2 ns at CAS ".($highestCAS-1);
1014                $access_time .= "\n".(($bytes->[26] >> 4) * 0.1 + ($bytes->[26] & 0xf) * 0.01)
1015                              . " ns at CAS ".($highestCAS-1);
1016        }
1017
1018        $ctime_max = $bytes->[43] == 0xff ? 0 : $bytes->[43]/4;
1019
1020        printl_cond(defined $core_timings, "tCL-tRCD-tRP-tRAS", $core_timings);
1021        printl_cond(defined $cycle_time, "Minimum Cycle Time", $cycle_time);
1022        printl_cond(defined $access_time, "Maximum Access Time", $access_time);
1023        printl_cond($bytes->[43] & 0xfc,
1024                    "Maximum Cycle Time (tCK max)",
1025                    $bytes->[43] == 0xff ? "No minimum frequency" :
1026                    $bytes->[43] == 0 ? "" : # Wouldn't be displayed, prevent div by 0
1027                    tns1($ctime_max)." (DDR-".int(8000 / $bytes->[43]).")");
1028
1029# standard DDR speeds
1030        prints("Timings at Standard Speeds");
1031        foreach $ctime (5, 6, 7.5, 10) {
1032                my $best_cas;
1033
1034                # Find min CAS latency at this speed
1035                if (defined $ctime2 && $ctime >= $ctime2) {
1036                        $best_cas = $highestCAS-1;
1037                } elsif (defined $ctime1 && $ctime >= $ctime1) {
1038                        $best_cas = $highestCAS-0.5;
1039                } else {
1040                        $best_cas = $highestCAS;
1041                }
1042
1043                printl_cond($ctime >= $ctime_min && ($ctime_max < 1 || $ctime <= $ctime_max),
1044                            "tCL-tRCD-tRP-tRAS" . as_ddr(1, $ctime),
1045                            ddr_core_timings($best_cas, $ctime,
1046                                             $trcd, $trp, $tras));
1047        }
1048
1049# more timing information
1050        prints("Timing Parameters");
1051        printl_cond($bytes->[32] != 0xff,
1052                    "Address/Command Setup Time Before Clock",
1053                    tns(ddr2_sdram_atime($bytes->[32])));
1054        printl_cond($bytes->[33] != 0xff,
1055                    "Address/Command Hold Time After Clock",
1056                    tns(ddr2_sdram_atime($bytes->[33])));
1057        printl_cond($bytes->[34] != 0xff,
1058                    "Data Input Setup Time Before Clock",
1059                    tns(ddr2_sdram_atime($bytes->[34])));
1060        printl_cond($bytes->[35] != 0xff,
1061                    "Data Input Hold Time After Clock",
1062                    tns(ddr2_sdram_atime($bytes->[35])));
1063        printl("Minimum Row Precharge Delay (tRP)", tns($trp));
1064        printl_cond($bytes->[28] & 0xfc,
1065                    "Minimum Row Active to Row Active Delay (tRRD)",
1066                    tns($bytes->[28]/4));
1067        printl("Minimum RAS# to CAS# Delay (tRCD)", tns($trcd));
1068        printl("Minimum RAS# Pulse Width (tRAS)", tns($tras));
1069        printl_cond($bytes->[41] && $bytes->[41] != 0xff,
1070                    "Minimum Active to Active/AR Time (tRC)",
1071                    tns($bytes->[41]));
1072        printl_cond($bytes->[42],
1073                    "Minimum AR to Active/AR Command Period (tRFC)",
1074                    tns($bytes->[42]));
1075        printl_cond($bytes->[44],
1076                    "Maximum DQS to DQ Skew (tDQSQ)",
1077                    tns($bytes->[44]/100));
1078        printl_cond(($bytes->[45] & 0xf0) && $bytes->[45] != 0xff,
1079                    "Maximum Read Data Hold Skew (tQHS)",
1080                    tns(ddr2_sdram_atime($bytes->[45])));
1081
1082# module attributes
1083        prints("Module Attributes");
1084        if (($bytes->[47] & 0x03) == 0x01) { $temp = "1.125\" to 1.25\""; }
1085        elsif (($bytes->[47] & 0x03) == 0x02) { $temp = "1.7\""; }
1086        else { $temp = "Other"; }
1087        printl_cond($bytes->[47] & 0x03, "Module Height", $temp);
1088}
1089
1090sub ddr2_sdram_ctime($)
1091{
1092        my $byte = shift;
1093        my $ctime;
1094
1095        $ctime = $byte >> 4;
1096        if (($byte & 0xf) <= 9) { $ctime += ($byte & 0xf) * 0.1; }
1097        elsif (($byte & 0xf) == 10) { $ctime += 0.25; }
1098        elsif (($byte & 0xf) == 11) { $ctime += 0.33; }
1099        elsif (($byte & 0xf) == 12) { $ctime += 0.66; }
1100        elsif (($byte & 0xf) == 13) { $ctime += 0.75; }
1101
1102        return $ctime;
1103}
1104
1105sub ddr2_sdram_atime($)
1106{
1107        my $byte = shift;
1108        my $atime;
1109
1110        $atime = ($byte >> 4) * 0.1 + ($byte & 0xf) * 0.01;
1111
1112        return $atime;
1113}
1114
1115# Base, high-bit, 3-bit fraction code
1116sub ddr2_sdram_rtime($$$)
1117{
1118        my ($rtime, $msb, $ext) = @_;
1119        my @table = (0, .25, .33, .50, .66, .75);
1120
1121        return $rtime + $msb * 256 + $table[$ext];
1122}
1123
1124sub ddr2_module_types($)
1125{
1126        my $byte = shift;
1127        my @types = qw(RDIMM UDIMM SO-DIMM Micro-DIMM Mini-RDIMM Mini-UDIMM);
1128        my @widths = (133.35, 133.25, 67.6, 45.5, 82.0, 82.0);
1129        my @suptypes;
1130        local $_;
1131
1132        foreach (0..5) {
1133                push @suptypes, "$types[$_] ($widths[$_] mm)"
1134                        if ($byte & (1 << $_));
1135        }
1136
1137        return @suptypes;
1138}
1139
1140# Common to SDR, DDR and DDR2 SDRAM
1141sub ddr2_refresh_rate($)
1142{
1143        my $byte = shift;
1144        my @refresh = qw(Normal Reduced Reduced Extended Extended Extended);
1145        my @refresht = (15.625, 3.9, 7.8, 31.3, 62.5, 125);
1146
1147        return "$refresh[$byte & 0x7f] ($refresht[$byte & 0x7f] us)".
1148               ($byte & 0x80 ? " - Self Refresh" : "");
1149}
1150
1151# Parameter: EEPROM bytes 0-127 (using 3-62)
1152sub decode_ddr2_sdram($)
1153{
1154        my $bytes = shift;
1155        my $temp;
1156        my ($ctime, $ctime1, $ctime2, $ctime_min, $ctime_max);
1157
1158# SPD revision
1159        printl_cond($bytes->[62] != 0xff, "SPD Revision",
1160                    ($bytes->[62] >> 4) . "." . ($bytes->[62] & 0xf));
1161
1162# speed
1163        prints("Memory Characteristics");
1164
1165        $ctime_min = $ctime = ddr2_sdram_ctime($bytes->[9]);
1166        my $ddrclk = 2 * (1000 / $ctime);
1167        my $tbits = ($bytes->[7] * 256) + $bytes->[6];
1168        if ($bytes->[11] & 0x03) { $tbits = $tbits - 8; }
1169        my $pcclk = int ($ddrclk * $tbits / 8);
1170        # Round down to comply with Jedec
1171        $pcclk = $pcclk - ($pcclk % 100);
1172        $ddrclk = int ($ddrclk);
1173        printl("Maximum module speed", "$ddrclk MHz (PC2-${pcclk})");
1174
1175#size computation
1176        my $k = 0;
1177        my $ii = 0;
1178
1179        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
1180        $k = (($bytes->[5] & 0x7) + 1) * $bytes->[17];
1181
1182        if($ii > 0 && $ii <= 12 && $k > 0) {
1183                printl("Size", ((1 << $ii) * $k) . " MB");
1184        } else {
1185                printl("Size", "INVALID: " . $bytes->[3] . "," . $bytes->[4] . "," .
1186                               $bytes->[5] . "," . $bytes->[17]);
1187        }
1188
1189        printl("Banks x Rows x Columns x Bits",
1190               join(' x ', $bytes->[17], $bytes->[3], $bytes->[4], $bytes->[6]));
1191        printl("Ranks", ($bytes->[5] & 7) + 1);
1192
1193        printl("SDRAM Device Width", $bytes->[13]." bits");
1194
1195        my @heights = ('< 25.4', '25.4', '25.4 - 30.0', '30.0', '30.5', '> 30.5');
1196        printl("Module Height", $heights[$bytes->[5] >> 5]." mm");
1197
1198        my @suptypes = ddr2_module_types($bytes->[20]);
1199        printl("Module Type".(@suptypes > 1 ? 's' : ''), join(', ', @suptypes));
1200
1201        printl("DRAM Package", $bytes->[5] & 0x10 ? "Stack" : "Planar");
1202
1203        printl("Voltage Interface Level",
1204               sdram_voltage_interface_level($bytes->[8]));
1205
1206        printl("Module Configuration Type",
1207               sdram_module_configuration_type($bytes->[11]));
1208
1209        printl("Refresh Rate", ddr2_refresh_rate($bytes->[12]));
1210
1211        my @burst;
1212        push @burst, 4 if ($bytes->[16] & 4);
1213        push @burst, 8 if ($bytes->[16] & 8);
1214        $burst[0] = 'None' if !@burst;
1215        printl("Supported Burst Lengths", join(', ', @burst));
1216
1217        my $highestCAS = 0;
1218        my %cas;
1219        for ($ii = 2; $ii < 7; $ii++) {
1220                if ($bytes->[18] & (1 << $ii)) {
1221                        $highestCAS = $ii;
1222                        $cas{$highestCAS}++;
1223                }
1224        }
1225
1226        my $trcd;
1227        my $trp;
1228        my $tras;
1229
1230        $trcd = ($bytes->[29] >> 2) + (($bytes->[29] & 3) * 0.25);
1231        $trp = ($bytes->[27] >> 2) + (($bytes->[27] & 3) * 0.25);
1232        $tras = $bytes->[30];
1233
1234# latencies
1235        printl("Supported CAS Latencies (tCL)", cas_latencies(keys %cas));
1236
1237# timings
1238        my ($cycle_time, $access_time, $core_timings);
1239
1240        if (exists $cas{$highestCAS}) {
1241                $core_timings = ddr_core_timings($highestCAS, $ctime,
1242                        $trcd, $trp, $tras) . as_ddr(2, $ctime);
1243
1244                $cycle_time = tns($ctime) . " at CAS $highestCAS (tCK min)";
1245                $access_time = tns(ddr2_sdram_atime($bytes->[10]))
1246                             . " at CAS $highestCAS (tAC)";
1247        }
1248
1249        if (exists $cas{$highestCAS-1} && spd_written(@$bytes[23..24])) {
1250                $ctime1 = ddr2_sdram_ctime($bytes->[23]);
1251                $core_timings .= "\n".ddr_core_timings($highestCAS-1, $ctime1,
1252                        $trcd, $trp, $tras) . as_ddr(2, $ctime1);
1253
1254                $cycle_time .= "\n".tns($ctime1)
1255                             . " at CAS ".($highestCAS-1);
1256                $access_time .= "\n".tns(ddr2_sdram_atime($bytes->[24]))
1257                              . " at CAS ".($highestCAS-1);
1258        }
1259
1260        if (exists $cas{$highestCAS-2} && spd_written(@$bytes[25..26])) {
1261                $ctime2 = ddr2_sdram_ctime($bytes->[25]);
1262                $core_timings .= "\n".ddr_core_timings($highestCAS-2, $ctime2,
1263                        $trcd, $trp, $tras) . as_ddr(2, $ctime2);
1264
1265                $cycle_time .= "\n".tns($ctime2)
1266                             . " at CAS ".($highestCAS-2);
1267                $access_time .= "\n".tns(ddr2_sdram_atime($bytes->[26]))
1268                              . " at CAS ".($highestCAS-2);
1269        }
1270
1271        $ctime_max = ddr2_sdram_ctime($bytes->[43]);
1272
1273        printl_cond(defined $core_timings, "tCL-tRCD-tRP-tRAS", $core_timings);
1274        printl_cond(defined $cycle_time, "Minimum Cycle Time", $cycle_time);
1275        printl_cond(defined $access_time, "Maximum Access Time", $access_time);
1276        printl_cond(($bytes->[43] & 0xf0) && $bytes->[43] != 0xff,
1277                    "Maximum Cycle Time (tCK max)",
1278                    $ctime_max == 0 ? "" : # Wouldn't be displayed, prevent div by 0
1279                    tns($ctime_max)." (DDR2-".int(2000 / $ctime_max).")");
1280
1281# standard DDR2 speeds
1282        prints("Timings at Standard Speeds");
1283        foreach $ctime (1.875, 2.5, 3, 3.75, 5) {
1284                my $best_cas;
1285
1286                # Find min CAS latency at this speed
1287                if (defined $ctime2 && $ctime >= $ctime2) {
1288                        $best_cas = $highestCAS-2;
1289                } elsif (defined $ctime1 && $ctime >= $ctime1) {
1290                        $best_cas = $highestCAS-1;
1291                } else {
1292                        $best_cas = $highestCAS;
1293                }
1294
1295                printl_cond($ctime >= $ctime_min && $ctime <= $ctime_max,
1296                            "tCL-tRCD-tRP-tRAS" . as_ddr(2,$ctime),
1297                            ddr_core_timings($best_cas, $ctime,
1298                                             $trcd, $trp, $tras));
1299        }
1300
1301# more timing information
1302        prints("Timing Parameters");
1303        # According to the JEDEC standard, the four timings below can't be less
1304        # than 0.1 ns, however we've seen memory modules code such values so
1305        # handle them properly.
1306        printl_cond($bytes->[32] && $bytes->[32] != 0xff,
1307                    "Address/Command Setup Time Before Clock (tIS)",
1308                    tns(ddr2_sdram_atime($bytes->[32])));
1309        printl_cond($bytes->[33] && $bytes->[33] != 0xff,
1310                    "Address/Command Hold Time After Clock (tIH)",
1311                    tns(ddr2_sdram_atime($bytes->[33])));
1312        printl_cond($bytes->[34] && $bytes->[34] != 0xff,
1313                    "Data Input Setup Time Before Strobe (tDS)",
1314                    tns(ddr2_sdram_atime($bytes->[34])));
1315        printl_cond($bytes->[35] && $bytes->[35] != 0xff,
1316                    "Data Input Hold Time After Strobe (tDH)",
1317                    tns(ddr2_sdram_atime($bytes->[35])));
1318
1319        printl("Minimum Row Precharge Delay (tRP)", tns($trp));
1320        printl_cond($bytes->[28] & 0xfc,
1321                    "Minimum Row Active to Row Active Delay (tRRD)",
1322                    tns($bytes->[28]/4));
1323        printl("Minimum RAS# to CAS# Delay (tRCD)", tns($trcd));
1324        printl("Minimum RAS# Pulse Width (tRAS)", tns($tras));
1325        printl_cond($bytes->[36] & 0xfc,
1326                    "Write Recovery Time (tWR)",
1327                    tns($bytes->[36]/4));
1328        printl_cond($bytes->[37] & 0xfc,
1329                    "Minimum Write to Read CMD Delay (tWTR)",
1330                    tns($bytes->[37]/4));
1331        printl_cond($bytes->[38] & 0xfc,
1332                    "Minimum Read to Pre-charge CMD Delay (tRTP)",
1333                    tns($bytes->[38]/4));
1334
1335        printl_cond($bytes->[41] && $bytes->[41] != 0xff,
1336                    "Minimum Active to Auto-refresh Delay (tRC)",
1337                    tns(ddr2_sdram_rtime($bytes->[41], 0,
1338                                         ($bytes->[40] >> 4) & 7)));
1339        printl_cond($bytes->[42],
1340                    "Minimum Recovery Delay (tRFC)",
1341                    tns(ddr2_sdram_rtime($bytes->[42], $bytes->[40] & 1,
1342                                         ($bytes->[40] >> 1) & 7)));
1343
1344        printl_cond($bytes->[44], "Maximum DQS to DQ Skew (tDQSQ)",
1345                    tns($bytes->[44]/100));
1346        printl_cond($bytes->[45], "Maximum Read Data Hold Skew (tQHS)",
1347                    tns($bytes->[45]/100));
1348        printl_cond($bytes->[46], "PLL Relock Time", $bytes->[46] . " us");
1349}
1350
1351# Return combined time in ns
1352sub ddr3_mtb_ftb($$$$)
1353{
1354        my ($byte1, $byte2, $mtb, $ftb) = @_;
1355
1356        # byte1 is unsigned in ns, but byte2 is signed in ps
1357        $byte2 -= 0x100 if $byte2 & 0x80;
1358
1359        return $byte1 * $mtb + $byte2 * $ftb / 1000;
1360}
1361
1362sub ddr3_reference_card($$)
1363{
1364        my ($rrc, $ext) = @_;
1365        my $alphabet = "ABCDEFGHJKLMNPRTUVWY";
1366        my $ref = $rrc & 0x1f;
1367        my $revision = $ext >> 5;
1368        my $ref_card;
1369
1370        return "ZZ" if $ref == 0x1f;
1371        $ref += 0x1f if $rrc & 0x80;
1372        $revision = (($rrc >> 5) & 0x03) if $revision == 0;
1373
1374        if ($ref < length($alphabet)) {
1375                # One letter reference card
1376                $ref_card = substr($alphabet, $ref, 1);
1377        } else {
1378                # Two letter reference card
1379                my $ref1 = int($ref / (length($alphabet)));
1380                $ref -= length($alphabet) * $ref1;
1381                $ref_card = substr($alphabet, $ref1, 1) .
1382                            substr($alphabet, $ref, 1);
1383        }
1384
1385        return "$ref_card revision $revision";
1386}
1387
1388sub ddr3_revision_number($)
1389{
1390        my $h = $_[0] >> 4;
1391        my $l = $_[0] & 0x0f;
1392
1393        # Decode as suggested by JEDEC Standard 21-C
1394        return sprintf("%d", $l) if $h == 0;
1395        return sprintf("%d.%d", $h, $l) if $h < 0xa;
1396        return sprintf("%c%d", ord('A') + $h - 0xa, $l);
1397}
1398
1399sub ddr3_device_type($)
1400{
1401        my $byte = shift;
1402        my $type = $byte & 0x80 ? "Non-Standard" : "Standard Monolithic";
1403        my $die_count = ($byte >> 4) & 0x07;
1404        my $loading = ($byte >> 2) & 0x03;
1405
1406        if ($die_count == 1) {
1407                $type .= "\nSingle die";
1408        } elsif ($die_count == 2) {
1409                $type .= "\n2 die";
1410        } elsif ($die_count == 3) {
1411                $type .= "\n4 die";
1412        } elsif ($die_count == 4) {
1413                $type .= "\n8 die";
1414        }
1415
1416        if ($loading == 1) {
1417                $type .= "\nMulti load stack";
1418        } elsif ($loading == 2) {
1419                $type .= "\nSingle load stack";
1420        }
1421
1422        return $type;
1423}
1424
1425use constant DDR3_UNBUFFERED    => 1;
1426use constant DDR3_REGISTERED    => 2;
1427use constant DDR3_CLOCKED       => 3;
1428use constant DDR3_LOAD_REDUCED  => 4;
1429
1430# Parameter: EEPROM bytes 0-127 (using 3-76)
1431sub decode_ddr3_sdram($)
1432{
1433        my $bytes = shift;
1434        my $temp;
1435        my $ctime;
1436        my ($ftb, $mtb);
1437        my $ii;
1438
1439        my @module_types = (
1440                { type => "Undefined",          width => "Unknown"      },
1441                { type => "RDIMM",              width => "133.35 mm",   family => DDR3_REGISTERED },
1442                { type => "UDIMM",              width => "133.35 mm",   family => DDR3_UNBUFFERED },
1443                { type => "SO-DIMM",            width => "67.6 mm",     family => DDR3_UNBUFFERED },
1444                { type => "Micro-DIMM",         width => "TBD",         family => DDR3_UNBUFFERED },
1445                { type => "Mini-RDIMM",         width => "82.0 mm",     family => DDR3_REGISTERED },
1446                { type => "Mini-UDIMM",         width => "82.0 mm",     family => DDR3_UNBUFFERED },
1447                { type => "Mini-CDIMM",         width => "67.6 mm",     family => DDR3_CLOCKED },
1448                { type => "72b-SO-UDIMM",       width => "67.6 mm",     family => DDR3_UNBUFFERED },
1449                { type => "72b-SO-RDIMM",       width => "67.6 mm",     family => DDR3_REGISTERED },
1450                { type => "72b-SO-CDIMM",       width => "67.6 mm",     family => DDR3_CLOCKED },
1451                { type => "LRDIMM",             width => "133.35 mm",   family => DDR3_LOAD_REDUCED },
1452                { type => "16b-SO-DIMM",        width => "67.6 mm",     family => DDR3_UNBUFFERED },
1453                { type => "32b-SO-DIMM",        width => "67.6 mm",     family => DDR3_UNBUFFERED },
1454        );
1455
1456        printl("Module Type", ($bytes->[3] <= $#module_types) ?
1457                                        $module_types[$bytes->[3]]->{type} :
1458                                        sprintf("Reserved (0x%.2X)", $bytes->[3]));
1459
1460# time bases
1461        if (($bytes->[9] & 0x0f) == 0 || $bytes->[11] == 0) {
1462                print STDERR "Invalid time base divisor, can't decode\n";
1463                return;
1464        }
1465        $ftb = ($bytes->[9] >> 4) / ($bytes->[9] & 0x0f);
1466        $mtb = $bytes->[10] / $bytes->[11];
1467
1468# speed
1469        prints("Memory Characteristics");
1470
1471        $ctime = ddr3_mtb_ftb($bytes->[12], $bytes->[34], $mtb, $ftb);
1472        # Starting with DDR3-1866, vendors may start approximating the
1473        # minimum cycle time. Try to guess what they really meant so
1474        # that the reported speed matches the standard.
1475        for ($ii = 7; $ii < 15; $ii++) {
1476                if ($ctime > 7.5/$ii - $ftb/1000 && $ctime < 7.5/$ii + $ftb/1000) {
1477                        $ctime = 7.5/$ii;
1478                        last;
1479                }
1480        }
1481
1482        my $ddrclk = 2 * (1000 / $ctime);
1483        my $tbits = 1 << (($bytes->[8] & 7) + 3);
1484        my $pcclk = int ($ddrclk * $tbits / 8);
1485        # Round down to comply with Jedec
1486        $pcclk = $pcclk - ($pcclk % 100);
1487        $ddrclk = int ($ddrclk);
1488        printl("Maximum module speed", "$ddrclk MHz (PC3-${pcclk})");
1489
1490# Size computation
1491
1492        my $cap =  ($bytes->[4]       & 15) + 28;
1493        $cap   +=  ($bytes->[8]       & 7)  + 3;
1494        $cap   -=  ($bytes->[7]       & 7)  + 2;
1495        $cap   -= 20 + 3;
1496        my $k   = (($bytes->[7] >> 3) & 31) + 1;
1497        printl("Size", ((1 << $cap) * $k) . " MB");
1498
1499        printl("Banks x Rows x Columns x Bits",
1500               join(' x ', 1 << ((($bytes->[4] >> 4) &  7) +  3),
1501                           ((($bytes->[5] >> 3) & 31) + 12),
1502                           ( ($bytes->[5]       &  7) +  9),
1503                           ( 1 << (($bytes->[8] &  7) + 3)) ));
1504        printl("Ranks", $k);
1505
1506        printl("SDRAM Device Width", (1 << (($bytes->[7] & 7) + 2))." bits");
1507
1508        printl("Bus Width Extension", ($bytes->[8] & 24)." bits");
1509
1510        my $taa;
1511        my $trcd;
1512        my $trp;
1513        my $tras;
1514
1515        $taa  = ddr3_mtb_ftb($bytes->[16], $bytes->[35], $mtb, $ftb);
1516        $trcd = ddr3_mtb_ftb($bytes->[18], $bytes->[36], $mtb, $ftb);
1517        $trp  = ddr3_mtb_ftb($bytes->[20], $bytes->[37], $mtb, $ftb);
1518        $tras = ((($bytes->[21] & 0x0f) << 8) + $bytes->[22]) * $mtb;
1519
1520        printl("tCL-tRCD-tRP-tRAS", ddr_core_timings(ceil($taa / $ctime), $ctime, $trcd, $trp, $tras));
1521
1522# latencies
1523        my $highestCAS = 0;
1524        my %cas;
1525        my $cas_sup = ($bytes->[15] << 8) + $bytes->[14];
1526        for ($ii = 0; $ii < 15; $ii++) {
1527                if ($cas_sup & (1 << $ii)) {
1528                        $highestCAS = $ii + 4;
1529                        $cas{$highestCAS}++;
1530                }
1531        }
1532        printl("Supported CAS Latencies (tCL)", cas_latencies(keys %cas));
1533
1534# standard DDR3 speeds
1535        prints("Timings at Standard Speeds");
1536        foreach my $ctime_at_speed (7.5/8, 7.5/7, 1.25, 1.5, 1.875, 2.5) {
1537                my $best_cas = 0;
1538
1539                # Find min CAS latency at this speed
1540                for ($ii = 14; $ii >= 0; $ii--) {
1541                        next unless ($cas_sup & (1 << $ii));
1542                        if (ceil($taa / $ctime_at_speed) <= $ii + 4) {
1543                                $best_cas = $ii + 4;
1544                        }
1545                }
1546
1547                printl_cond($best_cas && $ctime_at_speed >= $ctime,
1548                            "tCL-tRCD-tRP-tRAS" . as_ddr(3, $ctime_at_speed),
1549                            ddr_core_timings($best_cas, $ctime_at_speed,
1550                                             $trcd, $trp, $tras));
1551        }
1552
1553# more timing information
1554        prints("Timing Parameters");
1555
1556        printl("Minimum Cycle Time (tCK)", tns3($ctime));
1557        printl("Minimum CAS Latency Time (tAA)", tns3($taa));
1558        printl("Minimum Write Recovery time (tWR)", tns3($bytes->[17] * $mtb));
1559        printl("Minimum RAS# to CAS# Delay (tRCD)", tns3($trcd));
1560        printl("Minimum Row Active to Row Active Delay (tRRD)",
1561                tns3($bytes->[19] * $mtb));
1562        printl("Minimum Row Precharge Delay (tRP)", tns3($trp));
1563        printl("Minimum Active to Precharge Delay (tRAS)", tns3($tras));
1564        printl("Minimum Active to Auto-Refresh Delay (tRC)",
1565                tns3(ddr3_mtb_ftb((($bytes->[21] & 0xf0) << 4) + $bytes->[23], $bytes->[38], $mtb, $ftb)));
1566        printl("Minimum Recovery Delay (tRFC)",
1567                tns3((($bytes->[25] << 8) + $bytes->[24]) * $mtb));
1568        printl("Minimum Write to Read CMD Delay (tWTR)",
1569                tns3($bytes->[26] * $mtb));
1570        printl("Minimum Read to Pre-charge CMD Delay (tRTP)",
1571                tns3($bytes->[27] * $mtb));
1572        printl("Minimum Four Activate Window Delay (tFAW)",
1573                tns3(((($bytes->[28] & 15) << 8) + $bytes->[29]) * $mtb));
1574
1575# miscellaneous stuff
1576        prints("Optional Features");
1577
1578        my $volts = "1.5V";
1579        if ($bytes->[6] & 1) {
1580                $volts .= " tolerant";
1581        }
1582        if ($bytes->[6] & 2) {
1583                $volts .= ", 1.35V ";
1584        }
1585        if ($bytes->[6] & 4) {
1586                $volts .= ", 1.2X V";
1587        }
1588        printl("Operable voltages", $volts);
1589        printl("RZQ/6 supported?", ($bytes->[30] & 1) ? "Yes" : "No");
1590        printl("RZQ/7 supported?", ($bytes->[30] & 2) ? "Yes" : "No");
1591        printl("DLL-Off Mode supported?", ($bytes->[30] & 128) ? "Yes" : "No");
1592        printl("Operating temperature range", sprintf "0-%d degrees C",
1593                ($bytes->[31] & 1) ? 95 : 85);
1594        printl("Refresh Rate in extended temp range",
1595                ($bytes->[31] & 2) ? "2X" : "1X");
1596        printl("Auto Self-Refresh?", ($bytes->[31] & 4) ? "Yes" : "No");
1597        printl("On-Die Thermal Sensor readout?",
1598                ($bytes->[31] & 8) ? "Yes" : "No");
1599        printl("Partial Array Self-Refresh?",
1600                ($bytes->[31] & 128) ? "Yes" : "No");
1601        printl("Module Thermal Sensor",
1602                ($bytes->[32] & 128) ? "Yes" : "No");
1603        printl("SDRAM Device Type", ddr3_device_type($bytes->[33]));
1604
1605        # Following bytes are type-specific, so don't continue if type
1606        # isn't known.
1607        return if $bytes->[3] == 0 || $bytes->[3] > $#module_types;
1608
1609        if ($module_types[$bytes->[3]]->{family} == DDR3_UNBUFFERED ||
1610            $module_types[$bytes->[3]]->{family} == DDR3_REGISTERED ||
1611            $module_types[$bytes->[3]]->{family} == DDR3_CLOCKED ||
1612            $module_types[$bytes->[3]]->{family} == DDR3_LOAD_REDUCED) {
1613                prints("Physical Characteristics");
1614                printl("Module Height", (($bytes->[60] & 31) + 15) . " mm");
1615                printl("Module Thickness", sprintf("%d mm front, %d mm back",
1616                                                ($bytes->[61] & 15) + 1,
1617                                                (($bytes->[61] >> 4) & 15) +1));
1618                printl("Module Width", $module_types[$bytes->[3]]->{width});
1619                printl("Module Reference Card", ddr3_reference_card($bytes->[62], $bytes->[60]));
1620
1621                printl_cond($module_types[$bytes->[3]]->{family} == DDR3_UNBUFFERED,
1622                            "Rank 1 Mapping", $bytes->[63] & 0x01 ? "Mirrored" : "Standard");
1623        }
1624
1625        if ($module_types[$bytes->[3]]->{family} == DDR3_REGISTERED) {
1626                prints("Registered DIMM");
1627
1628                my @rows = ("Undefined", 1, 2, 4);
1629                printl("# DRAM Rows", $rows[($bytes->[63] >> 2) & 3]);
1630                printl("# Registers", $rows[$bytes->[63] & 3]);
1631                printl("Register manufacturer",
1632                        manufacturer_ddr3($bytes->[65], $bytes->[66]));
1633                printl("Register device type",
1634                                (($bytes->[68] & 7) == 0) ? "SSTE32882" :
1635                                        "Undefined");
1636                printl_cond($bytes->[67] != 0xff,
1637                            "Register revision", ddr3_revision_number($bytes->[67]));
1638                printl("Heat spreader", $bytes->[64] & 0x80 ? "Yes" : "No");
1639        }
1640
1641        if ($module_types[$bytes->[3]]->{family} == DDR3_LOAD_REDUCED) {
1642                prints("Load Reduced DIMM");
1643
1644                my @rows = ("Undefined", 1, 2, "Reserved");
1645                printl("# DRAM Rows", $rows[($bytes->[63] >> 2) & 3]);
1646                my @mirroring = ("None", "Odd ranks", "Reserved", "Reserved");
1647                printl("Mirroring", $mirroring[$bytes->[63] & 3]);
1648                printl("Rank Numbering", $bytes->[63] & 0x20 ? "Even only" : "Contiguous");
1649                printl("Buffer Orientation", $bytes->[63] & 0x10 ? "Horizontal" : "Vertical");
1650                printl("Register manufacturer",
1651                        manufacturer_ddr3($bytes->[65], $bytes->[66]));
1652                printl_cond($bytes->[64] != 0xff,
1653                            "Buffer Revision", ddr3_revision_number($bytes->[64]));
1654                printl("Heat spreader", $bytes->[63] & 0x80 ? "Yes" : "No");
1655        }
1656
1657}
1658
1659# Parameter: EEPROM bytes 0-127 (using 4-5)
1660sub decode_direct_rambus($)
1661{
1662        my $bytes = shift;
1663
1664#size computation
1665        prints("Memory Characteristics");
1666
1667        my $ii;
1668
1669        $ii = ($bytes->[4] & 0x0f) + ($bytes->[4] >> 4) + ($bytes->[5] & 0x07) - 13;
1670
1671        if ($ii > 0 && $ii < 16) {
1672                printl("Size", (1 << $ii) . " MB");
1673        } else {
1674                printl("Size", sprintf("INVALID: 0x%02x, 0x%02x",
1675                                       $bytes->[4], $bytes->[5]));
1676        }
1677}
1678
1679# Parameter: EEPROM bytes 0-127 (using 3-5)
1680sub decode_rambus($)
1681{
1682        my $bytes = shift;
1683
1684#size computation
1685        prints("Memory Characteristics");
1686
1687        my $ii;
1688
1689        $ii = ($bytes->[3] & 0x0f) + ($bytes->[3] >> 4) + ($bytes->[5] & 0x07) - 13;
1690
1691        if ($ii > 0 && $ii < 16) {
1692                printl("Size", (1 << $ii) . " MB");
1693        } else {
1694                printl("Size", "INVALID: " . sprintf("0x%02x, 0x%02x",
1695                                               $bytes->[3], $bytes->[5]));
1696        }
1697}
1698
1699%decode_callback = (
1700        "SDR SDRAM"     => \&decode_sdr_sdram,
1701        "DDR SDRAM"     => \&decode_ddr_sdram,
1702        "DDR2 SDRAM"    => \&decode_ddr2_sdram,
1703        "DDR3 SDRAM"    => \&decode_ddr3_sdram,
1704        "Direct Rambus" => \&decode_direct_rambus,
1705        "Rambus"        => \&decode_rambus,
1706);
1707
1708# Parameter: Manufacturing year/week bytes
1709sub manufacture_date($$)
1710{
1711        my ($year, $week) = @_;
1712
1713        # In theory the year and week are in BCD format, but
1714        # this is not always true in practice :(
1715        if (($year & 0xf0) <= 0x90 && ($year & 0x0f) <= 0x09
1716         && ($week & 0xf0) <= 0x90 && ($week & 0x0f) <= 0x09) {
1717                # Note that this heuristic will break in year 2080
1718                return sprintf("%d%02X-W%02X",
1719                                $year >= 0x80 ? 19 : 20, $year, $week);
1720        # Fallback to binary format if it seems to make sense
1721        } elsif ($year <= 99 && $week >= 1 && $week <= 53) {
1722                return sprintf("%d%02d-W%02d",
1723                                $year >= 80 ? 19 : 20, $year, $week);
1724        } else {
1725                return sprintf("0x%02X%02X", $year, $week);
1726        }
1727}
1728
1729sub printl_mfg_location_code($)
1730{
1731        my $code = shift;
1732        my $letter = chr($code);
1733
1734        # Try the location code as ASCII first, as earlier specifications
1735        # suggested this. As newer specifications don't mention it anymore,
1736        # we still fall back to binary.
1737        printl_cond(spd_written($code), "Manufacturing Location Code",
1738                    $letter =~ m/^[\w\d]$/ ? $letter : sprintf("0x%.2X", $code));
1739}
1740
1741sub printl_mfg_assembly_serial(@)
1742{
1743        printl_cond(spd_written(@_), "Assembly Serial Number",
1744                    sprintf("0x%02X%02X%02X%02X", @_));
1745}
1746
1747# Parameter: EEPROM bytes 0-175 (using 117-149)
1748sub decode_ddr3_mfg_data($)
1749{
1750        my $bytes = shift;
1751
1752        prints("Manufacturer Data");
1753
1754        printl("Module Manufacturer",
1755               manufacturer_ddr3($bytes->[117], $bytes->[118]));
1756
1757        printl_cond(spd_written(@{$bytes}[148..149]),
1758                    "DRAM Manufacturer",
1759                    manufacturer_ddr3($bytes->[148], $bytes->[149]));
1760
1761        printl_mfg_location_code($bytes->[119]);
1762
1763        printl_cond(spd_written(@{$bytes}[120..121]),
1764                    "Manufacturing Date",
1765                    manufacture_date($bytes->[120], $bytes->[121]));
1766
1767        printl_mfg_assembly_serial(@{$bytes}[122..125]);
1768
1769        printl("Part Number", part_number(@{$bytes}[128..145]));
1770
1771        printl_cond(spd_written(@{$bytes}[146..147]),
1772                    "Revision Code",
1773                    sprintf("0x%02X%02X", $bytes->[146], $bytes->[147]));
1774}
1775
1776# Parameter: EEPROM bytes 0-127 (using 64-98)
1777sub decode_manufacturing_information($)
1778{
1779        my $bytes = shift;
1780        my ($temp, $extra);
1781
1782        prints("Manufacturing Information");
1783
1784        # $extra is a reference to an array containing up to
1785        # 7 extra bytes from the Manufacturer field. Sometimes
1786        # these bytes are filled with interesting data.
1787        ($temp, $extra) = manufacturer(@{$bytes}[64..71]);
1788        printl("Manufacturer", $temp);
1789        $temp = manufacturer_data(@{$extra});
1790        printl_cond(defined $temp, "Custom Manufacturer Data", $temp);
1791
1792        printl_mfg_location_code($bytes->[72]);
1793
1794        printl("Part Number", part_number(@{$bytes}[73..90]));
1795
1796        printl_cond(spd_written(@{$bytes}[91..92]), "Revision Code",
1797                    sprintf("0x%02X%02X", @{$bytes}[91..92]));
1798
1799        printl_cond(spd_written(@{$bytes}[93..94]), "Manufacturing Date",
1800               manufacture_date($bytes->[93], $bytes->[94]));
1801
1802        printl_mfg_assembly_serial(@{$bytes}[95..98]);
1803}
1804
1805# Parameter: EEPROM bytes 0-127 (using 126-127)
1806sub decode_intel_spec_freq($)
1807{
1808        my $bytes = shift;
1809        my $temp;
1810
1811        prints("Intel Specification");
1812
1813        if ($bytes->[126] == 0x66) { $temp = "66 MHz"; }
1814        elsif ($bytes->[126] == 100) { $temp = "100 MHz or 133 MHz"; }
1815        elsif ($bytes->[126] == 133) { $temp = "133 MHz"; }
1816        else { $temp = "Undefined!"; }
1817        printl("Frequency", $temp);
1818
1819        $temp = "";
1820        if ($bytes->[127] & 1) { $temp .= "Intel Concurrent Auto-precharge\n"; }
1821        if ($bytes->[127] & 2) { $temp .= "CAS Latency = 2\n"; }
1822        if ($bytes->[127] & 4) { $temp .= "CAS Latency = 3\n"; }
1823        if ($bytes->[127] & 8) { $temp .= "Junction Temp A (100 degrees C)\n"; }
1824        else { $temp .= "Junction Temp B (90 degrees C)\n"; }
1825        if ($bytes->[127] & 16) { $temp .= "CLK 3 Connected\n"; }
1826        if ($bytes->[127] & 32) { $temp .= "CLK 2 Connected\n"; }
1827        if ($bytes->[127] & 64) { $temp .= "CLK 1 Connected\n"; }
1828        if ($bytes->[127] & 128) { $temp .= "CLK 0 Connected\n"; }
1829        if (($bytes->[127] & 192) == 192) { $temp .= "Double-sided DIMM\n"; }
1830        elsif (($bytes->[127] & 192) != 0) { $temp .= "Single-sided DIMM\n"; }
1831        printl("Details for 100 MHz Support", $temp);
1832}
1833
1834# Read various hex dump style formats: hexdump, hexdump -C, i2cdump, eeprog
1835# note that normal 'hexdump' format on a little-endian system byte-swaps
1836# words, using hexdump -C is better.
1837sub read_hexdump($)
1838{
1839        my $addr = 0;
1840        my $repstart = 0;
1841        my @bytes;
1842        my $header = 1;
1843        my $word = 0;
1844
1845        # Look in the cache first
1846        return @{$hexdump_cache{$_[0]}} if exists $hexdump_cache{$_[0]};
1847
1848        open F, '<', $_[0] or die "Unable to open: $_[0]";
1849        while (<F>) {
1850                chomp;
1851                if (/^\*$/) {
1852                        $repstart = $addr;
1853                        next;
1854                }
1855                /^(?:0000 )?([a-f\d]{2,8}):?\s+((:?[a-f\d]{4}\s*){8}|(:?[a-f\d]{2}\s*){16})/i ||
1856                /^(?:0000 )?([a-f\d]{2,8}):?\s*$/i;
1857                next if (!defined $1 && $header);               # skip leading unparsed lines
1858
1859                defined $1 or die "Unable to parse input";
1860                $header = 0;
1861
1862                $addr = hex $1;
1863                if ($repstart) {
1864                        @bytes[$repstart .. ($addr-1)] =
1865                                (@bytes[($repstart-16)..($repstart-1)]) x (($addr-$repstart)/16);
1866                        $repstart = 0;
1867                }
1868                last unless defined $2;
1869                foreach (split(/\s+/, $2)) {
1870                        if (/^(..)(..)$/) {
1871                                $word |= 1;
1872                                if ($use_hexdump eq LITTLEENDIAN) {
1873                                        $bytes[$addr++] = hex($2);
1874                                        $bytes[$addr++] = hex($1);
1875                                } else {
1876                                        $bytes[$addr++] = hex($1);
1877                                        $bytes[$addr++] = hex($2);
1878                                }
1879                        } else {
1880                                $bytes[$addr++] = hex($_);
1881                        }
1882                }
1883        }
1884        close F;
1885        $header and die "Unable to parse any data from hexdump '$_[0]'";
1886        $word and printc("Using $use_hexdump 16-bit hex dump");
1887
1888        # Cache the data for later use
1889        $hexdump_cache{$_[0]} = \@bytes;
1890        return @bytes;
1891}
1892
1893# Returns the (total, used) number of bytes in the EEPROM,
1894# assuming it is a non-Rambus SPD EEPROM.
1895sub spd_sizes($)
1896{
1897        my $bytes = shift;
1898
1899        if ($bytes->[2] >= 9) {
1900                # For FB-DIMM and newer, decode number of bytes written
1901                my $spd_len = ($bytes->[0] >> 4) & 7;
1902                my $size = 64 << ($bytes->[0] & 15);
1903                if ($spd_len == 0) {
1904                        return ($size, 128);
1905                } elsif ($spd_len == 1) {
1906                        return ($size, 176);
1907                } elsif ($spd_len == 2) {
1908                        return ($size, 256);
1909                } else {
1910                        return (64, 64);
1911                }
1912        } else {
1913                my $size;
1914                if ($bytes->[1] <= 14) {
1915                        $size = 1 << $bytes->[1];
1916                } elsif ($bytes->[1] == 0) {
1917                        $size = "RFU";
1918                } else { $size = "ERROR!" }
1919
1920                return ($size, ($bytes->[0] < 64) ? 64 : $bytes->[0]);
1921        }
1922}
1923
1924# Read bytes from SPD-EEPROM
1925# Note: offset must be a multiple of 16!
1926sub readspd($$$)
1927{
1928        my ($offset, $size, $dimm_i) = @_;
1929        my @bytes;
1930        if ($use_hexdump) {
1931                @bytes = read_hexdump($dimm_i);
1932                return @bytes[$offset..($offset + $size - 1)];
1933        } elsif ($use_sysfs) {
1934                # Kernel 2.6 with sysfs
1935                sysopen(HANDLE, "$dimm_i/eeprom", O_RDONLY)
1936                        or die "Cannot open $dimm_i/eeprom";
1937                binmode HANDLE;
1938                sysseek(HANDLE, $offset, SEEK_SET)
1939                        or die "Cannot seek $dimm_i/eeprom";
1940                sysread(HANDLE, my $eeprom, $size)
1941                        or die "Cannot read $dimm_i/eeprom";
1942                close HANDLE;
1943                @bytes = unpack("C*", $eeprom);
1944        } else {
1945                # Kernel 2.4 with procfs
1946                for my $i (0 .. ($size-1)/16) {
1947                        my $hexoff = sprintf('%02x', $offset + $i * 16);
1948                        push @bytes, split(" ", `cat $dimm_i/$hexoff`);
1949                }
1950        }
1951        return @bytes;
1952}
1953
1954# Calculate and verify checksum of first 63 bytes
1955sub checksum($)
1956{
1957        my $bytes = shift;
1958        my $dimm_checksum = 0;
1959        local $_;
1960
1961        $dimm_checksum += $bytes->[$_] foreach (0 .. 62);
1962        $dimm_checksum &= 0xff;
1963
1964        return ("EEPROM Checksum of bytes 0-62",
1965                ($bytes->[63] == $dimm_checksum) ? 1 : 0,
1966                sprintf('0x%02X', $bytes->[63]),
1967                sprintf('0x%02X', $dimm_checksum));
1968}
1969
1970# Calculate and verify CRC
1971sub check_crc($)
1972{
1973        my $bytes = shift;
1974        my $crc = 0;
1975        my $crc_cover = $bytes->[0] & 0x80 ? 116 : 125;
1976        my $crc_ptr = 0;
1977        my $crc_bit;
1978
1979        while ($crc_ptr <= $crc_cover) {
1980                $crc = $crc ^ ($bytes->[$crc_ptr] << 8);
1981                for ($crc_bit = 0; $crc_bit < 8; $crc_bit++) {
1982                        if ($crc & 0x8000) {
1983                                $crc = ($crc << 1) ^ 0x1021;
1984                        } else {
1985                                $crc = $crc << 1
1986                        }
1987                }
1988                $crc_ptr++;
1989        }
1990        $crc &= 0xffff;
1991
1992        my $dimm_crc = ($bytes->[127] << 8) | $bytes->[126];
1993        return ("EEPROM CRC of bytes 0-$crc_cover",
1994                ($dimm_crc == $crc) ? 1 : 0,
1995                sprintf("0x%04X", $dimm_crc),
1996                sprintf("0x%04X", $crc));
1997}
1998
1999# Parse command-line
2000foreach (@ARGV) {
2001        if ($_ eq '-h' || $_ eq '--help') {
2002                print "Usage: $0 [-c] [-f [-b]] [-x|-X file [files..]]\n",
2003                        "       $0 -h\n\n",
2004                        "  -f, --format            Print nice html output\n",
2005                        "  -b, --bodyonly          Don't print html header\n",
2006                        "                          (useful for postprocessing the output)\n",
2007                        "      --side-by-side      Display all DIMMs side-by-side if possible\n",
2008                        "      --merge-cells       Merge neighbour cells with identical values\n",
2009                        "                          (side-by-side output only, default)\n",
2010                        "      --no-merge-cells    Don't merge neighbour cells with identical values\n",
2011                        "                          (side-by-side output only)\n",
2012                        "  -c, --checksum          Decode completely even if checksum fails\n",
2013                        "  -x,                     Read data from hexdump files\n",
2014                        "  -X,                     Same as -x except treat multibyte hex\n",
2015                        "                          data as little endian\n",
2016                        "  -h, --help              Display this usage summary\n";
2017                print <<"EOF";
2018
2019Hexdumps can be the output from hexdump, hexdump -C, i2cdump, eeprog and
2020likely many other progams producing hex dumps of one kind or another.  Note
2021that the default output of "hexdump" will be byte-swapped on little-endian
2022systems and you must use -X instead of -x, otherwise the dump will not be
2023parsed correctly.  It is better to use "hexdump -C", which is not ambiguous.
2024EOF
2025                exit;
2026        }
2027
2028        if ($_ eq '-f' || $_ eq '--format') {
2029                $opt_html = 1;
2030                next;
2031        }
2032        if ($_ eq '-b' || $_ eq '--bodyonly') {
2033                $opt_bodyonly = 1;
2034                next;
2035        }
2036        if ($_ eq '--side-by-side') {
2037                $opt_side_by_side = 1;
2038                next;
2039        }
2040        if ($_ eq '--merge-cells') {
2041                $opt_merge = 1;
2042                next;
2043        }
2044        if ($_ eq '--no-merge-cells') {
2045                $opt_merge = 0;
2046                next;
2047        }
2048        if ($_ eq '-c' || $_ eq '--checksum') {
2049                $opt_igncheck = 1;
2050                next;
2051        }
2052        if ($_ eq '-x') {
2053                $use_hexdump = BIGENDIAN;
2054                next;
2055        }
2056        if ($_ eq '-X') {
2057                $use_hexdump = LITTLEENDIAN;
2058                next;
2059        }
2060
2061        if (m/^-/) {
2062                print STDERR "Unrecognized option $_\n";
2063                exit;
2064        }
2065
2066        push @dimm, { eeprom => basename($_), file => $_ } if $use_hexdump;
2067}
2068
2069# Default values
2070$opt_merge = 1 unless defined $opt_merge;
2071
2072# From a sysfs device path and an attribute name, return the attribute
2073# value, or undef (stolen from sensors-detect)
2074sub sysfs_device_attribute
2075{
2076        my ($device, $attr) = @_;
2077        my $value;
2078
2079        open(local *FILE, "$device/$attr") or return "";
2080        $value = <FILE>;
2081        close(FILE);
2082        return unless defined $value;
2083
2084        chomp($value);
2085        return $value;
2086}
2087
2088sub get_dimm_list
2089{
2090        my (@dirs, $dir, $opened, $file, @files);
2091
2092        if ($use_sysfs) {
2093                @dirs = ('/sys/bus/i2c/drivers/eeprom', '/sys/bus/i2c/drivers/at24');
2094        } else {
2095                @dirs = ('/proc/sys/dev/sensors');
2096        }
2097
2098        foreach $dir (@dirs) {
2099                next unless opendir(local *DIR, $dir);
2100                $opened++;
2101                while (defined($file = readdir(DIR))) {
2102                        if ($use_sysfs) {
2103                                # We look for I2C devices like 0-0050 or 2-0051
2104                                next unless $file =~ /^\d+-[\da-f]+$/i;
2105                                next unless -d "$dir/$file";
2106
2107                                # Device name must be eeprom (driver eeprom)
2108                                # or spd (driver at24)
2109                                my $attr = sysfs_device_attribute("$dir/$file", "name");
2110                                next unless defined $attr &&
2111                                            ($attr eq "eeprom" || $attr eq "spd");
2112                        } else {
2113                                next unless $file =~ /^eeprom-/;
2114                        }
2115                        push @files, { eeprom => "$file",
2116                                       file => "$dir/$file" };
2117                }
2118                close(DIR);
2119        }
2120
2121        if (!$opened) {
2122                print STDERR "No EEPROM found, try loading the eeprom or at24 module\n";
2123                exit;
2124        }
2125
2126        return sort { $a->{file} cmp $b->{file} } @files;
2127}
2128
2129# @dimm is a list of hashes. There's one hash for each EEPROM we found.
2130# Each hash has the following keys:
2131#  * eeprom: Name of the eeprom data file
2132#  * file: Full path to the eeprom data file
2133#  * bytes: The EEPROM data (array)
2134#  * is_rambus: Whether this is a RAMBUS DIMM or not (boolean)
2135#  * chk_label: The label to display for the checksum or CRC
2136#  * chk_valid: Whether the checksum or CRC is valid or not (boolean)
2137#  * chk_spd: The checksum or CRC value found in the EEPROM
2138#  * chk_calc: The checksum or CRC computed from the EEPROM data
2139# Keys are added over time.
2140@dimm = get_dimm_list() unless $use_hexdump;
2141
2142for my $i (0 .. $#dimm) {
2143        my @bytes = readspd(0, 128, $dimm[$i]->{file});
2144        $dimm[$i]->{bytes} = \@bytes;
2145        $dimm[$i]->{is_rambus} = $bytes[0] < 4;         # Simple heuristic
2146        if ($dimm[$i]->{is_rambus} || $bytes[2] < 9) {
2147                ($dimm[$i]->{chk_label}, $dimm[$i]->{chk_valid},
2148                 $dimm[$i]->{chk_spd}, $dimm[$i]->{chk_calc}) =
2149                        checksum(\@bytes);
2150        } else {
2151                ($dimm[$i]->{chk_label}, $dimm[$i]->{chk_valid},
2152                 $dimm[$i]->{chk_spd}, $dimm[$i]->{chk_calc}) =
2153                        check_crc(\@bytes);
2154        }
2155}
2156
2157# Checksum or CRC validation
2158if (!$opt_igncheck) {
2159        for (my $i = 0; $i < @dimm; ) {
2160                if ($dimm[$i]->{chk_valid}) {
2161                        $i++;
2162                } else {
2163                        splice(@dimm, $i, 1);
2164                }
2165        }
2166}
2167
2168
2169if ($opt_html && !$opt_bodyonly) {
2170        print "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.1//EN' \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n\n",
2171              "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\">\n",
2172              "<head>\n",
2173              "\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" />\n",
2174              "\t<title>PC DIMM Serial Presence Detect Tester/Decoder Output</title>\n",
2175              "</head>\n\n",
2176              "<body>\n";
2177}
2178
2179printc("decode-dimms version $revision");
2180printh('Memory Serial Presence Detect Decoder',
2181'By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner,
2182Jean Delvare, Trent Piepho and others');
2183
2184# Process the valid entries
2185for $current (0 .. $#dimm) {
2186        my @bytes = @{$dimm[$current]->{bytes}};
2187
2188        if ($opt_side_by_side) {
2189                printl("Decoding EEPROM", $dimm[$current]->{eeprom});
2190        }
2191
2192        if (!$use_hexdump) {
2193                if ($dimm[$current]->{file} =~ /-([\da-f]+)$/i) {
2194                        my $dimm_num = hex($1) - 0x50 + 1;
2195                        if ($dimm_num >= 1 && $dimm_num <= 8) {
2196                                printl("Guessing DIMM is in", "bank $dimm_num");
2197                        }
2198                }
2199        }
2200
2201# Decode first 3 bytes (0-2)
2202        prints("SPD EEPROM Information");
2203
2204        printl($dimm[$current]->{chk_label}, ($dimm[$current]->{chk_valid} ?
2205                sprintf("OK (%s)", $dimm[$current]->{chk_calc}) :
2206                sprintf("Bad\n(found %s, calculated %s)",
2207                        $dimm[$current]->{chk_spd}, $dimm[$current]->{chk_calc})));
2208
2209        my $temp;
2210        if ($dimm[$current]->{is_rambus}) {
2211                if ($bytes[0] == 1) { $temp = "0.7"; }
2212                elsif ($bytes[0] == 2) { $temp = "1.0"; }
2213                elsif ($bytes[0] == 0) { $temp = "Invalid"; }
2214                else { $temp = "Reserved"; }
2215                printl("SPD Revision", $temp);
2216        } else {
2217                my ($spd_size, $spd_used) = spd_sizes(\@bytes);
2218                printl("# of bytes written to SDRAM EEPROM", $spd_used);
2219                printl("Total number of bytes in EEPROM", $spd_size);
2220
2221                # If there's more data than what we've read, let's
2222                # read it now.  DDR3 will need this data.
2223                if ($spd_used > @bytes) {
2224                        push (@bytes,
2225                              readspd(@bytes, $spd_used - @bytes,
2226                                      $dimm[$current]->{file}));
2227                }
2228        }
2229
2230        my $type = sprintf("Unknown (0x%02x)", $bytes[2]);
2231        if ($dimm[$current]->{is_rambus}) {
2232                if ($bytes[2] == 1) { $type = "Direct Rambus"; }
2233                elsif ($bytes[2] == 17) { $type = "Rambus"; }
2234        } else {
2235                my @type_list = (
2236                        "Reserved", "FPM DRAM",         # 0, 1
2237                        "EDO", "Pipelined Nibble",      # 2, 3
2238                        "SDR SDRAM", "Multiplexed ROM", # 4, 5
2239                        "DDR SGRAM", "DDR SDRAM",       # 6, 7
2240                        "DDR2 SDRAM", "FB-DIMM",        # 8, 9
2241                        "FB-DIMM Probe", "DDR3 SDRAM",  # 10, 11
2242                );
2243                if ($bytes[2] < @type_list) {
2244                        $type = $type_list[$bytes[2]];
2245                }
2246        }
2247        printl("Fundamental Memory type", $type);
2248
2249# Decode next 61 bytes (3-63, depend on memory type)
2250        $decode_callback{$type}->(\@bytes)
2251                if exists $decode_callback{$type};
2252
2253        if ($type eq "DDR3 SDRAM") {
2254                # Decode DDR3-specific manufacturing data in bytes
2255                # 117-149
2256                decode_ddr3_mfg_data(\@bytes)
2257        } else {
2258                # Decode next 35 bytes (64-98, common to most
2259                # memory types)
2260                decode_manufacturing_information(\@bytes);
2261        }
2262
2263# Next 27 bytes (99-125) are manufacturer specific, can't decode
2264
2265# Last 2 bytes (126-127) are reserved, Intel used them as an extension
2266        if ($type eq "SDR SDRAM") {
2267                decode_intel_spec_freq(\@bytes);
2268        }
2269}
2270
2271# Side-by-side output format is only possible if all DIMMs have a similar
2272# output structure
2273if ($opt_side_by_side) {
2274        for $current (1 .. $#dimm) {
2275                my @ref_output = @{$dimm[0]->{output}};
2276                my @test_output = @{$dimm[$current]->{output}};
2277                my $line;
2278
2279                if (scalar @ref_output != scalar @test_output) {
2280                        $opt_side_by_side = 0;
2281                        last;
2282                }
2283
2284                for ($line = 0; $line < @ref_output; $line++) {
2285                        my ($ref_func, $ref_label, @ref_dummy) = @{$ref_output[$line]};
2286                        my ($test_func, $test_label, @test_dummy) = @{$test_output[$line]};
2287
2288                        if ($ref_func != $test_func || $ref_label ne $test_label) {
2289                                $opt_side_by_side = 0;
2290                                last;
2291                        }
2292                }
2293        }
2294
2295        if (!$opt_side_by_side) {
2296                printc("Side-by-side output only possible if all DIMMS are similar\n");
2297
2298                # Discard "Decoding EEPROM" entry from all outputs
2299                for $current (0 .. $#dimm) {
2300                        shift(@{$dimm[$current]->{output}});
2301                }
2302        }
2303}
2304
2305# Check if all dimms have the same value for a given line
2306sub line_has_same_values($)
2307{
2308        my $line = shift;
2309        my $value = $dimm[0]->{output}->[$line]->[2];
2310
2311        # Skip lines with no values (headers)
2312        return 1 unless defined $value;
2313
2314        for my $other (1 .. $#dimm) {
2315                return 0 unless $value eq $dimm[$other]->{output}->[$line]->[2];
2316        }
2317
2318        return 1;
2319}
2320
2321# Find out the longest value string to adjust the column width
2322sub find_col_width($)
2323{
2324        my $width = shift;
2325
2326        return $width unless $opt_side_by_side && !$opt_html;
2327
2328        my $line;
2329        my $line_nr = @{$dimm[0]->{output}};
2330
2331        for ($line = 0; $line < $line_nr; $line++) {
2332                next if $opt_merge && line_has_same_values($line);
2333
2334                my @strings;
2335
2336                for my $current (0 .. $#dimm) {
2337                        my $value = $dimm[$current]->{output}->[$line]->[2];
2338                        push @strings, split("\n", $value) if defined $value;
2339                }
2340
2341                foreach my $line2 (@strings) {
2342                        my $len = length($line2);
2343                        $width = $len if $len > $width;
2344                }
2345        }
2346
2347        return $width;
2348}
2349
2350$sbs_col_width = find_col_width(15);
2351
2352# Print the decoded information for all DIMMs
2353for $current (0 .. $#dimm) {
2354        if ($opt_side_by_side) {
2355                print "\n\n";
2356        } else {
2357                printl2("\n\nDecoding EEPROM", $dimm[$current]->{file},
2358                        "text-decoration: underline; font-weight: bold;");
2359        }
2360        print "<table border=\"1\">\n" if $opt_html;
2361
2362        my @output = @{$dimm[$current]->{output}};
2363        for (my $line = 0; $line < @output; $line++) {
2364                my ($func, @param) = @{$output[$line]};
2365
2366                if ($opt_side_by_side) {
2367                        foreach ($current+1 .. $#dimm) {
2368                                my @xoutput = @{$dimm[$_]->{output}};
2369                                if (@{$xoutput[$line]} == 3) {
2370                                        # Line with data, stack all values
2371                                        push @param, @{$xoutput[$line]}[2];
2372                                } else {
2373                                        # Separator, make it span
2374                                        push @param, scalar @dimm;
2375                                }
2376                        }
2377                }
2378
2379                $func->(@param);
2380        }
2381
2382        print "</table>\n" if $opt_html;
2383        last if $opt_side_by_side;
2384}
2385printl2("\n\nNumber of SDRAM DIMMs detected and decoded", scalar @dimm);
2386
2387print "</body></html>\n" if ($opt_html && !$opt_bodyonly);
Note: See TracBrowser for help on using the browser.