~eloaders/i-nex/I-Nex

« back to all changes in this revision

Viewing changes to JSON/i-nex-dimms

  • Committer: eloaders
  • Date: 2016-11-09 10:03:01 UTC
  • Revision ID: git-v1:b33ce88eec560e3b3fe591b70a98856d7907799f
Clean Code

Remove libprocps3-dev | libprocps4-dev | libprocps-dev | libprocps0-dev
| libproc-dev, from depends. it is no longer necessary

Remove manpages.

Show diffs side-by-side

added added

removed removed

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