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

Revision 6299, 83.0 KB (checked in by khali, 8 days ago)

decode-dimms: New manufacturer names from JEP106AQ

  • 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", "STMicroelectronics (former 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 Technologies Inc. (former 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", "Tezzaron Semiconductor (former Tachyon Semiconductor)",
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 "Memorysolution GmbH (former 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", "I'M Intelligent Memory Ltd (former 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", "LITE-ON IT Corporation", "Inuitive", "HMicro",
306 "BittWare Inc.", "GLOBALFOUNDRIES", "ACPI Digital Co. Ltd", "Annapurna Labs",
307 "AcSiP Technology Corporation", "Idea! Electronic Systems", "Gowe Technology Co. Ltd",
308 "Hermes Testing Solutions Inc.", "Positivo BGH", "Intelligence Silicon Technology"],
309["3D PLUS", "Diehl Aerospace", "Fairchild", "Mercury Systems",
310 "Sonics Inc.", "GE Intelligent Platforms GmbH & Co.", "Shenzhen Jinge Information Co. Ltd",
311 "SCWW", "Silicon Motion Inc.", "Anurag", "King Kong",
312 "FROM30 Co. Ltd", "Gowin Semiconductor Corp", "Fremont Micro Devices Ltd",
313 "Ericsson Modems", "Exelis", "Satixfy Ltd", "Galaxy Microsystems Ltd",
314 "Gloway International Co. Ltd", "Lab", "Smart Energy Instruments",
315 "Approved Memory Corporation", "Axell Corporation", "ISD Technology Limited",
316 "Phytium", "Xi'an SinoChip Semiconductor", "Ambiq Micro", "eveRAM Technology Inc.",
317 "Infomax", "Butterfly Network Inc.", "Shenzhen City Gcai Electronics",
318 "Stack Devices Corporation", "ADK Media Group", "TSP Global Co. Ltd",
319 "HighX", "Shenzhen Elicks Technology", "ISSI/Chingis", "Google Inc.",
320 "Dasima International Development", "Leahkinn Technology Limited",
321 "HIMA Paul Hildebrandt GmbH Co KG", "Keysight Technologies",
322 "Techcomp International (Fastable)", "Ancore Technology Corporation",
323 "Nuvoton", "Korea Uhbele International Group Ltd", "Ikegami Tsushinki Co. Ltd",
324 "RelChip Inc.", "Baikal Electronics", "Nemostech Inc.",
325 "Memorysolution GmbH", "Silicon Integrated Systems Corporation",
326 "Xiede", "Multilaser Components", "Flash Chi", "Jone",
327 "GCT Semiconductor Inc.", "Hong Kong Zetta Device Technology",
328 "Unimemory Technology(s) Pte Ltd", "Cuso", "Kuso",
329 "Uniquify Inc.", "Skymedi Corporation", "Core Chance Co. Ltd",
330 "Tekism Co. Ltd", "Seagate Technology PLC", "Hong Kong Gaia Group Co. Limited",
331 "Gigacom Semiconductor LLC", "V2 Technologies", "TLi", "Neotion",
332 "Lenovo", "Shenzhen Zhongteng Electronic Corp. Ltd", "Compound Photonics",
333 "Cognimem Technologies Inc.", "Shenzhen Pango Microsystems Co. Ltd",
334 "Vasekey", "Cal-Comp Industria de Semicondutores", "Eyenix Co. Ltd",
335 "Heoriady", "Accelerated Memory Production Inc.", "INVECAS Inc.",
336 "AP Memory", "Douqi Technology", "Etron Technology Inc.",
337 "Indie Semiconductor", "Socionext Inc.", "HGST", "EVGA",
338 "Audience Inc.", "EpicGear", "Vitesse Enterprise Co.",
339 "Foxtronn International Corporation", "Bretelon Inc.",
340 "Zbit Semiconductor Inc."]
341);
342
343$use_sysfs = -d '/sys/bus';
344
345# We consider that no data was written to this area of the SPD EEPROM if
346# all bytes read 0x00 or all bytes read 0xff
347sub spd_written(@)
348{
349        my $all_00 = 1;
350        my $all_ff = 1;
351
352        foreach my $b (@_) {
353                $all_00 = 0 unless $b == 0x00;
354                $all_ff = 0 unless $b == 0xff;
355                return 1 unless $all_00 or $all_ff;
356        }
357
358        return 0;
359}
360
361sub parity($)
362{
363        my $n = shift;
364        my $parity = 0;
365
366        while ($n) {
367                $parity++ if ($n & 1);
368                $n >>= 1;
369        }
370
371        return ($parity & 1);
372}
373
374# The code byte includes parity, the count byte does not.
375sub manufacturer_common($$)
376{
377        my ($count, $code) = @_;
378        my $manufacturer;
379
380        return "Invalid" if parity($code) != 1
381                         or ($code &= 0x7F) == 0;
382        return "Unknown" if $count >= @vendors
383                         or $code - 1 >= @{$vendors[$count]};
384        $manufacturer = $vendors[$count][$code - 1];
385        $manufacturer =~ s/ \(former .*\)$// if $opt_side_by_side;
386        return $manufacturer;
387}
388
389# New encoding format (as of DDR3) for manufacturer just has a count of
390# leading 0x7F rather than all the individual bytes.  The count bytes includes
391# parity!
392sub manufacturer_ddr3($$)
393{
394        my ($count, $code) = @_;
395        my $manufacturer;
396
397        return "Undefined" unless spd_written($count, $code);
398
399        $manufacturer = manufacturer_common($count & 0x7F, $code);
400        $manufacturer .= "? (Invalid parity)" if parity($count) != 1;
401        return $manufacturer;
402}
403
404sub manufacturer(@)
405{
406        my @bytes = @_;
407        my $ai = 0;
408        my $first;
409
410        return ("Undefined", []) unless spd_written(@bytes);
411
412        while (defined($first = shift(@bytes)) && $first == 0x7F) {
413                $ai++;
414        }
415
416        return ("Invalid", []) unless defined $first;
417        return (manufacturer_common($ai, $first), \@bytes);
418}
419
420sub manufacturer_data(@)
421{
422        my $hex = "";
423        my $asc = "";
424
425        return unless spd_written(@_);
426
427        foreach my $byte (@_) {
428                $hex .= sprintf("\%02X ", $byte);
429                $asc .= ($byte >= 32 && $byte < 127) ? chr($byte) : '?';
430        }
431
432        return "$hex(\"$asc\")";
433}
434
435sub part_number(@)
436{
437        my $asc = "";
438        my $byte;
439
440        while (defined ($byte = shift) && $byte >= 32 && $byte < 127) {
441                $asc .= chr($byte);
442        }
443
444        return ($asc eq "") ? "Undefined" : $asc;
445}
446
447sub cas_latencies(@)
448{
449        return "None" unless @_;
450        return join ', ', map("${_}T", sort { $b <=> $a } @_);
451}
452
453# Real printing functions
454
455sub html_encode($)
456{
457        my $text = shift;
458        $text =~ s/</\&lt;/sg;
459        $text =~ s/>/\&gt;/sg;
460        $text =~ s/ degrees C/\&deg;C/sg;
461        $text =~ s/\n/<br\/>\n/sg;
462        return $text;
463}
464
465sub same_values(@)
466{
467        my $value = shift;
468        while (@_) {
469                return 0 unless $value eq shift;
470        }
471        return 1;
472}
473
474sub real_printl($$) # print a line w/ label and values
475{
476        my ($label, @values) = @_;
477        local $_;
478        my $same_values = same_values(@values);
479
480        # If all values are N/A, don't bother printing
481        return if $values[0] eq "N/A" and $same_values;
482
483        if ($opt_html) {
484                $label = html_encode($label);
485                @values = map { html_encode($_) } @values;
486                print "<tr><td style=\"vertical-align: top;\">$label</td>";
487                if (!$opt_merge) {
488                        print "<td>$_</td>" foreach @values;
489                } elsif ($same_values) {
490                        print "<td colspan=\"".(scalar @values)."\">$values[0]</td>";
491                } else {
492                        # For HTML output, merge adjacent cells even if
493                        # the whole line cannot be merged.
494                        my $colcnt = 0;
495                        while (@values) {
496                                $colcnt++;
497                                my $value = shift @values;
498                                next if (@values && $value eq $values[0]);
499                                print "<td" . ($colcnt > 1 ? " colspan=\"$colcnt\"" : "") .">$value</td>";
500                                $colcnt = 0;
501                        }
502                }
503                print "</tr>\n";
504        } else {
505                if ($opt_merge && $same_values) {
506                        splice(@values, 1);
507                }
508
509                my $format = "%-47s".(("  %-".$sbs_col_width."s") x (scalar @values - 1))."  %s\n";
510                my $maxl = 0; # Keep track of the max number of lines
511
512                # It's a bit tricky because each value may span over more than
513                # one line. We can easily extract the values per column, but
514                # we need them per line at printing time. So we have to
515                # prepare a 2D array with all the individual string fragments.
516                my ($col, @lines);
517                for ($col = 0; $col < @values; $col++) {
518                        my @cells = split /\n/, $values[$col];
519                        $maxl = @cells if @cells > $maxl;
520                        for (my $l = 0; $l < @cells; $l++) {
521                                $lines[$l]->[$col] = $cells[$l];
522                        }
523                }
524
525                # Also make sure there are no holes in the array
526                for (my $l = 0; $l < $maxl; $l++) {
527                        for ($col = 0; $col < @values; $col++) {
528                                $lines[$l]->[$col] = ""
529                                        if not defined $lines[$l]->[$col];
530                        }
531                }
532
533                printf $format, $label, @{shift @lines};
534                printf $format, "", @{$_} foreach (@lines);
535        }
536}
537
538sub printl2 # print a line w/ label and value (outside a table)
539{
540        my ($label, $value, $style) = @_;
541        if ($opt_html) {
542                $label = html_encode($label);
543                $value = html_encode($value);
544                print "<p", (defined $style ? " style=\"$style\"" : ""), ">";
545        }
546        print "$label: $value\n";
547        print "</p>\n" if $opt_html;
548}
549
550sub real_prints($) # print separator w/ given text
551{
552        my ($label, $ncol) = @_;
553        $ncol = 1 unless $ncol;
554        if ($opt_html) {
555                $label = html_encode($label);
556                print "<tr><td style=\"font-weight: bold; text-align: center;\" colspan=\"".(1+$ncol)."\">$label</td></tr>\n";
557        } else {
558                print "\n---=== $label ===---\n";
559        }
560}
561
562sub printh($$) # print header w/ given text
563{
564        my ($header, $sub) = @_;
565        if ($opt_html) {
566                $header = html_encode($header);
567                $sub = html_encode($sub);
568                print "<h1>$header</h1>\n";
569                print "<p>$sub</p>\n";
570        } else {
571                print "\n$header\n$sub\n";
572        }
573}
574
575sub printc($) # print comment
576{
577        my ($comment) = @_;
578        if ($opt_html) {
579                $comment = html_encode($comment);
580                print "<!-- $comment -->\n";
581        } else {
582                print "# $comment\n";
583        }
584}
585
586# Fake printing functions
587# These don't actually print anything, instead they store the desired
588# output for later processing.
589
590sub printl($$) # print a line w/ label and value
591{
592        my @output = (\&real_printl, @_);
593        push @{$dimm[$current]->{output}}, \@output;
594}
595
596sub printl_cond($$$) # same as printl but conditional
597{
598        my ($cond, $label, $value) = @_;
599        return unless $cond || $opt_side_by_side;
600        printl($label, $cond ? $value : "N/A");
601}
602
603sub prints($) # print separator w/ given text
604{
605        my @output = (\&real_prints, @_);
606        push @{$dimm[$current]->{output}}, \@output;
607}
608
609# Helper functions
610
611sub tns1($) # print a time in ns, with 1 decimal digit
612{
613        return sprintf("%.1f ns", $_[0]);
614}
615
616sub tns($) # print a time in ns, with 2 decimal digits
617{
618        return sprintf("%3.2f ns", $_[0]);
619}
620
621sub tns3($) # print a time in ns, with 3 decimal digits
622{
623        return sprintf("%.3f ns", $_[0]);
624}
625
626sub value_or_undefined
627{
628        my ($value, $unit) = @_;
629        return "Undefined!" unless $value;
630        $value .= " $unit" if defined $unit;
631        return $value;
632}
633
634# Common to SDR, DDR and DDR2 SDRAM
635sub sdram_voltage_interface_level($)
636{
637        my @levels = (
638                "TTL (5V tolerant)",            #  0
639                "LVTTL (not 5V tolerant)",      #  1
640                "HSTL 1.5V",                    #  2
641                "SSTL 3.3V",                    #  3
642                "SSTL 2.5V",                    #  4
643                "SSTL 1.8V",                    #  5
644        );
645       
646        return ($_[0] < @levels) ? $levels[$_[0]] : "Undefined!";
647}
648
649# Common to SDR, DDR and DDR2 SDRAM
650sub sdram_module_configuration_type($)
651{
652        my $byte = $_[0] & 0x07;
653        my @edc;
654
655        return "No Parity" if $byte == 0;
656
657        # Data ECC includes Data Parity so don't print both
658        push @edc, "Data Parity" if ($byte & 0x03) == 0x01;
659        push @edc, "Data ECC" if ($byte & 0x02);
660        # New in DDR2 specification
661        push @edc, "Address/Command Parity" if ($byte & 0x04);
662
663        return join ", ", @edc;
664}
665
666# Parameter: EEPROM bytes 0-127 (using 3-62)
667sub decode_sdr_sdram($)
668{
669        my $bytes = shift;
670        my $temp;
671        my ($ctime, $ctime1, $ctime2, $ctime_min);
672
673# SPD revision
674        # Starting with SPD revision 1.2, this byte is encoded in BCD
675        printl("SPD Revision", $bytes->[62] < 0x12 ? $bytes->[62] :
676                ($bytes->[62] >> 4) . "." . ($bytes->[62] & 0xf));
677
678#size computation
679
680        prints("Memory Characteristics");
681
682        my $k = 0;
683        my $ii = 0;
684
685        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
686        if (($bytes->[5] <= 8) && ($bytes->[17] <= 8)) {
687                 $k = $bytes->[5] * $bytes->[17];
688        }
689
690        if ($ii > 0 && $ii <= 12 && $k > 0) {
691                printl("Size", ((1 << $ii) * $k) . " MB");
692        } else {
693                printl("Size", "INVALID: " . $bytes->[3] . "," . $bytes->[4] . "," .
694                               $bytes->[5] . "," . $bytes->[17]);
695        }
696
697        my @cas;
698        for ($ii = 0; $ii < 7; $ii++) {
699                push(@cas, $ii + 1) if ($bytes->[18] & (1 << $ii));
700        }
701
702        my $trcd;
703        my $trp;
704        my $tras;
705        $ctime_min = $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
706
707        $trcd = $bytes->[29];
708        $trp = $bytes->[27];
709        $tras = $bytes->[30];
710
711        printl("tCL-tRCD-tRP-tRAS",
712                $cas[$#cas] . "-" .
713                ceil($trcd/$ctime) . "-" .
714                ceil($trp/$ctime) . "-" .
715                ceil($tras/$ctime));
716
717        if ($bytes->[3] == 0) { $temp = "Undefined!"; }
718        elsif ($bytes->[3] == 1) { $temp = "1/16"; }
719        elsif ($bytes->[3] == 2) { $temp = "2/17"; }
720        elsif ($bytes->[3] == 3) { $temp = "3/18"; }
721        else { $temp = $bytes->[3]; }
722        printl("Number of Row Address Bits", $temp);
723
724        if ($bytes->[4] == 0) { $temp = "Undefined!"; }
725        elsif ($bytes->[4] == 1) { $temp = "1/16"; }
726        elsif ($bytes->[4] == 2) { $temp = "2/17"; }
727        elsif ($bytes->[4] == 3) { $temp = "3/18"; }
728        else { $temp = $bytes->[4]; }
729        printl("Number of Col Address Bits", $temp);
730
731        printl("Number of Module Rows", value_or_undefined($bytes->[5]));
732
733        if ($bytes->[7] > 1) { $temp = "Undefined!"; }
734        else { $temp = ($bytes->[7] * 256) + $bytes->[6]; }
735        printl("Data Width", $temp);
736
737        printl("Voltage Interface Level",
738               sdram_voltage_interface_level($bytes->[8]));
739
740        printl("Module Configuration Type",
741               sdram_module_configuration_type($bytes->[11]));
742
743        printl("Refresh Rate", ddr2_refresh_rate($bytes->[12]));
744
745        if ($bytes->[13] & 0x80) { $temp = "Bank2 = 2 x Bank1"; }
746        else { $temp = "No Bank2 OR Bank2 = Bank1 width"; }
747        printl("Primary SDRAM Component Bank Config", $temp);
748        printl("Primary SDRAM Component Widths",
749               value_or_undefined($bytes->[13] & 0x7f));
750
751        if ($bytes->[14] & 0x80) { $temp = "Bank2 = 2 x Bank1"; }
752        else { $temp = "No Bank2 OR Bank2 = Bank1 width"; }
753        printl("Error Checking SDRAM Component Bank Config", $temp);
754        printl("Error Checking SDRAM Component Widths",
755               value_or_undefined($bytes->[14] & 0x7f));
756
757        printl("Min Clock Delay for Back to Back Random Access",
758               value_or_undefined($bytes->[15]));
759
760        my @array;
761        for ($ii = 0; $ii < 4; $ii++) {
762                push(@array, 1 << $ii) if ($bytes->[16] & (1 << $ii));
763        }
764        push(@array, "Page") if ($bytes->[16] & 128);
765        if (@array) { $temp = join ', ', @array; }
766        else { $temp = "None"; }
767        printl("Supported Burst Lengths", $temp);
768
769        printl("Number of Device Banks",
770               value_or_undefined($bytes->[17]));
771
772        printl("Supported CAS Latencies", cas_latencies(@cas));
773
774        @array = ();
775        for ($ii = 0; $ii < 7; $ii++) {
776                push(@array, $ii) if ($bytes->[19] & (1 << $ii));
777        }
778        if (@array) { $temp = join ', ', @array; }
779        else { $temp = "None"; }
780        printl("Supported CS Latencies", $temp);
781
782        @array = ();
783        for ($ii = 0; $ii < 7; $ii++) {
784                push(@array, $ii) if ($bytes->[20] & (1 << $ii));
785        }
786        if (@array) { $temp = join ', ', @array; }
787        else { $temp = "None"; }
788        printl("Supported WE Latencies", $temp);
789
790        my ($cycle_time, $access_time);
791
792        if (@cas >= 1) {
793                $cycle_time = "$ctime ns at CAS ".$cas[$#cas];
794
795                $temp = ($bytes->[10] >> 4) + ($bytes->[10] & 0xf) * 0.1;
796                $access_time = "$temp ns at CAS ".$cas[$#cas];
797        }
798
799        if (@cas >= 2 && spd_written(@$bytes[23..24])) {
800                $temp = $bytes->[23] >> 4;
801                if ($temp == 0) { $temp = "Undefined!"; }
802                else {
803                        $temp += 15 if $temp < 4;
804                        $temp += ($bytes->[23] & 0xf) * 0.1;
805                        $ctime1 = $temp;
806                }
807                $cycle_time .= "\n$temp ns at CAS ".$cas[$#cas-1];
808
809                $temp = $bytes->[24] >> 4;
810                if ($temp == 0) { $temp = "Undefined!"; }
811                else {
812                        $temp += 15 if $temp < 4;
813                        $temp += ($bytes->[24] & 0xf) * 0.1;
814                }
815                $access_time .= "\n$temp ns at CAS ".$cas[$#cas-1];
816        }
817
818        if (@cas >= 3 && spd_written(@$bytes[25..26])) {
819                $temp = $bytes->[25] >> 2;
820                if ($temp == 0) { $temp = "Undefined!"; }
821                else {
822                        $temp += ($bytes->[25] & 0x3) * 0.25;
823                        $ctime2 = $temp;
824                }
825                $cycle_time .= "\n$temp ns at CAS ".$cas[$#cas-2];
826
827                $temp = $bytes->[26] >> 2;
828                if ($temp == 0) { $temp = "Undefined!"; }
829                else {
830                        $temp += ($bytes->[26] & 0x3) * 0.25;
831                }
832                $access_time .= "\n$temp ns at CAS ".$cas[$#cas-2];
833        }
834
835        printl_cond(defined $cycle_time, "Cycle Time", $cycle_time);
836        printl_cond(defined $access_time, "Access Time", $access_time);
837
838        prints("Attributes");
839        $temp = "";
840        if ($bytes->[21] & 1) { $temp .= "Buffered Address/Control Inputs\n"; }
841        if ($bytes->[21] & 2) { $temp .= "Registered Address/Control Inputs\n"; }
842        if ($bytes->[21] & 4) { $temp .= "On card PLL (clock)\n"; }
843        if ($bytes->[21] & 8) { $temp .= "Buffered DQMB Inputs\n"; }
844        if ($bytes->[21] & 16) { $temp .= "Registered DQMB Inputs\n"; }
845        if ($bytes->[21] & 32) { $temp .= "Differential Clock Input\n"; }
846        if ($bytes->[21] & 64) { $temp .= "Redundant Row Address\n"; }
847        if ($bytes->[21] & 128) { $temp .= "Undefined (bit 7)\n"; }
848        printl_cond($bytes->[21], "SDRAM Module Attributes", $temp);
849
850# standard DDR speeds
851        prints("Timings at Standard Speeds");
852        foreach $ctime (7.5, 10, 15) {
853                my $best_cas;
854
855                # Find min CAS latency at this speed
856                if (defined $ctime2 && $ctime >= $ctime2) {
857                        $best_cas = $cas[$#cas-2];
858                } elsif (defined $ctime1 && $ctime >= $ctime1) {
859                        $best_cas = $cas[$#cas-1];
860                } else {
861                        $best_cas = $cas[$#cas];
862                }
863
864                printl_cond($ctime >= $ctime_min,
865                            "tCL-tRCD-tRP-tRAS as PC" . int(1000 / $ctime),
866                            ddr_core_timings($best_cas, $ctime,
867                                             $trcd, $trp, $tras));
868        }
869
870        $temp = "";
871        if ($bytes->[22] & 1) { $temp .= "Supports Early RAS# Recharge\n"; }
872        if ($bytes->[22] & 2) { $temp .= "Supports Auto-Precharge\n"; }
873        if ($bytes->[22] & 4) { $temp .= "Supports Precharge All\n"; }
874        if ($bytes->[22] & 8) { $temp .= "Supports Write1/Read Burst\n"; }
875        if ($bytes->[22] & 16) { $temp .= "Lower VCC Tolerance: 5%\n"; }
876        else { $temp .= "Lower VCC Tolerance: 10%\n"; }
877        if ($bytes->[22] & 32) { $temp .= "Upper VCC Tolerance: 5%\n"; }
878        else { $temp .= "Upper VCC Tolerance: 10%\n"; }
879        if ($bytes->[22] & 64) { $temp .= "Undefined (bit 6)\n"; }
880        if ($bytes->[22] & 128) { $temp .= "Undefined (bit 7)\n"; }
881        printl("SDRAM Device Attributes (General)", $temp);
882
883        prints("Timing Parameters");
884        printl("Minimum Row Precharge Time",
885               value_or_undefined($bytes->[27], "ns"));
886
887        printl("Row Active to Row Active Min",
888               value_or_undefined($bytes->[28], "ns"));
889
890        printl("RAS to CAS Delay",
891               value_or_undefined($bytes->[29], "ns"));
892
893        printl("Min RAS Pulse Width",
894               value_or_undefined($bytes->[30], "ns"));
895
896        $temp = "";
897        if ($bytes->[31] & 1) { $temp .= "4 MByte\n"; }
898        if ($bytes->[31] & 2) { $temp .= "8 MByte\n"; }
899        if ($bytes->[31] & 4) { $temp .= "16 MByte\n"; }
900        if ($bytes->[31] & 8) { $temp .= "32 MByte\n"; }
901        if ($bytes->[31] & 16) { $temp .= "64 MByte\n"; }
902        if ($bytes->[31] & 32) { $temp .= "128 MByte\n"; }
903        if ($bytes->[31] & 64) { $temp .= "256 MByte\n"; }
904        if ($bytes->[31] & 128) { $temp .= "512 MByte\n"; }
905        if ($bytes->[31] == 0) { $temp .= "(Undefined! -- None Reported!)\n"; }
906        printl("Row Densities", $temp);
907
908        $temp = (($bytes->[32] & 0x7f) >> 4) + ($bytes->[32] & 0xf) * 0.1;
909        printl_cond(($bytes->[32] & 0xf) <= 9,
910                    "Command and Address Signal Setup Time",
911                    (($bytes->[32] >> 7) ? -$temp : $temp) . " ns");
912
913        $temp = (($bytes->[33] & 0x7f) >> 4) + ($bytes->[33] & 0xf) * 0.1;
914        printl_cond(($bytes->[33] & 0xf) <= 9,
915                    "Command and Address Signal Hold Time",
916                    (($bytes->[33] >> 7) ? -$temp : $temp) . " ns");
917
918        $temp = (($bytes->[34] & 0x7f) >> 4) + ($bytes->[34] & 0xf) * 0.1;
919        printl_cond(($bytes->[34] & 0xf) <= 9, "Data Signal Setup Time",
920                    (($bytes->[34] >> 7) ? -$temp : $temp) . " ns");
921
922        $temp = (($bytes->[35] & 0x7f) >> 4) + ($bytes->[35] & 0xf) * 0.1;
923        printl_cond(($bytes->[35] & 0xf) <= 9, "Data Signal Hold Time",
924                    (($bytes->[35] >> 7) ? -$temp : $temp) . " ns");
925}
926
927sub as_ddr($$)
928{
929        my ($gen, $ctime) = @_;
930
931        return " as DDR" . ($gen == 1 ? "" : $gen) . "-" .
932               int(2000 / $ctime);
933}
934
935sub ddr_core_timings($$$$$)
936{
937        my ($cas, $ctime, $trcd, $trp, $tras) = @_;
938
939        return $cas . "-" . ceil($trcd/$ctime) . "-" . ceil($trp/$ctime) .
940                "-" . ceil($tras/$ctime);
941}
942
943# Parameter: EEPROM bytes 0-127 (using 3-62)
944sub decode_ddr_sdram($)
945{
946        my $bytes = shift;
947        my $temp;
948        my ($ctime, $ctime1, $ctime2, $ctime_min, $ctime_max);
949
950# SPD revision
951        printl_cond($bytes->[62] != 0xff, "SPD Revision",
952                    ($bytes->[62] >> 4) . "." . ($bytes->[62] & 0xf));
953
954# speed
955        prints("Memory Characteristics");
956
957        $ctime_min = $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1;
958        my $ddrclk = 2 * (1000 / $ctime);
959        my $tbits = ($bytes->[7] * 256) + $bytes->[6];
960        if (($bytes->[11] == 2) || ($bytes->[11] == 1)) { $tbits = $tbits - 8; }
961        my $pcclk = int ($ddrclk * $tbits / 8);
962        $pcclk += 100 if ($pcclk % 100) >= 50; # Round properly
963        $pcclk = $pcclk - ($pcclk % 100);
964        $ddrclk = int ($ddrclk);
965        printl("Maximum module speed", "$ddrclk MHz (PC${pcclk})");
966
967#size computation
968        my $k = 0;
969        my $ii = 0;
970
971        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
972        if (($bytes->[5] <= 8) && ($bytes->[17] <= 8)) {
973                 $k = $bytes->[5] * $bytes->[17];
974        }
975
976        if ($ii > 0 && $ii <= 12 && $k > 0) {
977                printl("Size", ((1 << $ii) * $k) . " MB");
978        } else {
979                printl("Size", "INVALID: " . $bytes->[3] . ", " . $bytes->[4] . ", " .
980                               $bytes->[5] . ", " . $bytes->[17]);
981        }
982
983        printl("Banks x Rows x Columns x Bits",
984               join(' x ', $bytes->[17], $bytes->[3], $bytes->[4], $bytes->[6]));
985        printl("Ranks", $bytes->[5]);
986
987        printl("Voltage Interface Level",
988               sdram_voltage_interface_level($bytes->[8]));
989
990        printl("Module Configuration Type",
991               sdram_module_configuration_type($bytes->[11]));
992
993        printl("Refresh Rate", ddr2_refresh_rate($bytes->[12]));
994
995        my $highestCAS = 0;
996        my %cas;
997        for ($ii = 0; $ii < 7; $ii++) {
998                if ($bytes->[18] & (1 << $ii)) {
999                        $highestCAS = 1+$ii*0.5;
1000                        $cas{$highestCAS}++;
1001                }
1002        }
1003
1004        my $trcd;
1005        my $trp;
1006        my $tras;
1007
1008        $trcd = ($bytes->[29] >> 2) + (($bytes->[29] & 3) * 0.25);
1009        $trp = ($bytes->[27] >> 2) + (($bytes->[27] & 3) * 0.25);
1010        $tras = $bytes->[30];
1011
1012# latencies
1013        printl("Supported CAS Latencies", cas_latencies(keys %cas));
1014
1015        my @array;
1016        for ($ii = 0; $ii < 7; $ii++) {
1017                push(@array, $ii) if ($bytes->[19] & (1 << $ii));
1018        }
1019        if (@array) { $temp = join ', ', @array; }
1020        else { $temp = "None"; }
1021        printl("Supported CS Latencies", $temp);
1022
1023        @array = ();
1024        for ($ii = 0; $ii < 7; $ii++) {
1025                push(@array, $ii) if ($bytes->[20] & (1 << $ii));
1026        }
1027        if (@array) { $temp = join ', ', @array; }
1028        else { $temp = "None"; }
1029        printl("Supported WE Latencies", $temp);
1030
1031# timings
1032        my ($cycle_time, $access_time, $core_timings);
1033
1034        if (exists $cas{$highestCAS}) {
1035                $core_timings = ddr_core_timings($highestCAS, $ctime,
1036                        $trcd, $trp, $tras) . as_ddr(1, $ctime);
1037
1038                $cycle_time = "$ctime ns at CAS $highestCAS";
1039                $access_time = (($bytes->[10] >> 4) * 0.1 + ($bytes->[10] & 0xf) * 0.01)
1040                             . " ns at CAS $highestCAS";
1041        }
1042
1043        if (exists $cas{$highestCAS-0.5} && spd_written(@$bytes[23..24])) {
1044                $ctime1 = ($bytes->[23] >> 4) + ($bytes->[23] & 0xf) * 0.1;
1045                $core_timings .= "\n".ddr_core_timings($highestCAS-0.5, $ctime1,
1046                        $trcd, $trp, $tras) . as_ddr(1, $ctime1);
1047
1048                $cycle_time .= "\n$ctime1 ns at CAS ".($highestCAS-0.5);
1049                $access_time .= "\n".(($bytes->[24] >> 4) * 0.1 + ($bytes->[24] & 0xf) * 0.01)
1050                              . " ns at CAS ".($highestCAS-0.5);
1051        }
1052
1053        if (exists $cas{$highestCAS-1} && spd_written(@$bytes[25..26])) {
1054                $ctime2 = ($bytes->[25] >> 4) + ($bytes->[25] & 0xf) * 0.1,
1055                $core_timings .= "\n".ddr_core_timings($highestCAS-1, $ctime2,
1056                        $trcd, $trp, $tras) . as_ddr(1, $ctime2);
1057
1058                $cycle_time .= "\n$ctime2 ns at CAS ".($highestCAS-1);
1059                $access_time .= "\n".(($bytes->[26] >> 4) * 0.1 + ($bytes->[26] & 0xf) * 0.01)
1060                              . " ns at CAS ".($highestCAS-1);
1061        }
1062
1063        $ctime_max = $bytes->[43] == 0xff ? 0 : $bytes->[43]/4;
1064
1065        printl_cond(defined $core_timings, "tCL-tRCD-tRP-tRAS", $core_timings);
1066        printl_cond(defined $cycle_time, "Minimum Cycle Time", $cycle_time);
1067        printl_cond(defined $access_time, "Maximum Access Time", $access_time);
1068        printl_cond($bytes->[43] & 0xfc,
1069                    "Maximum Cycle Time (tCK max)",
1070                    $bytes->[43] == 0xff ? "No minimum frequency" :
1071                    $bytes->[43] == 0 ? "" : # Wouldn't be displayed, prevent div by 0
1072                    tns1($ctime_max)." (DDR-".int(8000 / $bytes->[43]).")");
1073
1074# standard DDR speeds
1075        prints("Timings at Standard Speeds");
1076        foreach $ctime (5, 6, 7.5, 10) {
1077                my $best_cas;
1078
1079                # Find min CAS latency at this speed
1080                if (defined $ctime2 && $ctime >= $ctime2) {
1081                        $best_cas = $highestCAS-1;
1082                } elsif (defined $ctime1 && $ctime >= $ctime1) {
1083                        $best_cas = $highestCAS-0.5;
1084                } else {
1085                        $best_cas = $highestCAS;
1086                }
1087
1088                printl_cond($ctime >= $ctime_min && ($ctime_max < 1 || $ctime <= $ctime_max),
1089                            "tCL-tRCD-tRP-tRAS" . as_ddr(1, $ctime),
1090                            ddr_core_timings($best_cas, $ctime,
1091                                             $trcd, $trp, $tras));
1092        }
1093
1094# more timing information
1095        prints("Timing Parameters");
1096        printl_cond($bytes->[32] != 0xff,
1097                    "Address/Command Setup Time Before Clock",
1098                    tns(ddr2_sdram_atime($bytes->[32])));
1099        printl_cond($bytes->[33] != 0xff,
1100                    "Address/Command Hold Time After Clock",
1101                    tns(ddr2_sdram_atime($bytes->[33])));
1102        printl_cond($bytes->[34] != 0xff,
1103                    "Data Input Setup Time Before Clock",
1104                    tns(ddr2_sdram_atime($bytes->[34])));
1105        printl_cond($bytes->[35] != 0xff,
1106                    "Data Input Hold Time After Clock",
1107                    tns(ddr2_sdram_atime($bytes->[35])));
1108        printl("Minimum Row Precharge Delay (tRP)", tns($trp));
1109        printl_cond($bytes->[28] & 0xfc,
1110                    "Minimum Row Active to Row Active Delay (tRRD)",
1111                    tns($bytes->[28]/4));
1112        printl("Minimum RAS# to CAS# Delay (tRCD)", tns($trcd));
1113        printl("Minimum RAS# Pulse Width (tRAS)", tns($tras));
1114        printl_cond($bytes->[41] && $bytes->[41] != 0xff,
1115                    "Minimum Active to Active/AR Time (tRC)",
1116                    tns($bytes->[41]));
1117        printl_cond($bytes->[42],
1118                    "Minimum AR to Active/AR Command Period (tRFC)",
1119                    tns($bytes->[42]));
1120        printl_cond($bytes->[44],
1121                    "Maximum DQS to DQ Skew (tDQSQ)",
1122                    tns($bytes->[44]/100));
1123        printl_cond(($bytes->[45] & 0xf0) && $bytes->[45] != 0xff,
1124                    "Maximum Read Data Hold Skew (tQHS)",
1125                    tns(ddr2_sdram_atime($bytes->[45])));
1126
1127# module attributes
1128        prints("Module Attributes");
1129        if (($bytes->[47] & 0x03) == 0x01) { $temp = "1.125\" to 1.25\""; }
1130        elsif (($bytes->[47] & 0x03) == 0x02) { $temp = "1.7\""; }
1131        else { $temp = "Other"; }
1132        printl_cond($bytes->[47] & 0x03, "Module Height", $temp);
1133}
1134
1135sub ddr2_sdram_ctime($)
1136{
1137        my $byte = shift;
1138        my $ctime;
1139
1140        $ctime = $byte >> 4;
1141        if (($byte & 0xf) <= 9) { $ctime += ($byte & 0xf) * 0.1; }
1142        elsif (($byte & 0xf) == 10) { $ctime += 0.25; }
1143        elsif (($byte & 0xf) == 11) { $ctime += 0.33; }
1144        elsif (($byte & 0xf) == 12) { $ctime += 0.66; }
1145        elsif (($byte & 0xf) == 13) { $ctime += 0.75; }
1146
1147        return $ctime;
1148}
1149
1150sub ddr2_sdram_atime($)
1151{
1152        my $byte = shift;
1153        my $atime;
1154
1155        $atime = ($byte >> 4) * 0.1 + ($byte & 0xf) * 0.01;
1156
1157        return $atime;
1158}
1159
1160# Base, high-bit, 3-bit fraction code
1161sub ddr2_sdram_rtime($$$)
1162{
1163        my ($rtime, $msb, $ext) = @_;
1164        my @table = (0, .25, .33, .50, .66, .75);
1165
1166        return $rtime + $msb * 256 + $table[$ext];
1167}
1168
1169sub ddr2_module_types($)
1170{
1171        my $byte = shift;
1172        my @types = qw(RDIMM UDIMM SO-DIMM Micro-DIMM Mini-RDIMM Mini-UDIMM);
1173        my @widths = (133.35, 133.25, 67.6, 45.5, 82.0, 82.0);
1174        my @suptypes;
1175        local $_;
1176
1177        foreach (0..5) {
1178                push @suptypes, "$types[$_] ($widths[$_] mm)"
1179                        if ($byte & (1 << $_));
1180        }
1181
1182        return @suptypes;
1183}
1184
1185# Common to SDR, DDR and DDR2 SDRAM
1186sub ddr2_refresh_rate($)
1187{
1188        my $byte = shift;
1189        my @refresh = qw(Normal Reduced Reduced Extended Extended Extended);
1190        my @refresht = (15.625, 3.9, 7.8, 31.3, 62.5, 125);
1191
1192        return "$refresh[$byte & 0x7f] ($refresht[$byte & 0x7f] us)".
1193               ($byte & 0x80 ? " - Self Refresh" : "");
1194}
1195
1196# Parameter: EEPROM bytes 0-127 (using 3-62)
1197sub decode_ddr2_sdram($)
1198{
1199        my $bytes = shift;
1200        my $temp;
1201        my ($ctime, $ctime1, $ctime2, $ctime_min, $ctime_max);
1202
1203# SPD revision
1204        printl_cond($bytes->[62] != 0xff, "SPD Revision",
1205                    ($bytes->[62] >> 4) . "." . ($bytes->[62] & 0xf));
1206
1207# speed
1208        prints("Memory Characteristics");
1209
1210        $ctime_min = $ctime = ddr2_sdram_ctime($bytes->[9]);
1211        my $ddrclk = 2 * (1000 / $ctime);
1212        my $tbits = ($bytes->[7] * 256) + $bytes->[6];
1213        if ($bytes->[11] & 0x03) { $tbits = $tbits - 8; }
1214        my $pcclk = int ($ddrclk * $tbits / 8);
1215        # Round down to comply with Jedec
1216        $pcclk = $pcclk - ($pcclk % 100);
1217        $ddrclk = int ($ddrclk);
1218        printl("Maximum module speed", "$ddrclk MHz (PC2-${pcclk})");
1219
1220#size computation
1221        my $k = 0;
1222        my $ii = 0;
1223
1224        $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17;
1225        $k = (($bytes->[5] & 0x7) + 1) * $bytes->[17];
1226
1227        if($ii > 0 && $ii <= 12 && $k > 0) {
1228                printl("Size", ((1 << $ii) * $k) . " MB");
1229        } else {
1230                printl("Size", "INVALID: " . $bytes->[3] . "," . $bytes->[4] . "," .
1231                               $bytes->[5] . "," . $bytes->[17]);
1232        }
1233
1234        printl("Banks x Rows x Columns x Bits",
1235               join(' x ', $bytes->[17], $bytes->[3], $bytes->[4], $bytes->[6]));
1236        printl("Ranks", ($bytes->[5] & 7) + 1);
1237
1238        printl("SDRAM Device Width", $bytes->[13]." bits");
1239
1240        my @heights = ('< 25.4', '25.4', '25.4 - 30.0', '30.0', '30.5', '> 30.5');
1241        printl("Module Height", $heights[$bytes->[5] >> 5]." mm");
1242
1243        my @suptypes = ddr2_module_types($bytes->[20]);
1244        printl("Module Type".(@suptypes > 1 ? 's' : ''), join(', ', @suptypes));
1245
1246        printl("DRAM Package", $bytes->[5] & 0x10 ? "Stack" : "Planar");
1247
1248        printl("Voltage Interface Level",
1249               sdram_voltage_interface_level($bytes->[8]));
1250
1251        printl("Module Configuration Type",
1252               sdram_module_configuration_type($bytes->[11]));
1253
1254        printl("Refresh Rate", ddr2_refresh_rate($bytes->[12]));
1255
1256        my @burst;
1257        push @burst, 4 if ($bytes->[16] & 4);
1258        push @burst, 8 if ($bytes->[16] & 8);
1259        $burst[0] = 'None' if !@burst;
1260        printl("Supported Burst Lengths", join(', ', @burst));
1261
1262        my $highestCAS = 0;
1263        my %cas;
1264        for ($ii = 2; $ii < 7; $ii++) {
1265                if ($bytes->[18] & (1 << $ii)) {
1266                        $highestCAS = $ii;
1267                        $cas{$highestCAS}++;
1268                }
1269        }
1270
1271        my $trcd;
1272        my $trp;
1273        my $tras;
1274
1275        $trcd = ($bytes->[29] >> 2) + (($bytes->[29] & 3) * 0.25);
1276        $trp = ($bytes->[27] >> 2) + (($bytes->[27] & 3) * 0.25);
1277        $tras = $bytes->[30];
1278
1279# latencies
1280        printl("Supported CAS Latencies (tCL)", cas_latencies(keys %cas));
1281
1282# timings
1283        my ($cycle_time, $access_time, $core_timings);
1284
1285        if (exists $cas{$highestCAS}) {
1286                $core_timings = ddr_core_timings($highestCAS, $ctime,
1287                        $trcd, $trp, $tras) . as_ddr(2, $ctime);
1288
1289                $cycle_time = tns($ctime) . " at CAS $highestCAS (tCK min)";
1290                $access_time = tns(ddr2_sdram_atime($bytes->[10]))
1291                             . " at CAS $highestCAS (tAC)";
1292        }
1293
1294        if (exists $cas{$highestCAS-1} && spd_written(@$bytes[23..24])) {
1295                $ctime1 = ddr2_sdram_ctime($bytes->[23]);
1296                $core_timings .= "\n".ddr_core_timings($highestCAS-1, $ctime1,
1297                        $trcd, $trp, $tras) . as_ddr(2, $ctime1);
1298
1299                $cycle_time .= "\n".tns($ctime1)
1300                             . " at CAS ".($highestCAS-1);
1301                $access_time .= "\n".tns(ddr2_sdram_atime($bytes->[24]))
1302                              . " at CAS ".($highestCAS-1);
1303        }
1304
1305        if (exists $cas{$highestCAS-2} && spd_written(@$bytes[25..26])) {
1306                $ctime2 = ddr2_sdram_ctime($bytes->[25]);
1307                $core_timings .= "\n".ddr_core_timings($highestCAS-2, $ctime2,
1308                        $trcd, $trp, $tras) . as_ddr(2, $ctime2);
1309
1310                $cycle_time .= "\n".tns($ctime2)
1311                             . " at CAS ".($highestCAS-2);
1312                $access_time .= "\n".tns(ddr2_sdram_atime($bytes->[26]))
1313                              . " at CAS ".($highestCAS-2);
1314        }
1315
1316        $ctime_max = ddr2_sdram_ctime($bytes->[43]);
1317
1318        printl_cond(defined $core_timings, "tCL-tRCD-tRP-tRAS", $core_timings);
1319        printl_cond(defined $cycle_time, "Minimum Cycle Time", $cycle_time);
1320        printl_cond(defined $access_time, "Maximum Access Time", $access_time);
1321        printl_cond(($bytes->[43] & 0xf0) && $bytes->[43] != 0xff,
1322                    "Maximum Cycle Time (tCK max)",
1323                    $ctime_max == 0 ? "" : # Wouldn't be displayed, prevent div by 0
1324                    tns($ctime_max)." (DDR2-".int(2000 / $ctime_max).")");
1325
1326# standard DDR2 speeds
1327        prints("Timings at Standard Speeds");
1328        foreach $ctime (1.875, 2.5, 3, 3.75, 5) {
1329                my $best_cas;
1330
1331                # Find min CAS latency at this speed
1332                if (defined $ctime2 && $ctime >= $ctime2) {
1333                        $best_cas = $highestCAS-2;
1334                } elsif (defined $ctime1 && $ctime >= $ctime1) {
1335                        $best_cas = $highestCAS-1;
1336                } else {
1337                        $best_cas = $highestCAS;
1338                }
1339
1340                printl_cond($ctime >= $ctime_min && $ctime <= $ctime_max,
1341                            "tCL-tRCD-tRP-tRAS" . as_ddr(2,$ctime),
1342                            ddr_core_timings($best_cas, $ctime,
1343                                             $trcd, $trp, $tras));
1344        }
1345
1346# more timing information
1347        prints("Timing Parameters");
1348        # According to the JEDEC standard, the four timings below can't be less
1349        # than 0.1 ns, however we've seen memory modules code such values so
1350        # handle them properly.
1351        printl_cond($bytes->[32] && $bytes->[32] != 0xff,
1352                    "Address/Command Setup Time Before Clock (tIS)",
1353                    tns(ddr2_sdram_atime($bytes->[32])));
1354        printl_cond($bytes->[33] && $bytes->[33] != 0xff,
1355                    "Address/Command Hold Time After Clock (tIH)",
1356                    tns(ddr2_sdram_atime($bytes->[33])));
1357        printl_cond($bytes->[34] && $bytes->[34] != 0xff,
1358                    "Data Input Setup Time Before Strobe (tDS)",
1359                    tns(ddr2_sdram_atime($bytes->[34])));
1360        printl_cond($bytes->[35] && $bytes->[35] != 0xff,
1361                    "Data Input Hold Time After Strobe (tDH)",
1362                    tns(ddr2_sdram_atime($bytes->[35])));
1363
1364        printl("Minimum Row Precharge Delay (tRP)", tns($trp));
1365        printl_cond($bytes->[28] & 0xfc,
1366                    "Minimum Row Active to Row Active Delay (tRRD)",
1367                    tns($bytes->[28]/4));
1368        printl("Minimum RAS# to CAS# Delay (tRCD)", tns($trcd));
1369        printl("Minimum RAS# Pulse Width (tRAS)", tns($tras));
1370        printl_cond($bytes->[36] & 0xfc,
1371                    "Write Recovery Time (tWR)",
1372                    tns($bytes->[36]/4));
1373        printl_cond($bytes->[37] & 0xfc,
1374                    "Minimum Write to Read CMD Delay (tWTR)",
1375                    tns($bytes->[37]/4));
1376        printl_cond($bytes->[38] & 0xfc,
1377                    "Minimum Read to Pre-charge CMD Delay (tRTP)",
1378                    tns($bytes->[38]/4));
1379
1380        printl_cond($bytes->[41] && $bytes->[41] != 0xff,
1381                    "Minimum Active to Auto-refresh Delay (tRC)",
1382                    tns(ddr2_sdram_rtime($bytes->[41], 0,
1383                                         ($bytes->[40] >> 4) & 7)));
1384        printl_cond($bytes->[42],
1385                    "Minimum Recovery Delay (tRFC)",
1386                    tns(ddr2_sdram_rtime($bytes->[42], $bytes->[40] & 1,
1387                                         ($bytes->[40] >> 1) & 7)));
1388
1389        printl_cond($bytes->[44], "Maximum DQS to DQ Skew (tDQSQ)",
1390                    tns($bytes->[44]/100));
1391        printl_cond($bytes->[45], "Maximum Read Data Hold Skew (tQHS)",
1392                    tns($bytes->[45]/100));
1393        printl_cond($bytes->[46], "PLL Relock Time", $bytes->[46] . " us");
1394}
1395
1396# Return combined time in ns
1397sub ddr3_mtb_ftb($$$$)
1398{
1399        my ($byte1, $byte2, $mtb, $ftb) = @_;
1400
1401        # byte1 is unsigned in ns, but byte2 is signed in ps
1402        $byte2 -= 0x100 if $byte2 & 0x80;
1403
1404        return $byte1 * $mtb + $byte2 * $ftb / 1000;
1405}
1406
1407sub ddr3_reference_card($$)
1408{
1409        my ($rrc, $ext) = @_;
1410        my $alphabet = "ABCDEFGHJKLMNPRTUVWY";
1411        my $ref = $rrc & 0x1f;
1412        my $revision = $ext >> 5;
1413        my $ref_card;
1414
1415        return "ZZ" if $ref == 0x1f;
1416        $ref += 0x1f if $rrc & 0x80;
1417        $revision = (($rrc >> 5) & 0x03) if $revision == 0;
1418
1419        if ($ref < length($alphabet)) {
1420                # One letter reference card
1421                $ref_card = substr($alphabet, $ref, 1);
1422        } else {
1423                # Two letter reference card
1424                my $ref1 = int($ref / (length($alphabet)));
1425                $ref -= length($alphabet) * $ref1;
1426                $ref_card = substr($alphabet, $ref1, 1) .
1427                            substr($alphabet, $ref, 1);
1428        }
1429
1430        return "$ref_card revision $revision";
1431}
1432
1433sub ddr3_revision_number($)
1434{
1435        my $h = $_[0] >> 4;
1436        my $l = $_[0] & 0x0f;
1437
1438        # Decode as suggested by JEDEC Standard 21-C
1439        return sprintf("%d", $l) if $h == 0;
1440        return sprintf("%d.%d", $h, $l) if $h < 0xa;
1441        return sprintf("%c%d", ord('A') + $h - 0xa, $l);
1442}
1443
1444sub ddr3_device_type($)
1445{
1446        my $byte = shift;
1447        my $type = $byte & 0x80 ? "Non-Standard" : "Standard Monolithic";
1448        my $die_count = ($byte >> 4) & 0x07;
1449        my $loading = ($byte >> 2) & 0x03;
1450
1451        if ($die_count == 1) {
1452                $type .= "\nSingle die";
1453        } elsif ($die_count == 2) {
1454                $type .= "\n2 die";
1455        } elsif ($die_count == 3) {
1456                $type .= "\n4 die";
1457        } elsif ($die_count == 4) {
1458                $type .= "\n8 die";
1459        }
1460
1461        if ($loading == 1) {
1462                $type .= "\nMulti load stack";
1463        } elsif ($loading == 2) {
1464                $type .= "\nSingle load stack";
1465        }
1466
1467        return $type;
1468}
1469
1470use constant DDR3_UNBUFFERED    => 1;
1471use constant DDR3_REGISTERED    => 2;
1472use constant DDR3_CLOCKED       => 3;
1473use constant DDR3_LOAD_REDUCED  => 4;
1474
1475# Parameter: EEPROM bytes 0-127 (using 3-76)
1476sub decode_ddr3_sdram($)
1477{
1478        my $bytes = shift;
1479        my $temp;
1480        my $ctime;
1481        my ($ftb, $mtb);
1482        my $ii;
1483
1484        my @module_types = (
1485                { type => "Undefined",          width => "Unknown"      },
1486                { type => "RDIMM",              width => "133.35 mm",   family => DDR3_REGISTERED },
1487                { type => "UDIMM",              width => "133.35 mm",   family => DDR3_UNBUFFERED },
1488                { type => "SO-DIMM",            width => "67.6 mm",     family => DDR3_UNBUFFERED },
1489                { type => "Micro-DIMM",         width => "TBD",         family => DDR3_UNBUFFERED },
1490                { type => "Mini-RDIMM",         width => "82.0 mm",     family => DDR3_REGISTERED },
1491                { type => "Mini-UDIMM",         width => "82.0 mm",     family => DDR3_UNBUFFERED },
1492                { type => "Mini-CDIMM",         width => "67.6 mm",     family => DDR3_CLOCKED },
1493                { type => "72b-SO-UDIMM",       width => "67.6 mm",     family => DDR3_UNBUFFERED },
1494                { type => "72b-SO-RDIMM",       width => "67.6 mm",     family => DDR3_REGISTERED },
1495                { type => "72b-SO-CDIMM",       width => "67.6 mm",     family => DDR3_CLOCKED },
1496                { type => "LRDIMM",             width => "133.35 mm",   family => DDR3_LOAD_REDUCED },
1497                { type => "16b-SO-DIMM",        width => "67.6 mm",     family => DDR3_UNBUFFERED },
1498                { type => "32b-SO-DIMM",        width => "67.6 mm",     family => DDR3_UNBUFFERED },
1499        );
1500
1501        printl("Module Type", ($bytes->[3] <= $#module_types) ?
1502                                        $module_types[$bytes->[3]]->{type} :
1503                                        sprintf("Reserved (0x%.2X)", $bytes->[3]));
1504
1505# time bases
1506        if (($bytes->[9] & 0x0f) == 0 || $bytes->[11] == 0) {
1507                print STDERR "Invalid time base divisor, can't decode\n";
1508                return;
1509        }
1510        $ftb = ($bytes->[9] >> 4) / ($bytes->[9] & 0x0f);
1511        $mtb = $bytes->[10] / $bytes->[11];
1512
1513# speed
1514        prints("Memory Characteristics");
1515
1516        $ctime = ddr3_mtb_ftb($bytes->[12], $bytes->[34], $mtb, $ftb);
1517        # Starting with DDR3-1866, vendors may start approximating the
1518        # minimum cycle time. Try to guess what they really meant so
1519        # that the reported speed matches the standard.
1520        for ($ii = 7; $ii < 15; $ii++) {
1521                if ($ctime > 7.5/$ii - $ftb/1000 && $ctime < 7.5/$ii + $ftb/1000) {
1522                        $ctime = 7.5/$ii;
1523                        last;
1524                }
1525        }
1526
1527        my $ddrclk = 2 * (1000 / $ctime);
1528        my $tbits = 1 << (($bytes->[8] & 7) + 3);
1529        my $pcclk = int ($ddrclk * $tbits / 8);
1530        # Round down to comply with Jedec
1531        $pcclk = $pcclk - ($pcclk % 100);
1532        $ddrclk = int ($ddrclk);
1533        printl("Maximum module speed", "$ddrclk MHz (PC3-${pcclk})");
1534
1535# Size computation
1536
1537        my $cap =  ($bytes->[4]       & 15) + 28;
1538        $cap   +=  ($bytes->[8]       & 7)  + 3;
1539        $cap   -=  ($bytes->[7]       & 7)  + 2;
1540        $cap   -= 20 + 3;
1541        my $k   = (($bytes->[7] >> 3) & 31) + 1;
1542        printl("Size", ((1 << $cap) * $k) . " MB");
1543
1544        printl("Banks x Rows x Columns x Bits",
1545               join(' x ', 1 << ((($bytes->[4] >> 4) &  7) +  3),
1546                           ((($bytes->[5] >> 3) & 31) + 12),
1547                           ( ($bytes->[5]       &  7) +  9),
1548                           ( 1 << (($bytes->[8] &  7) + 3)) ));
1549        printl("Ranks", $k);
1550
1551        printl("SDRAM Device Width", (1 << (($bytes->[7] & 7) + 2))." bits");
1552
1553        printl("Bus Width Extension", ($bytes->[8] & 24)." bits");
1554
1555        my $taa;
1556        my $trcd;
1557        my $trp;
1558        my $tras;
1559
1560        $taa  = ddr3_mtb_ftb($bytes->[16], $bytes->[35], $mtb, $ftb);
1561        $trcd = ddr3_mtb_ftb($bytes->[18], $bytes->[36], $mtb, $ftb);
1562        $trp  = ddr3_mtb_ftb($bytes->[20], $bytes->[37], $mtb, $ftb);
1563        $tras = ((($bytes->[21] & 0x0f) << 8) + $bytes->[22]) * $mtb;
1564
1565        printl("tCL-tRCD-tRP-tRAS", ddr_core_timings(ceil($taa / $ctime), $ctime, $trcd, $trp, $tras));
1566
1567# latencies
1568        my $highestCAS = 0;
1569        my %cas;
1570        my $cas_sup = ($bytes->[15] << 8) + $bytes->[14];
1571        for ($ii = 0; $ii < 15; $ii++) {
1572                if ($cas_sup & (1 << $ii)) {
1573                        $highestCAS = $ii + 4;
1574                        $cas{$highestCAS}++;
1575                }
1576        }
1577        printl("Supported CAS Latencies (tCL)", cas_latencies(keys %cas));
1578
1579# standard DDR3 speeds
1580        prints("Timings at Standard Speeds");
1581        foreach my $ctime_at_speed (7.5/8, 7.5/7, 1.25, 1.5, 1.875, 2.5) {
1582                my $best_cas = 0;
1583
1584                # Find min CAS latency at this speed
1585                for ($ii = 14; $ii >= 0; $ii--) {
1586                        next unless ($cas_sup & (1 << $ii));
1587                        if (ceil($taa / $ctime_at_speed) <= $ii + 4) {
1588                                $best_cas = $ii + 4;
1589                        }
1590                }
1591
1592                printl_cond($best_cas && $ctime_at_speed >= $ctime,
1593                            "tCL-tRCD-tRP-tRAS" . as_ddr(3, $ctime_at_speed),
1594                            ddr_core_timings($best_cas, $ctime_at_speed,
1595                                             $trcd, $trp, $tras));
1596        }
1597
1598# more timing information
1599        prints("Timing Parameters");
1600
1601        printl("Minimum Cycle Time (tCK)", tns3($ctime));
1602        printl("Minimum CAS Latency Time (tAA)", tns3($taa));
1603        printl("Minimum Write Recovery time (tWR)", tns3($bytes->[17] * $mtb));
1604        printl("Minimum RAS# to CAS# Delay (tRCD)", tns3($trcd));
1605        printl("Minimum Row Active to Row Active Delay (tRRD)",
1606                tns3($bytes->[19] * $mtb));
1607        printl("Minimum Row Precharge Delay (tRP)", tns3($trp));
1608        printl("Minimum Active to Precharge Delay (tRAS)", tns3($tras));
1609        printl("Minimum Active to Auto-Refresh Delay (tRC)",
1610                tns3(ddr3_mtb_ftb((($bytes->[21] & 0xf0) << 4) + $bytes->[23], $bytes->[38], $mtb, $ftb)));
1611        printl("Minimum Recovery Delay (tRFC)",
1612                tns3((($bytes->[25] << 8) + $bytes->[24]) * $mtb));
1613        printl("Minimum Write to Read CMD Delay (tWTR)",
1614                tns3($bytes->[26] * $mtb));
1615        printl("Minimum Read to Pre-charge CMD Delay (tRTP)",
1616                tns3($bytes->[27] * $mtb));
1617        printl("Minimum Four Activate Window Delay (tFAW)",
1618                tns3(((($bytes->[28] & 15) << 8) + $bytes->[29]) * $mtb));
1619
1620# miscellaneous stuff
1621        prints("Optional Features");
1622
1623        my $volts = "1.5V";
1624        if ($bytes->[6] & 1) {
1625                $volts .= " tolerant";
1626        }
1627        if ($bytes->[6] & 2) {
1628                $volts .= ", 1.35V ";
1629        }
1630        if ($bytes->[6] & 4) {
1631                $volts .= ", 1.2X V";
1632        }
1633        printl("Operable voltages", $volts);
1634        printl("RZQ/6 supported?", ($bytes->[30] & 1) ? "Yes" : "No");
1635        printl("RZQ/7 supported?", ($bytes->[30] & 2) ? "Yes" : "No");
1636        printl("DLL-Off Mode supported?", ($bytes->[30] & 128) ? "Yes" : "No");
1637        printl("Operating temperature range", sprintf "0-%d degrees C",
1638                ($bytes->[31] & 1) ? 95 : 85);
1639        printl_cond($bytes->[31] & 1,
1640                    "Refresh Rate in extended temp range",
1641                    ($bytes->[31] & 2) ? "1X" : "2X");
1642        printl("Auto Self-Refresh?", ($bytes->[31] & 4) ? "Yes" : "No");
1643        printl("On-Die Thermal Sensor readout?",
1644                ($bytes->[31] & 8) ? "Yes" : "No");
1645        printl("Partial Array Self-Refresh?",
1646                ($bytes->[31] & 128) ? "Yes" : "No");
1647        printl("Module Thermal Sensor",
1648                ($bytes->[32] & 128) ? "Yes" : "No");
1649        printl("SDRAM Device Type", ddr3_device_type($bytes->[33]));
1650
1651        # Following bytes are type-specific, so don't continue if type
1652        # isn't known.
1653        return if $bytes->[3] == 0 || $bytes->[3] > $#module_types;
1654
1655        if ($module_types[$bytes->[3]]->{family} == DDR3_UNBUFFERED ||
1656            $module_types[$bytes->[3]]->{family} == DDR3_REGISTERED ||
1657            $module_types[$bytes->[3]]->{family} == DDR3_CLOCKED ||
1658            $module_types[$bytes->[3]]->{family} == DDR3_LOAD_REDUCED) {
1659                prints("Physical Characteristics");
1660                printl("Module Height", (($bytes->[60] & 31) + 15) . " mm");
1661                printl("Module Thickness", sprintf("%d mm front, %d mm back",
1662                                                ($bytes->[61] & 15) + 1,
1663                                                (($bytes->[61] >> 4) & 15) +1));
1664                printl("Module Width", $module_types[$bytes->[3]]->{width});
1665                printl("Module Reference Card", ddr3_reference_card($bytes->[62], $bytes->[60]));
1666
1667                printl_cond($module_types[$bytes->[3]]->{family} == DDR3_UNBUFFERED,
1668                            "Rank 1 Mapping", $bytes->[63] & 0x01 ? "Mirrored" : "Standard");
1669        }
1670
1671        if ($module_types[$bytes->[3]]->{family} == DDR3_REGISTERED) {
1672                prints("Registered DIMM");
1673
1674                my @rows = ("Undefined", 1, 2, 4);
1675                printl("# DRAM Rows", $rows[($bytes->[63] >> 2) & 3]);
1676                printl("# Registers", $rows[$bytes->[63] & 3]);
1677                printl("Register manufacturer",
1678                        manufacturer_ddr3($bytes->[65], $bytes->[66]));
1679                printl("Register device type",
1680                                (($bytes->[68] & 7) == 0) ? "SSTE32882" :
1681                                        "Undefined");
1682                printl_cond($bytes->[67] != 0xff,
1683                            "Register revision", ddr3_revision_number($bytes->[67]));
1684                printl("Heat spreader", $bytes->[64] & 0x80 ? "Yes" : "No");
1685        }
1686
1687        if ($module_types[$bytes->[3]]->{family} == DDR3_LOAD_REDUCED) {
1688                prints("Load Reduced DIMM");
1689
1690                my @rows = ("Undefined", 1, 2, "Reserved");
1691                printl("# DRAM Rows", $rows[($bytes->[63] >> 2) & 3]);
1692                my @mirroring = ("None", "Odd ranks", "Reserved", "Reserved");
1693                printl("Mirroring", $mirroring[$bytes->[63] & 3]);
1694                printl("Rank Numbering", $bytes->[63] & 0x20 ? "Even only" : "Contiguous");
1695                printl("Buffer Orientation", $bytes->[63] & 0x10 ? "Horizontal" : "Vertical");
1696                printl("Register manufacturer",
1697                        manufacturer_ddr3($bytes->[65], $bytes->[66]));
1698                printl_cond($bytes->[64] != 0xff,
1699                            "Buffer Revision", ddr3_revision_number($bytes->[64]));
1700                printl("Heat spreader", $bytes->[63] & 0x80 ? "Yes" : "No");
1701        }
1702
1703}
1704
1705# Parameter: EEPROM bytes 0-127 (using 4-5)
1706sub decode_direct_rambus($)
1707{
1708        my $bytes = shift;
1709
1710#size computation
1711        prints("Memory Characteristics");
1712
1713        my $ii;
1714
1715        $ii = ($bytes->[4] & 0x0f) + ($bytes->[4] >> 4) + ($bytes->[5] & 0x07) - 13;
1716
1717        if ($ii > 0 && $ii < 16) {
1718                printl("Size", (1 << $ii) . " MB");
1719        } else {
1720                printl("Size", sprintf("INVALID: 0x%02x, 0x%02x",
1721                                       $bytes->[4], $bytes->[5]));
1722        }
1723}
1724
1725# Parameter: EEPROM bytes 0-127 (using 3-5)
1726sub decode_rambus($)
1727{
1728        my $bytes = shift;
1729
1730#size computation
1731        prints("Memory Characteristics");
1732
1733        my $ii;
1734
1735        $ii = ($bytes->[3] & 0x0f) + ($bytes->[3] >> 4) + ($bytes->[5] & 0x07) - 13;
1736
1737        if ($ii > 0 && $ii < 16) {
1738                printl("Size", (1 << $ii) . " MB");
1739        } else {
1740                printl("Size", "INVALID: " . sprintf("0x%02x, 0x%02x",
1741                                               $bytes->[3], $bytes->[5]));
1742        }
1743}
1744
1745%decode_callback = (
1746        "SDR SDRAM"     => \&decode_sdr_sdram,
1747        "DDR SDRAM"     => \&decode_ddr_sdram,
1748        "DDR2 SDRAM"    => \&decode_ddr2_sdram,
1749        "DDR3 SDRAM"    => \&decode_ddr3_sdram,
1750        "Direct Rambus" => \&decode_direct_rambus,
1751        "Rambus"        => \&decode_rambus,
1752);
1753
1754# Parameter: Manufacturing year/week bytes
1755sub manufacture_date($$)
1756{
1757        my ($year, $week) = @_;
1758
1759        # In theory the year and week are in BCD format, but
1760        # this is not always true in practice :(
1761        if (($year & 0xf0) <= 0x90 && ($year & 0x0f) <= 0x09
1762         && ($week & 0xf0) <= 0x90 && ($week & 0x0f) <= 0x09) {
1763                # Note that this heuristic will break in year 2080
1764                return sprintf("%d%02X-W%02X",
1765                                $year >= 0x80 ? 19 : 20, $year, $week);
1766        # Fallback to binary format if it seems to make sense
1767        } elsif ($year <= 99 && $week >= 1 && $week <= 53) {
1768                return sprintf("%d%02d-W%02d",
1769                                $year >= 80 ? 19 : 20, $year, $week);
1770        } else {
1771                return sprintf("0x%02X%02X", $year, $week);
1772        }
1773}
1774
1775sub printl_mfg_location_code($)
1776{
1777        my $code = shift;
1778        my $letter = chr($code);
1779
1780        # Try the location code as ASCII first, as earlier specifications
1781        # suggested this. As newer specifications don't mention it anymore,
1782        # we still fall back to binary.
1783        printl_cond(spd_written($code), "Manufacturing Location Code",
1784                    $letter =~ m/^[\w\d]$/ ? $letter : sprintf("0x%.2X", $code));
1785}
1786
1787sub printl_mfg_assembly_serial(@)
1788{
1789        printl_cond(spd_written(@_), "Assembly Serial Number",
1790                    sprintf("0x%02X%02X%02X%02X", @_));
1791}
1792
1793# Parameter: EEPROM bytes 0-175 (using 117-149)
1794sub decode_ddr3_mfg_data($)
1795{
1796        my $bytes = shift;
1797
1798        prints("Manufacturer Data");
1799
1800        printl("Module Manufacturer",
1801               manufacturer_ddr3($bytes->[117], $bytes->[118]));
1802
1803        printl_cond(spd_written(@{$bytes}[148..149]),
1804                    "DRAM Manufacturer",
1805                    manufacturer_ddr3($bytes->[148], $bytes->[149]));
1806
1807        printl_mfg_location_code($bytes->[119]);
1808
1809        printl_cond(spd_written(@{$bytes}[120..121]),
1810                    "Manufacturing Date",
1811                    manufacture_date($bytes->[120], $bytes->[121]));
1812
1813        printl_mfg_assembly_serial(@{$bytes}[122..125]);
1814
1815        printl("Part Number", part_number(@{$bytes}[128..145]));
1816
1817        printl_cond(spd_written(@{$bytes}[146..147]),
1818                    "Revision Code",
1819                    sprintf("0x%02X%02X", $bytes->[146], $bytes->[147]));
1820}
1821
1822# Parameter: EEPROM bytes 0-127 (using 64-98)
1823sub decode_manufacturing_information($)
1824{
1825        my $bytes = shift;
1826        my ($temp, $extra);
1827
1828        prints("Manufacturing Information");
1829
1830        # $extra is a reference to an array containing up to
1831        # 7 extra bytes from the Manufacturer field. Sometimes
1832        # these bytes are filled with interesting data.
1833        ($temp, $extra) = manufacturer(@{$bytes}[64..71]);
1834        printl("Manufacturer", $temp);
1835        $temp = manufacturer_data(@{$extra});
1836        printl_cond(defined $temp, "Custom Manufacturer Data", $temp);
1837
1838        printl_mfg_location_code($bytes->[72]);
1839
1840        printl("Part Number", part_number(@{$bytes}[73..90]));
1841
1842        printl_cond(spd_written(@{$bytes}[91..92]), "Revision Code",
1843                    sprintf("0x%02X%02X", @{$bytes}[91..92]));
1844
1845        printl_cond(spd_written(@{$bytes}[93..94]), "Manufacturing Date",
1846               manufacture_date($bytes->[93], $bytes->[94]));
1847
1848        printl_mfg_assembly_serial(@{$bytes}[95..98]);
1849}
1850
1851# Parameter: EEPROM bytes 0-127 (using 126-127)
1852sub decode_intel_spec_freq($)
1853{
1854        my $bytes = shift;
1855        my $temp;
1856
1857        prints("Intel Specification");
1858
1859        if ($bytes->[126] == 0x66) { $temp = "66 MHz"; }
1860        elsif ($bytes->[126] == 100) { $temp = "100 MHz or 133 MHz"; }
1861        elsif ($bytes->[126] == 133) { $temp = "133 MHz"; }
1862        else { $temp = "Undefined!"; }
1863        printl("Frequency", $temp);
1864
1865        $temp = "";
1866        if ($bytes->[127] & 1) { $temp .= "Intel Concurrent Auto-precharge\n"; }
1867        if ($bytes->[127] & 2) { $temp .= "CAS Latency = 2\n"; }
1868        if ($bytes->[127] & 4) { $temp .= "CAS Latency = 3\n"; }
1869        if ($bytes->[127] & 8) { $temp .= "Junction Temp A (100 degrees C)\n"; }
1870        else { $temp .= "Junction Temp B (90 degrees C)\n"; }
1871        if ($bytes->[127] & 16) { $temp .= "CLK 3 Connected\n"; }
1872        if ($bytes->[127] & 32) { $temp .= "CLK 2 Connected\n"; }
1873        if ($bytes->[127] & 64) { $temp .= "CLK 1 Connected\n"; }
1874        if ($bytes->[127] & 128) { $temp .= "CLK 0 Connected\n"; }
1875        if (($bytes->[127] & 192) == 192) { $temp .= "Double-sided DIMM\n"; }
1876        elsif (($bytes->[127] & 192) != 0) { $temp .= "Single-sided DIMM\n"; }
1877        printl("Details for 100 MHz Support", $temp);
1878}
1879
1880# Read various hex dump style formats: hexdump, hexdump -C, i2cdump, eeprog
1881# note that normal 'hexdump' format on a little-endian system byte-swaps
1882# words, using hexdump -C is better.
1883sub read_hexdump($)
1884{
1885        my $addr = 0;
1886        my $repstart = 0;
1887        my @bytes;
1888        my $header = 1;
1889        my $word = 0;
1890
1891        # Look in the cache first
1892        return @{$hexdump_cache{$_[0]}} if exists $hexdump_cache{$_[0]};
1893
1894        open F, '<', $_[0] or die "Unable to open: $_[0]";
1895        while (<F>) {
1896                chomp;
1897                if (/^\*$/) {
1898                        $repstart = $addr;
1899                        next;
1900                }
1901                /^(?:0000 )?([a-f\d]{2,8}):?\s+((:?[a-f\d]{4}\s*){8}|(:?[a-f\d]{2}\s*){16})/i ||
1902                /^(?:0000 )?([a-f\d]{2,8}):?\s*$/i;
1903                next if (!defined $1 && $header);               # skip leading unparsed lines
1904
1905                defined $1 or die "Unable to parse input";
1906                $header = 0;
1907
1908                $addr = hex $1;
1909                if ($repstart) {
1910                        @bytes[$repstart .. ($addr-1)] =
1911                                (@bytes[($repstart-16)..($repstart-1)]) x (($addr-$repstart)/16);
1912                        $repstart = 0;
1913                }
1914                last unless defined $2;
1915                foreach (split(/\s+/, $2)) {
1916                        if (/^(..)(..)$/) {
1917                                $word |= 1;
1918                                if ($use_hexdump eq LITTLEENDIAN) {
1919                                        $bytes[$addr++] = hex($2);
1920                                        $bytes[$addr++] = hex($1);
1921                                } else {
1922                                        $bytes[$addr++] = hex($1);
1923                                        $bytes[$addr++] = hex($2);
1924                                }
1925                        } else {
1926                                $bytes[$addr++] = hex($_);
1927                        }
1928                }
1929        }
1930        close F;
1931        $header and die "Unable to parse any data from hexdump '$_[0]'";
1932        $word and printc("Using $use_hexdump 16-bit hex dump");
1933
1934        # Cache the data for later use
1935        $hexdump_cache{$_[0]} = \@bytes;
1936        return @bytes;
1937}
1938
1939# Returns the (total, used) number of bytes in the EEPROM,
1940# assuming it is a non-Rambus SPD EEPROM.
1941sub spd_sizes($)
1942{
1943        my $bytes = shift;
1944
1945        if ($bytes->[2] >= 9) {
1946                # For FB-DIMM and newer, decode number of bytes written
1947                my $spd_len = ($bytes->[0] >> 4) & 7;
1948                my $size = 64 << ($bytes->[0] & 15);
1949                if ($spd_len == 0) {
1950                        return ($size, 128);
1951                } elsif ($spd_len == 1) {
1952                        return ($size, 176);
1953                } elsif ($spd_len == 2) {
1954                        return ($size, 256);
1955                } else {
1956                        return (64, 64);
1957                }
1958        } else {
1959                my $size;
1960                if ($bytes->[1] <= 14) {
1961                        $size = 1 << $bytes->[1];
1962                } elsif ($bytes->[1] == 0) {
1963                        $size = "RFU";
1964                } else { $size = "ERROR!" }
1965
1966                return ($size, ($bytes->[0] < 64) ? 64 : $bytes->[0]);
1967        }
1968}
1969
1970# Read bytes from SPD-EEPROM
1971# Note: offset must be a multiple of 16!
1972sub readspd($$$)
1973{
1974        my ($offset, $size, $dimm_i) = @_;
1975        my @bytes;
1976        if ($use_hexdump) {
1977                @bytes = read_hexdump($dimm_i);
1978                return @bytes[$offset..($offset + $size - 1)];
1979        } elsif ($use_sysfs) {
1980                # Kernel 2.6 with sysfs
1981                sysopen(HANDLE, "$dimm_i/eeprom", O_RDONLY)
1982                        or die "Cannot open $dimm_i/eeprom";
1983                binmode HANDLE;
1984                sysseek(HANDLE, $offset, SEEK_SET)
1985                        or die "Cannot seek $dimm_i/eeprom";
1986                sysread(HANDLE, my $eeprom, $size)
1987                        or die "Cannot read $dimm_i/eeprom";
1988                close HANDLE;
1989                @bytes = unpack("C*", $eeprom);
1990        } else {
1991                # Kernel 2.4 with procfs
1992                for my $i (0 .. ($size-1)/16) {
1993                        my $hexoff = sprintf('%02x', $offset + $i * 16);
1994                        push @bytes, split(" ", `cat $dimm_i/$hexoff`);
1995                }
1996        }
1997        return @bytes;
1998}
1999
2000# Calculate and verify checksum of first 63 bytes
2001sub checksum($)
2002{
2003        my $bytes = shift;
2004        my $dimm_checksum = 0;
2005        local $_;
2006
2007        $dimm_checksum += $bytes->[$_] foreach (0 .. 62);
2008        $dimm_checksum &= 0xff;
2009
2010        return ("EEPROM Checksum of bytes 0-62",
2011                ($bytes->[63] == $dimm_checksum) ? 1 : 0,
2012                sprintf('0x%02X', $bytes->[63]),
2013                sprintf('0x%02X', $dimm_checksum));
2014}
2015
2016# Calculate and verify CRC
2017sub check_crc($)
2018{
2019        my $bytes = shift;
2020        my $crc = 0;
2021        my $crc_cover = $bytes->[0] & 0x80 ? 116 : 125;
2022        my $crc_ptr = 0;
2023        my $crc_bit;
2024
2025        while ($crc_ptr <= $crc_cover) {
2026                $crc = $crc ^ ($bytes->[$crc_ptr] << 8);
2027                for ($crc_bit = 0; $crc_bit < 8; $crc_bit++) {
2028                        if ($crc & 0x8000) {
2029                                $crc = ($crc << 1) ^ 0x1021;
2030                        } else {
2031                                $crc = $crc << 1
2032                        }
2033                }
2034                $crc_ptr++;
2035        }
2036        $crc &= 0xffff;
2037
2038        my $dimm_crc = ($bytes->[127] << 8) | $bytes->[126];
2039        return ("EEPROM CRC of bytes 0-$crc_cover",
2040                ($dimm_crc == $crc) ? 1 : 0,
2041                sprintf("0x%04X", $dimm_crc),
2042                sprintf("0x%04X", $crc));
2043}
2044
2045# Parse command-line
2046foreach (@ARGV) {
2047        if ($_ eq '-h' || $_ eq '--help') {
2048                print "Usage: $0 [-c] [-f [-b]] [-x|-X file [files..]]\n",
2049                        "       $0 -h\n\n",
2050                        "  -f, --format            Print nice html output\n",
2051                        "  -b, --bodyonly          Don't print html header\n",
2052                        "                          (useful for postprocessing the output)\n",
2053                        "      --side-by-side      Display all DIMMs side-by-side if possible\n",
2054                        "      --merge-cells       Merge neighbour cells with identical values\n",
2055                        "                          (side-by-side output only, default)\n",
2056                        "      --no-merge-cells    Don't merge neighbour cells with identical values\n",
2057                        "                          (side-by-side output only)\n",
2058                        "  -c, --checksum          Decode completely even if checksum fails\n",
2059                        "  -x,                     Read data from hexdump files\n",
2060                        "  -X,                     Same as -x except treat multibyte hex\n",
2061                        "                          data as little endian\n",
2062                        "  -h, --help              Display this usage summary\n";
2063                print <<"EOF";
2064
2065Hexdumps can be the output from hexdump, hexdump -C, i2cdump, eeprog and
2066likely many other progams producing hex dumps of one kind or another.  Note
2067that the default output of "hexdump" will be byte-swapped on little-endian
2068systems and you must use -X instead of -x, otherwise the dump will not be
2069parsed correctly.  It is better to use "hexdump -C", which is not ambiguous.
2070EOF
2071                exit;
2072        }
2073
2074        if ($_ eq '-f' || $_ eq '--format') {
2075                $opt_html = 1;
2076                next;
2077        }
2078        if ($_ eq '-b' || $_ eq '--bodyonly') {
2079                $opt_bodyonly = 1;
2080                next;
2081        }
2082        if ($_ eq '--side-by-side') {
2083                $opt_side_by_side = 1;
2084                next;
2085        }
2086        if ($_ eq '--merge-cells') {
2087                $opt_merge = 1;
2088                next;
2089        }
2090        if ($_ eq '--no-merge-cells') {
2091                $opt_merge = 0;
2092                next;
2093        }
2094        if ($_ eq '-c' || $_ eq '--checksum') {
2095                $opt_igncheck = 1;
2096                next;
2097        }
2098        if ($_ eq '-x') {
2099                $use_hexdump = BIGENDIAN;
2100                next;
2101        }
2102        if ($_ eq '-X') {
2103                $use_hexdump = LITTLEENDIAN;
2104                next;
2105        }
2106
2107        if (m/^-/) {
2108                print STDERR "Unrecognized option $_\n";
2109                exit;
2110        }
2111
2112        push @dimm, { eeprom => basename($_), file => $_ } if $use_hexdump;
2113}
2114
2115# Default values
2116$opt_merge = 1 unless defined $opt_merge;
2117
2118# From a sysfs device path and an attribute name, return the attribute
2119# value, or undef (stolen from sensors-detect)
2120sub sysfs_device_attribute
2121{
2122        my ($device, $attr) = @_;
2123        my $value;
2124
2125        open(local *FILE, "$device/$attr") or return "";
2126        $value = <FILE>;
2127        close(FILE);
2128        return unless defined $value;
2129
2130        chomp($value);
2131        return $value;
2132}
2133
2134sub get_dimm_list
2135{
2136        my (@dirs, $dir, $opened, $file, @files);
2137
2138        if ($use_sysfs) {
2139                @dirs = ('/sys/bus/i2c/drivers/eeprom', '/sys/bus/i2c/drivers/at24');
2140        } else {
2141                @dirs = ('/proc/sys/dev/sensors');
2142        }
2143
2144        foreach $dir (@dirs) {
2145                next unless opendir(local *DIR, $dir);
2146                $opened++;
2147                while (defined($file = readdir(DIR))) {
2148                        if ($use_sysfs) {
2149                                # We look for I2C devices like 0-0050 or 2-0051
2150                                next unless $file =~ /^\d+-[\da-f]+$/i;
2151                                next unless -d "$dir/$file";
2152
2153                                # Device name must be eeprom (driver eeprom)
2154                                # or spd (driver at24)
2155                                my $attr = sysfs_device_attribute("$dir/$file", "name");
2156                                next unless defined $attr &&
2157                                            ($attr eq "eeprom" || $attr eq "spd");
2158                        } else {
2159                                next unless $file =~ /^eeprom-/;
2160                        }
2161                        push @files, { eeprom => "$file",
2162                                       file => "$dir/$file" };
2163                }
2164                close(DIR);
2165        }
2166
2167        if (!$opened) {
2168                print STDERR "No EEPROM found, try loading the eeprom or at24 module\n";
2169                exit;
2170        }
2171
2172        return sort { $a->{file} cmp $b->{file} } @files;
2173}
2174
2175# @dimm is a list of hashes. There's one hash for each EEPROM we found.
2176# Each hash has the following keys:
2177#  * eeprom: Name of the eeprom data file
2178#  * file: Full path to the eeprom data file
2179#  * bytes: The EEPROM data (array)
2180#  * is_rambus: Whether this is a RAMBUS DIMM or not (boolean)
2181#  * chk_label: The label to display for the checksum or CRC
2182#  * chk_valid: Whether the checksum or CRC is valid or not (boolean)
2183#  * chk_spd: The checksum or CRC value found in the EEPROM
2184#  * chk_calc: The checksum or CRC computed from the EEPROM data
2185# Keys are added over time.
2186@dimm = get_dimm_list() unless $use_hexdump;
2187
2188for my $i (0 .. $#dimm) {
2189        my @bytes = readspd(0, 128, $dimm[$i]->{file});
2190        $dimm[$i]->{bytes} = \@bytes;
2191        $dimm[$i]->{is_rambus} = $bytes[0] < 4;         # Simple heuristic
2192        if ($dimm[$i]->{is_rambus} || $bytes[2] < 9) {
2193                ($dimm[$i]->{chk_label}, $dimm[$i]->{chk_valid},
2194                 $dimm[$i]->{chk_spd}, $dimm[$i]->{chk_calc}) =
2195                        checksum(\@bytes);
2196        } else {
2197                ($dimm[$i]->{chk_label}, $dimm[$i]->{chk_valid},
2198                 $dimm[$i]->{chk_spd}, $dimm[$i]->{chk_calc}) =
2199                        check_crc(\@bytes);
2200        }
2201}
2202
2203# Checksum or CRC validation
2204if (!$opt_igncheck) {
2205        for (my $i = 0; $i < @dimm; ) {
2206                if ($dimm[$i]->{chk_valid}) {
2207                        $i++;
2208                } else {
2209                        splice(@dimm, $i, 1);
2210                }
2211        }
2212}
2213
2214
2215if ($opt_html && !$opt_bodyonly) {
2216        print "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.1//EN' \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n\n",
2217              "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\">\n",
2218              "<head>\n",
2219              "\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" />\n",
2220              "\t<title>PC DIMM Serial Presence Detect Tester/Decoder Output</title>\n",
2221              "</head>\n\n",
2222              "<body>\n";
2223}
2224
2225printc("decode-dimms version $revision");
2226printh('Memory Serial Presence Detect Decoder',
2227'By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner,
2228Jean Delvare, Trent Piepho and others');
2229
2230# Process the valid entries
2231for $current (0 .. $#dimm) {
2232        my @bytes = @{$dimm[$current]->{bytes}};
2233
2234        if ($opt_side_by_side) {
2235                printl("Decoding EEPROM", $dimm[$current]->{eeprom});
2236        }
2237
2238        if (!$use_hexdump) {
2239                if ($dimm[$current]->{file} =~ /-([\da-f]+)$/i) {
2240                        my $dimm_num = hex($1) - 0x50 + 1;
2241                        if ($dimm_num >= 1 && $dimm_num <= 8) {
2242                                printl("Guessing DIMM is in", "bank $dimm_num");
2243                        }
2244                }
2245        }
2246
2247# Decode first 3 bytes (0-2)
2248        prints("SPD EEPROM Information");
2249
2250        printl($dimm[$current]->{chk_label}, ($dimm[$current]->{chk_valid} ?
2251                sprintf("OK (%s)", $dimm[$current]->{chk_calc}) :
2252                sprintf("Bad\n(found %s, calculated %s)",
2253                        $dimm[$current]->{chk_spd}, $dimm[$current]->{chk_calc})));
2254
2255        my $temp;
2256        if ($dimm[$current]->{is_rambus}) {
2257                if ($bytes[0] == 1) { $temp = "0.7"; }
2258                elsif ($bytes[0] == 2) { $temp = "1.0"; }
2259                elsif ($bytes[0] == 0) { $temp = "Invalid"; }
2260                else { $temp = "Reserved"; }
2261                printl("SPD Revision", $temp);
2262        } else {
2263                my ($spd_size, $spd_used) = spd_sizes(\@bytes);
2264                printl("# of bytes written to SDRAM EEPROM", $spd_used);
2265                printl("Total number of bytes in EEPROM", $spd_size);
2266
2267                # If there's more data than what we've read, let's
2268                # read it now.  DDR3 will need this data.
2269                if ($spd_used > @bytes) {
2270                        push (@bytes,
2271                              readspd(@bytes, $spd_used - @bytes,
2272                                      $dimm[$current]->{file}));
2273                }
2274        }
2275
2276        my $type = sprintf("Unknown (0x%02x)", $bytes[2]);
2277        if ($dimm[$current]->{is_rambus}) {
2278                if ($bytes[2] == 1) { $type = "Direct Rambus"; }
2279                elsif ($bytes[2] == 17) { $type = "Rambus"; }
2280        } else {
2281                my @type_list = (
2282                        "Reserved", "FPM DRAM",         # 0, 1
2283                        "EDO", "Pipelined Nibble",      # 2, 3
2284                        "SDR SDRAM", "Multiplexed ROM", # 4, 5
2285                        "DDR SGRAM", "DDR SDRAM",       # 6, 7
2286                        "DDR2 SDRAM", "FB-DIMM",        # 8, 9
2287                        "FB-DIMM Probe", "DDR3 SDRAM",  # 10, 11
2288                );
2289                if ($bytes[2] < @type_list) {
2290                        $type = $type_list[$bytes[2]];
2291                }
2292        }
2293        printl("Fundamental Memory type", $type);
2294
2295# Decode next 61 bytes (3-63, depend on memory type)
2296        $decode_callback{$type}->(\@bytes)
2297                if exists $decode_callback{$type};
2298
2299        if ($type eq "DDR3 SDRAM") {
2300                # Decode DDR3-specific manufacturing data in bytes
2301                # 117-149
2302                decode_ddr3_mfg_data(\@bytes)
2303        } else {
2304                # Decode next 35 bytes (64-98, common to most
2305                # memory types)
2306                decode_manufacturing_information(\@bytes);
2307        }
2308
2309# Next 27 bytes (99-125) are manufacturer specific, can't decode
2310
2311# Last 2 bytes (126-127) are reserved, Intel used them as an extension
2312        if ($type eq "SDR SDRAM") {
2313                decode_intel_spec_freq(\@bytes);
2314        }
2315}
2316
2317# Side-by-side output format is only possible if all DIMMs have a similar
2318# output structure
2319if ($opt_side_by_side) {
2320        for $current (1 .. $#dimm) {
2321                my @ref_output = @{$dimm[0]->{output}};
2322                my @test_output = @{$dimm[$current]->{output}};
2323                my $line;
2324
2325                if (scalar @ref_output != scalar @test_output) {
2326                        $opt_side_by_side = 0;
2327                        last;
2328                }
2329
2330                for ($line = 0; $line < @ref_output; $line++) {
2331                        my ($ref_func, $ref_label, @ref_dummy) = @{$ref_output[$line]};
2332                        my ($test_func, $test_label, @test_dummy) = @{$test_output[$line]};
2333
2334                        if ($ref_func != $test_func || $ref_label ne $test_label) {
2335                                $opt_side_by_side = 0;
2336                                last;
2337                        }
2338                }
2339        }
2340
2341        if (!$opt_side_by_side) {
2342                printc("Side-by-side output only possible if all DIMMS are similar\n");
2343
2344                # Discard "Decoding EEPROM" entry from all outputs
2345                for $current (0 .. $#dimm) {
2346                        shift(@{$dimm[$current]->{output}});
2347                }
2348        }
2349}
2350
2351# Check if all dimms have the same value for a given line
2352sub line_has_same_values($)
2353{
2354        my $line = shift;
2355        my $value = $dimm[0]->{output}->[$line]->[2];
2356
2357        # Skip lines with no values (headers)
2358        return 1 unless defined $value;
2359
2360        for my $other (1 .. $#dimm) {
2361                return 0 unless $value eq $dimm[$other]->{output}->[$line]->[2];
2362        }
2363
2364        return 1;
2365}
2366
2367# Find out the longest value string to adjust the column width
2368sub find_col_width($)
2369{
2370        my $width = shift;
2371
2372        return $width unless $opt_side_by_side && !$opt_html;
2373
2374        my $line;
2375        my $line_nr = @{$dimm[0]->{output}};
2376
2377        for ($line = 0; $line < $line_nr; $line++) {
2378                next if $opt_merge && line_has_same_values($line);
2379
2380                my @strings;
2381
2382                for my $current (0 .. $#dimm) {
2383                        my $value = $dimm[$current]->{output}->[$line]->[2];
2384                        push @strings, split("\n", $value) if defined $value;
2385                }
2386
2387                foreach my $line2 (@strings) {
2388                        my $len = length($line2);
2389                        $width = $len if $len > $width;
2390                }
2391        }
2392
2393        return $width;
2394}
2395
2396$sbs_col_width = find_col_width(15);
2397
2398# Print the decoded information for all DIMMs
2399for $current (0 .. $#dimm) {
2400        if ($opt_side_by_side) {
2401                print "\n\n";
2402        } else {
2403                printl2("\n\nDecoding EEPROM", $dimm[$current]->{file},
2404                        "text-decoration: underline; font-weight: bold;");
2405        }
2406        print "<table border=\"1\">\n" if $opt_html;
2407
2408        my @output = @{$dimm[$current]->{output}};
2409        for (my $line = 0; $line < @output; $line++) {
2410                my ($func, @param) = @{$output[$line]};
2411
2412                if ($opt_side_by_side) {
2413                        foreach ($current+1 .. $#dimm) {
2414                                my @xoutput = @{$dimm[$_]->{output}};
2415                                if (@{$xoutput[$line]} == 3) {
2416                                        # Line with data, stack all values
2417                                        push @param, @{$xoutput[$line]}[2];
2418                                } else {
2419                                        # Separator, make it span
2420                                        push @param, scalar @dimm;
2421                                }
2422                        }
2423                }
2424
2425                $func->(@param);
2426        }
2427
2428        print "</table>\n" if $opt_html;
2429        last if $opt_side_by_side;
2430}
2431printl2("\n\nNumber of SDRAM DIMMs detected and decoded", scalar @dimm);
2432
2433print "</body></html>\n" if ($opt_html && !$opt_bodyonly);
Note: See TracBrowser for help on using the browser.