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

Revision 6249, 80.3 KB (checked in by khali, 4 months ago)

decode-dimms: Fix DDR3 extended temp range refresh rate decoding

Based on a patch from Peter Missel. The meaning of byte 31, bit 1,
was inverted. Also, this bit is only meaningful if the extended
temperature range is supported (bit 0 is set.)

  • 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_cond($bytes->[31] & 1,
1595                    "Refresh Rate in extended temp range",
1596                    ($bytes->[31] & 2) ? "1X" : "2X");
1597        printl("Auto Self-Refresh?", ($bytes->[31] & 4) ? "Yes" : "No");
1598        printl("On-Die Thermal Sensor readout?",
1599                ($bytes->[31] & 8) ? "Yes" : "No");
1600        printl("Partial Array Self-Refresh?",
1601                ($bytes->[31] & 128) ? "Yes" : "No");
1602        printl("Module Thermal Sensor",
1603                ($bytes->[32] & 128) ? "Yes" : "No");
1604        printl("SDRAM Device Type", ddr3_device_type($bytes->[33]));
1605
1606        # Following bytes are type-specific, so don't continue if type
1607        # isn't known.
1608        return if $bytes->[3] == 0 || $bytes->[3] > $#module_types;
1609
1610        if ($module_types[$bytes->[3]]->{family} == DDR3_UNBUFFERED ||
1611            $module_types[$bytes->[3]]->{family} == DDR3_REGISTERED ||
1612            $module_types[$bytes->[3]]->{family} == DDR3_CLOCKED ||
1613            $module_types[$bytes->[3]]->{family} == DDR3_LOAD_REDUCED) {
1614                prints("Physical Characteristics");
1615                printl("Module Height", (($bytes->[60] & 31) + 15) . " mm");
1616                printl("Module Thickness", sprintf("%d mm front, %d mm back",
1617                                                ($bytes->[61] & 15) + 1,
1618                                                (($bytes->[61] >> 4) & 15) +1));
1619                printl("Module Width", $module_types[$bytes->[3]]->{width});
1620                printl("Module Reference Card", ddr3_reference_card($bytes->[62], $bytes->[60]));
1621
1622                printl_cond($module_types[$bytes->[3]]->{family} == DDR3_UNBUFFERED,
1623                            "Rank 1 Mapping", $bytes->[63] & 0x01 ? "Mirrored" : "Standard");
1624        }
1625
1626        if ($module_types[$bytes->[3]]->{family} == DDR3_REGISTERED) {
1627                prints("Registered DIMM");
1628
1629                my @rows = ("Undefined", 1, 2, 4);
1630                printl("# DRAM Rows", $rows[($bytes->[63] >> 2) & 3]);
1631                printl("# Registers", $rows[$bytes->[63] & 3]);
1632                printl("Register manufacturer",
1633                        manufacturer_ddr3($bytes->[65], $bytes->[66]));
1634                printl("Register device type",
1635                                (($bytes->[68] & 7) == 0) ? "SSTE32882" :
1636                                        "Undefined");
1637                printl_cond($bytes->[67] != 0xff,
1638                            "Register revision", ddr3_revision_number($bytes->[67]));
1639                printl("Heat spreader", $bytes->[64] & 0x80 ? "Yes" : "No");
1640        }
1641
1642        if ($module_types[$bytes->[3]]->{family} == DDR3_LOAD_REDUCED) {
1643                prints("Load Reduced DIMM");
1644
1645                my @rows = ("Undefined", 1, 2, "Reserved");
1646                printl("# DRAM Rows", $rows[($bytes->[63] >> 2) & 3]);
1647                my @mirroring = ("None", "Odd ranks", "Reserved", "Reserved");
1648                printl("Mirroring", $mirroring[$bytes->[63] & 3]);
1649                printl("Rank Numbering", $bytes->[63] & 0x20 ? "Even only" : "Contiguous");
1650                printl("Buffer Orientation", $bytes->[63] & 0x10 ? "Horizontal" : "Vertical");
1651                printl("Register manufacturer",
1652                        manufacturer_ddr3($bytes->[65], $bytes->[66]));
1653                printl_cond($bytes->[64] != 0xff,
1654                            "Buffer Revision", ddr3_revision_number($bytes->[64]));
1655                printl("Heat spreader", $bytes->[63] & 0x80 ? "Yes" : "No");
1656        }
1657
1658}
1659
1660# Parameter: EEPROM bytes 0-127 (using 4-5)
1661sub decode_direct_rambus($)
1662{
1663        my $bytes = shift;
1664
1665#size computation
1666        prints("Memory Characteristics");
1667
1668        my $ii;
1669
1670        $ii = ($bytes->[4] & 0x0f) + ($bytes->[4] >> 4) + ($bytes->[5] & 0x07) - 13;
1671
1672        if ($ii > 0 && $ii < 16) {
1673                printl("Size", (1 << $ii) . " MB");
1674        } else {
1675                printl("Size", sprintf("INVALID: 0x%02x, 0x%02x",
1676                                       $bytes->[4], $bytes->[5]));
1677        }
1678}
1679
1680# Parameter: EEPROM bytes 0-127 (using 3-5)
1681sub decode_rambus($)
1682{
1683        my $bytes = shift;
1684
1685#size computation
1686        prints("Memory Characteristics");
1687
1688        my $ii;
1689
1690        $ii = ($bytes->[3] & 0x0f) + ($bytes->[3] >> 4) + ($bytes->[5] & 0x07) - 13;
1691
1692        if ($ii > 0 && $ii < 16) {
1693                printl("Size", (1 << $ii) . " MB");
1694        } else {
1695                printl("Size", "INVALID: " . sprintf("0x%02x, 0x%02x",
1696                                               $bytes->[3], $bytes->[5]));
1697        }
1698}
1699
1700%decode_callback = (
1701        "SDR SDRAM"     => \&decode_sdr_sdram,
1702        "DDR SDRAM"     => \&decode_ddr_sdram,
1703        "DDR2 SDRAM"    => \&decode_ddr2_sdram,
1704        "DDR3 SDRAM"    => \&decode_ddr3_sdram,
1705        "Direct Rambus" => \&decode_direct_rambus,
1706        "Rambus"        => \&decode_rambus,
1707);
1708
1709# Parameter: Manufacturing year/week bytes
1710sub manufacture_date($$)
1711{
1712        my ($year, $week) = @_;
1713
1714        # In theory the year and week are in BCD format, but
1715        # this is not always true in practice :(
1716        if (($year & 0xf0) <= 0x90 && ($year & 0x0f) <= 0x09
1717         && ($week & 0xf0) <= 0x90 && ($week & 0x0f) <= 0x09) {
1718                # Note that this heuristic will break in year 2080
1719                return sprintf("%d%02X-W%02X",
1720                                $year >= 0x80 ? 19 : 20, $year, $week);
1721        # Fallback to binary format if it seems to make sense
1722        } elsif ($year <= 99 && $week >= 1 && $week <= 53) {
1723                return sprintf("%d%02d-W%02d",
1724                                $year >= 80 ? 19 : 20, $year, $week);
1725        } else {
1726                return sprintf("0x%02X%02X", $year, $week);
1727        }
1728}
1729
1730sub printl_mfg_location_code($)
1731{
1732        my $code = shift;
1733        my $letter = chr($code);
1734
1735        # Try the location code as ASCII first, as earlier specifications
1736        # suggested this. As newer specifications don't mention it anymore,
1737        # we still fall back to binary.
1738        printl_cond(spd_written($code), "Manufacturing Location Code",
1739                    $letter =~ m/^[\w\d]$/ ? $letter : sprintf("0x%.2X", $code));
1740}
1741
1742sub printl_mfg_assembly_serial(@)
1743{
1744        printl_cond(spd_written(@_), "Assembly Serial Number",
1745                    sprintf("0x%02X%02X%02X%02X", @_));
1746}
1747
1748# Parameter: EEPROM bytes 0-175 (using 117-149)
1749sub decode_ddr3_mfg_data($)
1750{
1751        my $bytes = shift;
1752
1753        prints("Manufacturer Data");
1754
1755        printl("Module Manufacturer",
1756               manufacturer_ddr3($bytes->[117], $bytes->[118]));
1757
1758        printl_cond(spd_written(@{$bytes}[148..149]),
1759                    "DRAM Manufacturer",
1760                    manufacturer_ddr3($bytes->[148], $bytes->[149]));
1761
1762        printl_mfg_location_code($bytes->[119]);
1763
1764        printl_cond(spd_written(@{$bytes}[120..121]),
1765                    "Manufacturing Date",
1766                    manufacture_date($bytes->[120], $bytes->[121]));
1767
1768        printl_mfg_assembly_serial(@{$bytes}[122..125]);
1769
1770        printl("Part Number", part_number(@{$bytes}[128..145]));
1771
1772        printl_cond(spd_written(@{$bytes}[146..147]),
1773                    "Revision Code",
1774                    sprintf("0x%02X%02X", $bytes->[146], $bytes->[147]));
1775}
1776
1777# Parameter: EEPROM bytes 0-127 (using 64-98)
1778sub decode_manufacturing_information($)
1779{
1780        my $bytes = shift;
1781        my ($temp, $extra);
1782
1783        prints("Manufacturing Information");
1784
1785        # $extra is a reference to an array containing up to
1786        # 7 extra bytes from the Manufacturer field. Sometimes
1787        # these bytes are filled with interesting data.
1788        ($temp, $extra) = manufacturer(@{$bytes}[64..71]);
1789        printl("Manufacturer", $temp);
1790        $temp = manufacturer_data(@{$extra});
1791        printl_cond(defined $temp, "Custom Manufacturer Data", $temp);
1792
1793        printl_mfg_location_code($bytes->[72]);
1794
1795        printl("Part Number", part_number(@{$bytes}[73..90]));
1796
1797        printl_cond(spd_written(@{$bytes}[91..92]), "Revision Code",
1798                    sprintf("0x%02X%02X", @{$bytes}[91..92]));
1799
1800        printl_cond(spd_written(@{$bytes}[93..94]), "Manufacturing Date",
1801               manufacture_date($bytes->[93], $bytes->[94]));
1802
1803        printl_mfg_assembly_serial(@{$bytes}[95..98]);
1804}
1805
1806# Parameter: EEPROM bytes 0-127 (using 126-127)
1807sub decode_intel_spec_freq($)
1808{
1809        my $bytes = shift;
1810        my $temp;
1811
1812        prints("Intel Specification");
1813
1814        if ($bytes->[126] == 0x66) { $temp = "66 MHz"; }
1815        elsif ($bytes->[126] == 100) { $temp = "100 MHz or 133 MHz"; }
1816        elsif ($bytes->[126] == 133) { $temp = "133 MHz"; }
1817        else { $temp = "Undefined!"; }
1818        printl("Frequency", $temp);
1819
1820        $temp = "";
1821        if ($bytes->[127] & 1) { $temp .= "Intel Concurrent Auto-precharge\n"; }
1822        if ($bytes->[127] & 2) { $temp .= "CAS Latency = 2\n"; }
1823        if ($bytes->[127] & 4) { $temp .= "CAS Latency = 3\n"; }
1824        if ($bytes->[127] & 8) { $temp .= "Junction Temp A (100 degrees C)\n"; }
1825        else { $temp .= "Junction Temp B (90 degrees C)\n"; }
1826        if ($bytes->[127] & 16) { $temp .= "CLK 3 Connected\n"; }
1827        if ($bytes->[127] & 32) { $temp .= "CLK 2 Connected\n"; }
1828        if ($bytes->[127] & 64) { $temp .= "CLK 1 Connected\n"; }
1829        if ($bytes->[127] & 128) { $temp .= "CLK 0 Connected\n"; }
1830        if (($bytes->[127] & 192) == 192) { $temp .= "Double-sided DIMM\n"; }
1831        elsif (($bytes->[127] & 192) != 0) { $temp .= "Single-sided DIMM\n"; }
1832        printl("Details for 100 MHz Support", $temp);
1833}
1834
1835# Read various hex dump style formats: hexdump, hexdump -C, i2cdump, eeprog
1836# note that normal 'hexdump' format on a little-endian system byte-swaps
1837# words, using hexdump -C is better.
1838sub read_hexdump($)
1839{
1840        my $addr = 0;
1841        my $repstart = 0;
1842        my @bytes;
1843        my $header = 1;
1844        my $word = 0;
1845
1846        # Look in the cache first
1847        return @{$hexdump_cache{$_[0]}} if exists $hexdump_cache{$_[0]};
1848
1849        open F, '<', $_[0] or die "Unable to open: $_[0]";
1850        while (<F>) {
1851                chomp;
1852                if (/^\*$/) {
1853                        $repstart = $addr;
1854                        next;
1855                }
1856                /^(?:0000 )?([a-f\d]{2,8}):?\s+((:?[a-f\d]{4}\s*){8}|(:?[a-f\d]{2}\s*){16})/i ||
1857                /^(?:0000 )?([a-f\d]{2,8}):?\s*$/i;
1858                next if (!defined $1 && $header);               # skip leading unparsed lines
1859
1860                defined $1 or die "Unable to parse input";
1861                $header = 0;
1862
1863                $addr = hex $1;
1864                if ($repstart) {
1865                        @bytes[$repstart .. ($addr-1)] =
1866                                (@bytes[($repstart-16)..($repstart-1)]) x (($addr-$repstart)/16);
1867                        $repstart = 0;
1868                }
1869                last unless defined $2;
1870                foreach (split(/\s+/, $2)) {
1871                        if (/^(..)(..)$/) {
1872                                $word |= 1;
1873                                if ($use_hexdump eq LITTLEENDIAN) {
1874                                        $bytes[$addr++] = hex($2);
1875                                        $bytes[$addr++] = hex($1);
1876                                } else {
1877                                        $bytes[$addr++] = hex($1);
1878                                        $bytes[$addr++] = hex($2);
1879                                }
1880                        } else {
1881                                $bytes[$addr++] = hex($_);
1882                        }
1883                }
1884        }
1885        close F;
1886        $header and die "Unable to parse any data from hexdump '$_[0]'";
1887        $word and printc("Using $use_hexdump 16-bit hex dump");
1888
1889        # Cache the data for later use
1890        $hexdump_cache{$_[0]} = \@bytes;
1891        return @bytes;
1892}
1893
1894# Returns the (total, used) number of bytes in the EEPROM,
1895# assuming it is a non-Rambus SPD EEPROM.
1896sub spd_sizes($)
1897{
1898        my $bytes = shift;
1899
1900        if ($bytes->[2] >= 9) {
1901                # For FB-DIMM and newer, decode number of bytes written
1902                my $spd_len = ($bytes->[0] >> 4) & 7;
1903                my $size = 64 << ($bytes->[0] & 15);
1904                if ($spd_len == 0) {
1905                        return ($size, 128);
1906                } elsif ($spd_len == 1) {
1907                        return ($size, 176);
1908                } elsif ($spd_len == 2) {
1909                        return ($size, 256);
1910                } else {
1911                        return (64, 64);
1912                }
1913        } else {
1914                my $size;
1915                if ($bytes->[1] <= 14) {
1916                        $size = 1 << $bytes->[1];
1917                } elsif ($bytes->[1] == 0) {
1918                        $size = "RFU";
1919                } else { $size = "ERROR!" }
1920
1921                return ($size, ($bytes->[0] < 64) ? 64 : $bytes->[0]);
1922        }
1923}
1924
1925# Read bytes from SPD-EEPROM
1926# Note: offset must be a multiple of 16!
1927sub readspd($$$)
1928{
1929        my ($offset, $size, $dimm_i) = @_;
1930        my @bytes;
1931        if ($use_hexdump) {
1932                @bytes = read_hexdump($dimm_i);
1933                return @bytes[$offset..($offset + $size - 1)];
1934        } elsif ($use_sysfs) {
1935                # Kernel 2.6 with sysfs
1936                sysopen(HANDLE, "$dimm_i/eeprom", O_RDONLY)
1937                        or die "Cannot open $dimm_i/eeprom";
1938                binmode HANDLE;
1939                sysseek(HANDLE, $offset, SEEK_SET)
1940                        or die "Cannot seek $dimm_i/eeprom";
1941                sysread(HANDLE, my $eeprom, $size)
1942                        or die "Cannot read $dimm_i/eeprom";
1943                close HANDLE;
1944                @bytes = unpack("C*", $eeprom);
1945        } else {
1946                # Kernel 2.4 with procfs
1947                for my $i (0 .. ($size-1)/16) {
1948                        my $hexoff = sprintf('%02x', $offset + $i * 16);
1949                        push @bytes, split(" ", `cat $dimm_i/$hexoff`);
1950                }
1951        }
1952        return @bytes;
1953}
1954
1955# Calculate and verify checksum of first 63 bytes
1956sub checksum($)
1957{
1958        my $bytes = shift;
1959        my $dimm_checksum = 0;
1960        local $_;
1961
1962        $dimm_checksum += $bytes->[$_] foreach (0 .. 62);
1963        $dimm_checksum &= 0xff;
1964
1965        return ("EEPROM Checksum of bytes 0-62",
1966                ($bytes->[63] == $dimm_checksum) ? 1 : 0,
1967                sprintf('0x%02X', $bytes->[63]),
1968                sprintf('0x%02X', $dimm_checksum));
1969}
1970
1971# Calculate and verify CRC
1972sub check_crc($)
1973{
1974        my $bytes = shift;
1975        my $crc = 0;
1976        my $crc_cover = $bytes->[0] & 0x80 ? 116 : 125;
1977        my $crc_ptr = 0;
1978        my $crc_bit;
1979
1980        while ($crc_ptr <= $crc_cover) {
1981                $crc = $crc ^ ($bytes->[$crc_ptr] << 8);
1982                for ($crc_bit = 0; $crc_bit < 8; $crc_bit++) {
1983                        if ($crc & 0x8000) {
1984                                $crc = ($crc << 1) ^ 0x1021;
1985                        } else {
1986                                $crc = $crc << 1
1987                        }
1988                }
1989                $crc_ptr++;
1990        }
1991        $crc &= 0xffff;
1992
1993        my $dimm_crc = ($bytes->[127] << 8) | $bytes->[126];
1994        return ("EEPROM CRC of bytes 0-$crc_cover",
1995                ($dimm_crc == $crc) ? 1 : 0,
1996                sprintf("0x%04X", $dimm_crc),
1997                sprintf("0x%04X", $crc));
1998}
1999
2000# Parse command-line
2001foreach (@ARGV) {
2002        if ($_ eq '-h' || $_ eq '--help') {
2003                print "Usage: $0 [-c] [-f [-b]] [-x|-X file [files..]]\n",
2004                        "       $0 -h\n\n",
2005                        "  -f, --format            Print nice html output\n",
2006                        "  -b, --bodyonly          Don't print html header\n",
2007                        "                          (useful for postprocessing the output)\n",
2008                        "      --side-by-side      Display all DIMMs side-by-side if possible\n",
2009                        "      --merge-cells       Merge neighbour cells with identical values\n",
2010                        "                          (side-by-side output only, default)\n",
2011                        "      --no-merge-cells    Don't merge neighbour cells with identical values\n",
2012                        "                          (side-by-side output only)\n",
2013                        "  -c, --checksum          Decode completely even if checksum fails\n",
2014                        "  -x,                     Read data from hexdump files\n",
2015                        "  -X,                     Same as -x except treat multibyte hex\n",
2016                        "                          data as little endian\n",
2017                        "  -h, --help              Display this usage summary\n";
2018                print <<"EOF";
2019
2020Hexdumps can be the output from hexdump, hexdump -C, i2cdump, eeprog and
2021likely many other progams producing hex dumps of one kind or another.  Note
2022that the default output of "hexdump" will be byte-swapped on little-endian
2023systems and you must use -X instead of -x, otherwise the dump will not be
2024parsed correctly.  It is better to use "hexdump -C", which is not ambiguous.
2025EOF
2026                exit;
2027        }
2028
2029        if ($_ eq '-f' || $_ eq '--format') {
2030                $opt_html = 1;
2031                next;
2032        }
2033        if ($_ eq '-b' || $_ eq '--bodyonly') {
2034                $opt_bodyonly = 1;
2035                next;
2036        }
2037        if ($_ eq '--side-by-side') {
2038                $opt_side_by_side = 1;
2039                next;
2040        }
2041        if ($_ eq '--merge-cells') {
2042                $opt_merge = 1;
2043                next;
2044        }
2045        if ($_ eq '--no-merge-cells') {
2046                $opt_merge = 0;
2047                next;
2048        }
2049        if ($_ eq '-c' || $_ eq '--checksum') {
2050                $opt_igncheck = 1;
2051                next;
2052        }
2053        if ($_ eq '-x') {
2054                $use_hexdump = BIGENDIAN;
2055                next;
2056        }
2057        if ($_ eq '-X') {
2058                $use_hexdump = LITTLEENDIAN;
2059                next;
2060        }
2061
2062        if (m/^-/) {
2063                print STDERR "Unrecognized option $_\n";
2064                exit;
2065        }
2066
2067        push @dimm, { eeprom => basename($_), file => $_ } if $use_hexdump;
2068}
2069
2070# Default values
2071$opt_merge = 1 unless defined $opt_merge;
2072
2073# From a sysfs device path and an attribute name, return the attribute
2074# value, or undef (stolen from sensors-detect)
2075sub sysfs_device_attribute
2076{
2077        my ($device, $attr) = @_;
2078        my $value;
2079
2080        open(local *FILE, "$device/$attr") or return "";
2081        $value = <FILE>;
2082        close(FILE);
2083        return unless defined $value;
2084
2085        chomp($value);
2086        return $value;
2087}
2088
2089sub get_dimm_list
2090{
2091        my (@dirs, $dir, $opened, $file, @files);
2092
2093        if ($use_sysfs) {
2094                @dirs = ('/sys/bus/i2c/drivers/eeprom', '/sys/bus/i2c/drivers/at24');
2095        } else {
2096                @dirs = ('/proc/sys/dev/sensors');
2097        }
2098
2099        foreach $dir (@dirs) {
2100                next unless opendir(local *DIR, $dir);
2101                $opened++;
2102                while (defined($file = readdir(DIR))) {
2103                        if ($use_sysfs) {
2104                                # We look for I2C devices like 0-0050 or 2-0051
2105                                next unless $file =~ /^\d+-[\da-f]+$/i;
2106                                next unless -d "$dir/$file";
2107
2108                                # Device name must be eeprom (driver eeprom)
2109                                # or spd (driver at24)
2110                                my $attr = sysfs_device_attribute("$dir/$file", "name");
2111                                next unless defined $attr &&
2112                                            ($attr eq "eeprom" || $attr eq "spd");
2113                        } else {
2114                                next unless $file =~ /^eeprom-/;
2115                        }
2116                        push @files, { eeprom => "$file",
2117                                       file => "$dir/$file" };
2118                }
2119                close(DIR);
2120        }
2121
2122        if (!$opened) {
2123                print STDERR "No EEPROM found, try loading the eeprom or at24 module\n";
2124                exit;
2125        }
2126
2127        return sort { $a->{file} cmp $b->{file} } @files;
2128}
2129
2130# @dimm is a list of hashes. There's one hash for each EEPROM we found.
2131# Each hash has the following keys:
2132#  * eeprom: Name of the eeprom data file
2133#  * file: Full path to the eeprom data file
2134#  * bytes: The EEPROM data (array)
2135#  * is_rambus: Whether this is a RAMBUS DIMM or not (boolean)
2136#  * chk_label: The label to display for the checksum or CRC
2137#  * chk_valid: Whether the checksum or CRC is valid or not (boolean)
2138#  * chk_spd: The checksum or CRC value found in the EEPROM
2139#  * chk_calc: The checksum or CRC computed from the EEPROM data
2140# Keys are added over time.
2141@dimm = get_dimm_list() unless $use_hexdump;
2142
2143for my $i (0 .. $#dimm) {
2144        my @bytes = readspd(0, 128, $dimm[$i]->{file});
2145        $dimm[$i]->{bytes} = \@bytes;
2146        $dimm[$i]->{is_rambus} = $bytes[0] < 4;         # Simple heuristic
2147        if ($dimm[$i]->{is_rambus} || $bytes[2] < 9) {
2148                ($dimm[$i]->{chk_label}, $dimm[$i]->{chk_valid},
2149                 $dimm[$i]->{chk_spd}, $dimm[$i]->{chk_calc}) =
2150                        checksum(\@bytes);
2151        } else {
2152                ($dimm[$i]->{chk_label}, $dimm[$i]->{chk_valid},
2153                 $dimm[$i]->{chk_spd}, $dimm[$i]->{chk_calc}) =
2154                        check_crc(\@bytes);
2155        }
2156}
2157
2158# Checksum or CRC validation
2159if (!$opt_igncheck) {
2160        for (my $i = 0; $i < @dimm; ) {
2161                if ($dimm[$i]->{chk_valid}) {
2162                        $i++;
2163                } else {
2164                        splice(@dimm, $i, 1);
2165                }
2166        }
2167}
2168
2169
2170if ($opt_html && !$opt_bodyonly) {
2171        print "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.1//EN' \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n\n",
2172              "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\">\n",
2173              "<head>\n",
2174              "\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" />\n",
2175              "\t<title>PC DIMM Serial Presence Detect Tester/Decoder Output</title>\n",
2176              "</head>\n\n",
2177              "<body>\n";
2178}
2179
2180printc("decode-dimms version $revision");
2181printh('Memory Serial Presence Detect Decoder',
2182'By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner,
2183Jean Delvare, Trent Piepho and others');
2184
2185# Process the valid entries
2186for $current (0 .. $#dimm) {
2187        my @bytes = @{$dimm[$current]->{bytes}};
2188
2189        if ($opt_side_by_side) {
2190                printl("Decoding EEPROM", $dimm[$current]->{eeprom});
2191        }
2192
2193        if (!$use_hexdump) {
2194                if ($dimm[$current]->{file} =~ /-([\da-f]+)$/i) {
2195                        my $dimm_num = hex($1) - 0x50 + 1;
2196                        if ($dimm_num >= 1 && $dimm_num <= 8) {
2197                                printl("Guessing DIMM is in", "bank $dimm_num");
2198                        }
2199                }
2200        }
2201
2202# Decode first 3 bytes (0-2)
2203        prints("SPD EEPROM Information");
2204
2205        printl($dimm[$current]->{chk_label}, ($dimm[$current]->{chk_valid} ?
2206                sprintf("OK (%s)", $dimm[$current]->{chk_calc}) :
2207                sprintf("Bad\n(found %s, calculated %s)",
2208                        $dimm[$current]->{chk_spd}, $dimm[$current]->{chk_calc})));
2209
2210        my $temp;
2211        if ($dimm[$current]->{is_rambus}) {
2212                if ($bytes[0] == 1) { $temp = "0.7"; }
2213                elsif ($bytes[0] == 2) { $temp = "1.0"; }
2214                elsif ($bytes[0] == 0) { $temp = "Invalid"; }
2215                else { $temp = "Reserved"; }
2216                printl("SPD Revision", $temp);
2217        } else {
2218                my ($spd_size, $spd_used) = spd_sizes(\@bytes);
2219                printl("# of bytes written to SDRAM EEPROM", $spd_used);
2220                printl("Total number of bytes in EEPROM", $spd_size);
2221
2222                # If there's more data than what we've read, let's
2223                # read it now.  DDR3 will need this data.
2224                if ($spd_used > @bytes) {
2225                        push (@bytes,
2226                              readspd(@bytes, $spd_used - @bytes,
2227                                      $dimm[$current]->{file}));
2228                }
2229        }
2230
2231        my $type = sprintf("Unknown (0x%02x)", $bytes[2]);
2232        if ($dimm[$current]->{is_rambus}) {
2233                if ($bytes[2] == 1) { $type = "Direct Rambus"; }
2234                elsif ($bytes[2] == 17) { $type = "Rambus"; }
2235        } else {
2236                my @type_list = (
2237                        "Reserved", "FPM DRAM",         # 0, 1
2238                        "EDO", "Pipelined Nibble",      # 2, 3
2239                        "SDR SDRAM", "Multiplexed ROM", # 4, 5
2240                        "DDR SGRAM", "DDR SDRAM",       # 6, 7
2241                        "DDR2 SDRAM", "FB-DIMM",        # 8, 9
2242                        "FB-DIMM Probe", "DDR3 SDRAM",  # 10, 11
2243                );
2244                if ($bytes[2] < @type_list) {
2245                        $type = $type_list[$bytes[2]];
2246                }
2247        }
2248        printl("Fundamental Memory type", $type);
2249
2250# Decode next 61 bytes (3-63, depend on memory type)
2251        $decode_callback{$type}->(\@bytes)
2252                if exists $decode_callback{$type};
2253
2254        if ($type eq "DDR3 SDRAM") {
2255                # Decode DDR3-specific manufacturing data in bytes
2256                # 117-149
2257                decode_ddr3_mfg_data(\@bytes)
2258        } else {
2259                # Decode next 35 bytes (64-98, common to most
2260                # memory types)
2261                decode_manufacturing_information(\@bytes);
2262        }
2263
2264# Next 27 bytes (99-125) are manufacturer specific, can't decode
2265
2266# Last 2 bytes (126-127) are reserved, Intel used them as an extension
2267        if ($type eq "SDR SDRAM") {
2268                decode_intel_spec_freq(\@bytes);
2269        }
2270}
2271
2272# Side-by-side output format is only possible if all DIMMs have a similar
2273# output structure
2274if ($opt_side_by_side) {
2275        for $current (1 .. $#dimm) {
2276                my @ref_output = @{$dimm[0]->{output}};
2277                my @test_output = @{$dimm[$current]->{output}};
2278                my $line;
2279
2280                if (scalar @ref_output != scalar @test_output) {
2281                        $opt_side_by_side = 0;
2282                        last;
2283                }
2284
2285                for ($line = 0; $line < @ref_output; $line++) {
2286                        my ($ref_func, $ref_label, @ref_dummy) = @{$ref_output[$line]};
2287                        my ($test_func, $test_label, @test_dummy) = @{$test_output[$line]};
2288
2289                        if ($ref_func != $test_func || $ref_label ne $test_label) {
2290                                $opt_side_by_side = 0;
2291                                last;
2292                        }
2293                }
2294        }
2295
2296        if (!$opt_side_by_side) {
2297                printc("Side-by-side output only possible if all DIMMS are similar\n");
2298
2299                # Discard "Decoding EEPROM" entry from all outputs
2300                for $current (0 .. $#dimm) {
2301                        shift(@{$dimm[$current]->{output}});
2302                }
2303        }
2304}
2305
2306# Check if all dimms have the same value for a given line
2307sub line_has_same_values($)
2308{
2309        my $line = shift;
2310        my $value = $dimm[0]->{output}->[$line]->[2];
2311
2312        # Skip lines with no values (headers)
2313        return 1 unless defined $value;
2314
2315        for my $other (1 .. $#dimm) {
2316                return 0 unless $value eq $dimm[$other]->{output}->[$line]->[2];
2317        }
2318
2319        return 1;
2320}
2321
2322# Find out the longest value string to adjust the column width
2323sub find_col_width($)
2324{
2325        my $width = shift;
2326
2327        return $width unless $opt_side_by_side && !$opt_html;
2328
2329        my $line;
2330        my $line_nr = @{$dimm[0]->{output}};
2331
2332        for ($line = 0; $line < $line_nr; $line++) {
2333                next if $opt_merge && line_has_same_values($line);
2334
2335                my @strings;
2336
2337                for my $current (0 .. $#dimm) {
2338                        my $value = $dimm[$current]->{output}->[$line]->[2];
2339                        push @strings, split("\n", $value) if defined $value;
2340                }
2341
2342                foreach my $line2 (@strings) {
2343                        my $len = length($line2);
2344                        $width = $len if $len > $width;
2345                }
2346        }
2347
2348        return $width;
2349}
2350
2351$sbs_col_width = find_col_width(15);
2352
2353# Print the decoded information for all DIMMs
2354for $current (0 .. $#dimm) {
2355        if ($opt_side_by_side) {
2356                print "\n\n";
2357        } else {
2358                printl2("\n\nDecoding EEPROM", $dimm[$current]->{file},
2359                        "text-decoration: underline; font-weight: bold;");
2360        }
2361        print "<table border=\"1\">\n" if $opt_html;
2362
2363        my @output = @{$dimm[$current]->{output}};
2364        for (my $line = 0; $line < @output; $line++) {
2365                my ($func, @param) = @{$output[$line]};
2366
2367                if ($opt_side_by_side) {
2368                        foreach ($current+1 .. $#dimm) {
2369                                my @xoutput = @{$dimm[$_]->{output}};
2370                                if (@{$xoutput[$line]} == 3) {
2371                                        # Line with data, stack all values
2372                                        push @param, @{$xoutput[$line]}[2];
2373                                } else {
2374                                        # Separator, make it span
2375                                        push @param, scalar @dimm;
2376                                }
2377                        }
2378                }
2379
2380                $func->(@param);
2381        }
2382
2383        print "</table>\n" if $opt_html;
2384        last if $opt_side_by_side;
2385}
2386printl2("\n\nNumber of SDRAM DIMMs detected and decoded", scalar @dimm);
2387
2388print "</body></html>\n" if ($opt_html && !$opt_bodyonly);
Note: See TracBrowser for help on using the browser.