From f638182842e21c0dcca08103e695216b35633908 Mon Sep 17 00:00:00 2001 From: eloaders Date: Fri, 22 Aug 2014 15:26:17 +0200 Subject: [PATCH] Add Experimental decode-dimms as i-nex-dimms --- I-Nex/i-nex/.project | 2 +- I-Nex/i-nex/.src/Finfosys.class | 48 +- I-Nex/i-nex/.src/Finfosys.form | 91 +- I-Nex/i-nex/.src/MDecode_dimms.module | 78 + I-Nex/i-nex/.src/MStart.module | 1 + I-Nex/i-nex/.src/i2c.module | 2 + JSON/Makefile | 7 +- JSON/i-nex-dimms | 2055 +++++++++++++++++++++++++ Makefile | 8 +- i-nex.mk | 4 +- i2c_smbus.rules | 1 - 11 files changed, 2232 insertions(+), 65 deletions(-) create mode 100644 I-Nex/i-nex/.src/MDecode_dimms.module create mode 100755 JSON/i-nex-dimms diff --git a/I-Nex/i-nex/.project b/I-Nex/i-nex/.project index 1b251dd..cce5f85 100644 --- a/I-Nex/i-nex/.project +++ b/I-Nex/i-nex/.project @@ -1,5 +1,5 @@ # Gambas Project File 3.0 -# Compiled with Gambas 3.5.3 +# Compiled with Gambas 3.5.4 Title=I-Nex Startup=MStart Icon=logo/i-nex.0.4.x.png diff --git a/I-Nex/i-nex/.src/Finfosys.class b/I-Nex/i-nex/.src/Finfosys.class index 66488af..6b961cb 100644 --- a/I-Nex/i-nex/.src/Finfosys.class +++ b/I-Nex/i-nex/.src/Finfosys.class @@ -47,7 +47,7 @@ Public Sub Form_Open() Dim sLine As String Dim MTRR As New String[] Logs("Starting log", Logger.Info) - Button4.Visible = False + ComboBox10.Visible = False Object.Call(MCPUSAGE, "GetUSAGE") ''Okno na środku '' @@ -185,21 +185,21 @@ Public Sub Form_Open() Try Close #data - If Exist("/proc/mtrr", True) Then - For i = 1 To MaxCount - Label[i] = New Label(ScrollView2) - With Label[i] - .X = 2 - .Y = Int(i * 20) - .Width = 440 - .Height = 21 - .Border = Border.Raised - .Padding = 3 - .Font.Size = 8 - .Text = MTRR[i - 1] - End With - Next - Endif + ' If Exist("/proc/mtrr", True) Then + ' For i = 1 To MaxCount + ' Label[i] = New Label(ScrollView2) + ' With Label[i] + ' .X = 2 + ' .Y = Int(i * 20) + ' .Width = 440 + ' .Height = 21 + ' .Border = Border.Raised + ' .Padding = 3 + ' .Font.Size = 8 + ' .Text = MTRR[i - 1] + ' End With + ' Next + ' Endif 'Procesor Logs("Check available procesors", Logger.Info) @@ -1102,9 +1102,9 @@ TabPanel1_Click() HBox19.Visible = False Button8.Visible = False Button14.Visible = False - Button4.Visible = True + ComboBox10.Visible = True Else - Button4.Visible = False + ComboBox10.Visible = False Endif If TabStrip1.Current.Text Like "*Kernel*" Then Button6.Visible = True @@ -1233,3 +1233,15 @@ Public Sub Button20_Click() Disk_Drives._init_2() End + +Public Sub ComboBox10_Click() + +Try TextBox31.Text = Replace(MDecode_dimms.MODULE_MANUFACTURER[CFloat(ComboBox10.Index)], "\n", "") +Try TextBox32.Text = Replace(MDecode_dimms.DRAM_MANUFACTURER[CFloat(ComboBox10.Index)], "\n", "") +Try TextBox33.Text = Replace(MDecode_dimms.MODULE_TYPE[CFloat(ComboBox10.Index)], "\n", "") +Try TextBox34.Text = Replace(MDecode_dimms.PART_NUMBER[CFloat(ComboBox10.Index)], "\n", "") +Try TextBox35.Text = Replace(MDecode_dimms.RAM_SIZE[CFloat(ComboBox10.Index)], "\n", "") +Try TextBox36.Text = Replace(MDecode_dimms.tCL_tRCD_tRDP_tRAS[CFloat(ComboBox10.Index)], "\n", "") +Try TextBox37.Text = Replace(MDecode_dimms.SDRAM_DEVICE_TYPE[CFloat(ComboBox10.Index)], "\n", "") +Try TextBox38.Text = Replace(MDecode_dimms.MAX_MODULE_SPEED[CFloat(ComboBox10.Index)], "\n", "") +End diff --git a/I-Nex/i-nex/.src/Finfosys.form b/I-Nex/i-nex/.src/Finfosys.form index 0f3b73a..f4308da 100644 --- a/I-Nex/i-nex/.src/Finfosys.form +++ b/I-Nex/i-nex/.src/Finfosys.form @@ -1762,67 +1762,63 @@ Text = "Memory" Picture = Picture["AppIcons/Finfosys/utilities-system-monitor.png"] { ProgressBar1 ProgressBar - Move(0,21,469,21) + Move(119,21,350,21) } { ProgressBar4 ProgressBar - Move(0,63,469,21) + Move(119,63,350,21) } { TextLabel24 TextLabel - Move(0,42,469,21) + Move(119,42,350,21) Font = Font["Bold,8"] Padding = 1 Text = ("N/A") Alignment = Align.Center Border = Border.Raised } - { Separator5 Separator - Move(0,196,469,21) - #Translate = False - } { Label176 Label - Move(0,175,343,21) + Move(154,133,315,21) Font = Font["Bold,8"] Padding = 3 Text = ("Label176") Border = Border.Raised } { Label85 Label - Move(0,91,189,21) + Move(0,91,154,21) Font = Font["Bold,8"] Expand = True Padding = 3 Border = Border.Raised } { Label86 Label - Move(0,112,189,21) + Move(308,91,161,21) Font = Font["Bold,8"] Expand = True Padding = 3 Border = Border.Raised } { Label87 Label - Move(0,133,189,21) + Move(154,112,154,21) Font = Font["Bold,8"] Expand = True Padding = 3 Border = Border.Raised } { Label88 Label - Move(0,154,189,21) + Move(308,112,161,21) Font = Font["Bold,8"] Expand = True Padding = 3 Border = Border.Raised } { Label89 Label - Move(189,91,154,21) + Move(154,91,154,21) Font = Font["Bold,8"] Expand = True Padding = 3 Border = Border.Raised } { Label90 Label - Move(189,112,154,21) + Move(0,112,154,21) Font = Font["Bold,8"] Expand = True Padding = 3 @@ -1830,45 +1826,65 @@ Border = Border.Raised } { Label91 Label - Move(189,154,154,21) + Move(0,133,154,21) Font = Font["Bold,8"] Expand = True Padding = 3 Border = Border.Raised } { Label223 Label - Move(0,0,469,21) + Move(119,0,350,21) Font = Font["Bold,8"] Padding = 3 Text = ("Label223") Alignment = Align.Center Border = Border.Raised } - { ScrollView2 ScrollView - Move(0,217,469,259) - Font = Font["8"] - } - { Label227 Label - Move(154,196,168,21) - #Translate = False - Font = Font["Bold,8"] - Text = "MTRR" - Alignment = Align.Center - Transparent = True - } { Label74 Label - Move(350,119,112,77) + Move(7,21,105,70) Font = Font["Bold,20"] Foreground = &HFF7F00& Alignment = Align.Center } { Label77 Label - Move(343,91,119,21) + Move(0,0,119,21) Font = Font["Bold,8"] Text = ("Processes ↓") Alignment = Align.Center Border = Border.Raised } + { TextBox31 TextBox + Move(0,154,231,28) + Text = ("TextBox31") + } + { TextBox32 TextBox + Move(231,154,238,28) + Text = ("TextBox32") + } + { TextBox33 TextBox + Move(0,182,231,28) + Text = ("TextBox33") + } + { TextBox34 TextBox + Move(231,182,238,28) + Text = ("TextBox34") + } + { TextBox35 TextBox + Move(0,210,231,28) + Text = ("TextBox35") + } + { TextBox36 TextBox + Move(231,210,238,28) + Text = ("TextBox36") + } + { TextBox37 TextBox + Move(0,238,231,28) + Text = ("TextBox37") + } + { TextBox38 TextBox + Move(231,238,238,28) + Text = ("TextBox38") + } Index = 8 Text = "Network" Picture = Picture["AppIcons/Finfosys/network-wired.png"] @@ -2674,25 +2690,22 @@ Alignment = Align.Center Border = Border.Plain } - { Button4 Button - Move(287,511,126,28) - #Translate = False - Visible = False - Font = Font["Bold,8"] - Text = "Decode DIMMS" - Picture = Picture["AppIcons/gtk-yes.png"] - } { Button6 Button Move(315,511,98,28) Font = Font["8,Bold"] Text = ("Show modules") } { MenuButton1 MenuButton - Move(210,511,112,28) + Move(238,511,112,28) Font = Font["Bold,8"] Foreground = &H0000FF& Text = ("Validate") Picture = Picture["AppIcons/gnome-network-properties.png"] Menu = "Menu2" } + { ComboBox10 ComboBox + Move(315,511,98,28) + ReadOnly = True + Text = ("ComboBox10") + } } diff --git a/I-Nex/i-nex/.src/MDecode_dimms.module b/I-Nex/i-nex/.src/MDecode_dimms.module new file mode 100644 index 0000000..7557c12 --- /dev/null +++ b/I-Nex/i-nex/.src/MDecode_dimms.module @@ -0,0 +1,78 @@ +' Gambas module file + +Public NOSDRAMDIMMSDAD As New String[] +Public MODULE_TYPE As New String[] +Public MODULE_MANUFACTURER As New String[] +Public DRAM_MANUFACTURER As New String[] +Public ASM_SERIALNUMBER As New String[] +Public PART_NUMBER As New String[] +Public RAM_SIZE As New String[] +Public MAX_MODULE_SPEED As New String[] +Public FUN_MEM_TYPE As New String[] +Public tCL_tRCD_tRDP_tRAS As New String[] +Public SDRAM_DEVICE_TYPE As New String[] +'DRAM Manufacturer +Public Sub _inits() + Dim DECODE_DIMMS As Stream + Dim sLine As String + Dim sIcon As String + Dim iPos As Integer + Dim Character As Integer + Dim sKey As String + Dim sVal As String + DECODE_DIMMS = Exec ["i-nex-dimms"] For Read + For Each sLine In DECODE_DIMMS.Lines + sLine = Trim(sLine) + If Not sLine Then Continue + + iPos = InStr(sLine, ":") + If iPos = 0 Then Continue + + sKey = Trim(Left$(sLine, iPos - 1)) + sVal = Trim(Mid$(sLine, iPos + 1)) + + Select Case sKey + Case Like "*Module Manufacturer*" + MODULE_MANUFACTURER.Add(sVal) + Case Like "*DRAM Manufacturer*" + DRAM_MANUFACTURER.Add(sVal) + Case Like "*Assembly Serial Number*" + ASM_SERIALNUMBER.Add(sVal) + Case Like "*Part Number*" + PART_NUMBER.Add(sVal) + Case Like "*Module Type*" + MODULE_TYPE.Add(sVal) + Case Like "*Decoding EEPROM*" + NOSDRAMDIMMSDAD.Add(Right(sVal, 6)) + Case Like "*Size*" + RAM_SIZE.Add(sVal) + Case Like "*Maximum module speed*" + MAX_MODULE_SPEED.Add(sVal) + Case Like "*Fundamental Memory type*" + FUN_MEM_TYPE.Add(sVal) + Case Like "*Module Type*" + MODULE_TYPE.Add(sVal) + Case Like "*tCL-tRCD-tRP-tRAS*" + tCL_tRCD_tRDP_tRAS.Add(sVal) + Case Like "*SDRAM Device Type*" + SDRAM_DEVICE_TYPE.Add(sVal) + Case Like "**" + + Case Like "**" + + Case Like "**" + + Case Like "**" + + Case Like "**" + + Case Like "**" + + End Select + 'Print sVal + Next + Finfosys.ComboBox10.List = NOSDRAMDIMMSDAD + Finfosys.ComboBox10.Index = 0 + + +End diff --git a/I-Nex/i-nex/.src/MStart.module b/I-Nex/i-nex/.src/MStart.module index b76b50e..d5ed53c 100644 --- a/I-Nex/i-nex/.src/MStart.module +++ b/I-Nex/i-nex/.src/MStart.module @@ -17,6 +17,7 @@ Public CPUINFO_FILE As String Public Sub Main() Dim PrintErr As Boolean = True + MDecode_dimms._inits() Select Case Application.Args[1] Case "--help" Print ("\n " & Application.name & " " & Application.version & " running on:\n" diff --git a/I-Nex/i-nex/.src/i2c.module b/I-Nex/i-nex/.src/i2c.module index 88dfea5..ee5e855 100644 --- a/I-Nex/i-nex/.src/i2c.module +++ b/I-Nex/i-nex/.src/i2c.module @@ -37,4 +37,6 @@ Public Sub _inits() Break Endif Next + + End diff --git a/JSON/Makefile b/JSON/Makefile index fd2e69e..f4ecdec 100644 --- a/JSON/Makefile +++ b/JSON/Makefile @@ -65,7 +65,8 @@ install-bin: install-bin-i-nex-edid \ install-bin-i-nex-usage \ install-bin-i-nex-cpusage \ install-bin-i-nex-glinfo \ - install-bin-i-nex-uname + install-bin-i-nex-uname \ + install-bin-i-nex-dimms @@ -87,7 +88,9 @@ install-bin-i-nex-glinfo: install-bin-i-nex-uname: test -d $(DESTDIR)$(bindir) || mkdir $(DESTDIR)$(bindir) $(INSTALL) 0755 i-nex-uname $(DESTDIR)$(bindir) - +install-bin-i-nex-dimms: + test -d $(DESTDIR)$(bindir) || mkdir $(DESTDIR)$(bindir) + $(INSTALL) 0755 i-nex-dimms $(DESTDIR)$(bindir) clean: @echo -e '$(ACTION_COLOR)Clean JSON$(NO_COLOR)' $(RM_COM) $(RMFILE_OPT) i-nex-edid diff --git a/JSON/i-nex-dimms b/JSON/i-nex-dimms new file mode 100755 index 0000000..47a2166 --- /dev/null +++ b/JSON/i-nex-dimms @@ -0,0 +1,2055 @@ +#!/usr/bin/perl -w +# +# EEPROM data decoder for SDRAM DIMM modules +# +# Copyright 1998, 1999 Philip Edelbrock +# modified by Christian Zuckschwerdt +# modified by Burkart Lingner +# Copyright (C) 2005-2013 Jean Delvare +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA. +# +# +# The eeprom driver must be loaded (unless option -x is used). For kernels +# older than 2.6.0, the eeprom driver can be found in the lm-sensors package. +# +# References: +# PC SDRAM Serial Presence +# Detect (SPD) Specification, Intel, +# 1997,1999, Rev 1.2B +# +# Jedec Standards 4.1.x & 4.5.x +# http://www.jedec.org +# + +require 5.004; + +use strict; +use POSIX qw(ceil); +use Fcntl qw(:DEFAULT :seek); +use File::Basename; +use vars qw($opt_html $opt_bodyonly $opt_side_by_side $opt_merge + $opt_igncheck $use_sysfs $use_hexdump $sbs_col_width + @vendors %decode_callback $revision @dimm $current %hexdump_cache); + +use constant LITTLEENDIAN => "little-endian"; +use constant BIGENDIAN => "big-endian"; + +$revision = '$Revision: 6231 $ ($Date: 2014-02-20 10:54:34 +0100 (jeu. 20 févr. 2014) $)'; +$revision =~ s/\$\w+: (.*?) \$/$1/g; +$revision =~ s/ \([^()]*\)//; + +@vendors = ( +["AMD", "AMI", "Fairchild", "Fujitsu", + "GTE", "Harris", "Hitachi", "Inmos", + "Intel", "I.T.T.", "Intersil", "Monolithic Memories", + "Mostek", "Freescale (former Motorola)", "National", "NEC", + "RCA", "Raytheon", "Conexant (Rockwell)", "Seeq", + "NXP (former Signetics, Philips Semi.)", "Synertek", "Texas Instruments", "Toshiba", + "Xicor", "Zilog", "Eurotechnique", "Mitsubishi", + "Lucent (AT&T)", "Exel", "Atmel", "SGS/Thomson", + "Lattice Semi.", "NCR", "Wafer Scale Integration", "IBM", + "Tristar", "Visic", "Intl. CMOS Technology", "SSSI", + "MicrochipTechnology", "Ricoh Ltd.", "VLSI", "Micron Technology", + "SK Hynix (former Hyundai Electronics)", "OKI Semiconductor", "ACTEL", "Sharp", + "Catalyst", "Panasonic", "IDT", "Cypress", + "DEC", "LSI Logic", "Zarlink (former Plessey)", "UTMC", + "Thinking Machine", "Thomson CSF", "Integrated CMOS (Vertex)", "Honeywell", + "Tektronix", "Oracle Corporation (former Sun Microsystems)", "Silicon Storage Technology", "ProMos/Mosel Vitelic", + "Infineon (former Siemens)", "Macronix", "Xerox", "Plus Logic", + "SunDisk", "Elan Circuit Tech.", "European Silicon Str.", "Apple Computer", + "Xilinx", "Compaq", "Protocol Engines", "SCI", + "Seiko Instruments", "Samsung", "I3 Design System", "Klic", + "Crosspoint Solutions", "Alliance Semiconductor", "Tandem", "Hewlett-Packard", + "Integrated Silicon Solutions", "Brooktree", "New Media", "MHS Electronic", + "Performance Semi.", "Winbond Electronic", "Kawasaki Steel", "Bright Micro", + "TECMAR", "Exar", "PCMCIA", "LG Semi (former Goldstar)", + "Northern Telecom", "Sanyo", "Array Microsystems", "Crystal Semiconductor", + "Analog Devices", "PMC-Sierra", "Asparix", "Convex Computer", + "Quality Semiconductor", "Nimbus Technology", "Transwitch", "Micronas (ITT Intermetall)", + "Cannon", "Altera", "NEXCOM", "QUALCOMM", + "Sony", "Cray Research", "AMS(Austria Micro)", "Vitesse", + "Aster Electronics", "Bay Networks (Synoptic)", "Zentrum or ZMD", "TRW", + "Thesys", "Solbourne Computer", "Allied-Signal", "Dialog", + "Media Vision", "Numonyx Corporation (former Level One Communication)"], +["Cirrus Logic", "National Instruments", "ILC Data Device", "Alcatel Mietec", + "Micro Linear", "Univ. of NC", "JTAG Technologies", "BAE Systems", + "Nchip", "Galileo Tech", "Bestlink Systems", "Graychip", + "GENNUM", "VideoLogic", "Robert Bosch", "Chip Express", + "DATARAM", "United Microelec Corp.", "TCSI", "Smart Modular", + "Hughes Aircraft", "Lanstar Semiconductor", "Qlogic", "Kingston", + "Music Semi", "Ericsson Components", "SpaSE", "Eon Silicon Devices", + "Programmable Micro Corp", "DoD", "Integ. Memories Tech.", "Corollary Inc.", + "Dallas Semiconductor", "Omnivision", "EIV(Switzerland)", "Novatel Wireless", + "Zarlink (former Mitel)", "Clearpoint", "Cabletron", "STEC (former Silicon Technology)", + "Vanguard", "Hagiwara Sys-Com", "Vantis", "Celestica", + "Century", "Hal Computers", "Rohm Company Ltd.", "Juniper Networks", + "Libit Signal Processing", "Mushkin Enhanced Memory", "Tundra Semiconductor", "Adaptec Inc.", + "LightSpeed Semi.", "ZSP Corp.", "AMIC Technology", "Adobe Systems", + "Dynachip", "PNY Electronics", "Newport Digital", "MMC Networks", + "T Square", "Seiko Epson", "Broadcom", "Viking Components", + "V3 Semiconductor", "Flextronics (former Orbit)", "Suwa Electronics", "Transmeta", + "Micron CMS", "American Computer & Digital Components Inc", "Enhance 3000 Inc", "Tower Semiconductor", + "CPU Design", "Price Point", "Maxim Integrated Product", "Tellabs", + "Centaur Technology", "Unigen Corporation", "Transcend Information", "Memory Card Technology", + "CKD Corporation Ltd.", "Capital Instruments, Inc.", "Aica Kogyo, Ltd.", "Linvex Technology", + "MSC Vertriebs GmbH", "AKM Company, Ltd.", "Dynamem, Inc.", "NERA ASA", + "GSI Technology", "Dane-Elec (C Memory)", "Acorn Computers", "Lara Technology", + "Oak Technology, Inc.", "Itec Memory", "Tanisys Technology", "Truevision", + "Wintec Industries", "Super PC Memory", "MGV Memory", "Galvantech", + "Gadzoox Nteworks", "Multi Dimensional Cons.", "GateField", "Integrated Memory System", + "Triscend", "XaQti", "Goldenram", "Clear Logic", + "Cimaron Communications", "Nippon Steel Semi. Corp.", "Advantage Memory", "AMCC", + "LeCroy", "Yamaha Corporation", "Digital Microwave", "NetLogic Microsystems", + "MIMOS Semiconductor", "Advanced Fibre", "BF Goodrich Data.", "Epigram", + "Acbel Polytech Inc.", "Apacer Technology", "Admor Memory", "FOXCONN", + "Quadratics Superconductor", "3COM"], +["Camintonn Corporation", "ISOA Incorporated", "Agate Semiconductor", "ADMtek Incorporated", + "HYPERTEC", "Adhoc Technologies", "MOSAID Technologies", "Ardent Technologies", + "Switchcore", "Cisco Systems, Inc.", "Allayer Technologies", "WorkX AG (Wichman)", + "Oasis Semiconductor", "Novanet Semiconductor", "E-M Solutions", "Power General", + "Advanced Hardware Arch.", "Inova Semiconductors GmbH", "Telocity", "Delkin Devices", + "Symagery Microsystems", "C-Port Corporation", "SiberCore Technologies", "Southland Microsystems", + "Malleable Technologies", "Kendin Communications", "Great Technology Microcomputer", "Sanmina Corporation", + "HADCO Corporation", "Corsair", "Actrans System Inc.", "ALPHA Technologies", + "Silicon Laboratories, Inc. (Cygnal)", "Artesyn Technologies", "Align Manufacturing", "Peregrine Semiconductor", + "Chameleon Systems", "Aplus Flash Technology", "MIPS Technologies", "Chrysalis ITS", + "ADTEC Corporation", "Kentron Technologies", "Win Technologies", "Tachyon Semiconductor (former ASIC Designs Inc.)", + "Extreme Packet Devices", "RF Micro Devices", "Siemens AG", "Sarnoff Corporation", + "Itautec SA (former Itautec Philco SA)", "Radiata Inc.", "Benchmark Elect. (AVEX)", "Legend", + "SpecTek Incorporated", "Hi/fn", "Enikia Incorporated", "SwitchOn Networks", + "AANetcom Incorporated", "Micro Memory Bank", "ESS Technology", "Virata Corporation", + "Excess Bandwidth", "West Bay Semiconductor", "DSP Group", "Newport Communications", + "Chip2Chip Incorporated", "Phobos Corporation", "Intellitech Corporation", "Nordic VLSI ASA", + "Ishoni Networks", "Silicon Spice", "Alchemy Semiconductor", "Agilent Technologies", + "Centillium Communications", "W.L. Gore", "HanBit Electronics", "GlobeSpan", + "Element 14", "Pycon", "Saifun Semiconductors", "Sibyte, Incorporated", + "MetaLink Technologies", "Feiya Technology", "I & C Technology", "Shikatronics", + "Elektrobit", "Megic", "Com-Tier", "Malaysia Micro Solutions", + "Hyperchip", "Gemstone Communications", "Anadigm (former Anadyne)", "3ParData", + "Mellanox Technologies", "Tenx Technologies", "Helix AG", "Domosys", + "Skyup Technology", "HiNT Corporation", "Chiaro", "MDT Technologies GmbH (former MCI Computer GMBH)", + "Exbit Technology A/S", "Integrated Technology Express", "AVED Memory", "Legerity", + "Jasmine Networks", "Caspian Networks", "nCUBE", "Silicon Access Networks", + "FDK Corporation", "High Bandwidth Access", "MultiLink Technology", "BRECIS", + "World Wide Packets", "APW", "Chicory Systems", "Xstream Logic", + "Fast-Chip", "Zucotto Wireless", "Realchip", "Galaxy Power", + "eSilicon", "Morphics Technology", "Accelerant Networks", "Silicon Wave", + "SandCraft", "Elpida"], +["Solectron", "Optosys Technologies", "Buffalo (former Melco)", "TriMedia Technologies", + "Cyan Technologies", "Global Locate", "Optillion", "Terago Communications", + "Ikanos Communications", "Princeton Technology", "Nanya Technology", "Elite Flash Storage", + "Mysticom", "LightSand Communications", "ATI Technologies", "Agere Systems", + "NeoMagic", "AuroraNetics", "Golden Empire", "Mushkin", + "Tioga Technologies", "Netlist", "TeraLogic", "Cicada Semiconductor", + "Centon Electronics", "Tyco Electronics", "Magis Works", "Zettacom", + "Cogency Semiconductor", "Chipcon AS", "Aspex Technology", "F5 Networks", + "Programmable Silicon Solutions", "ChipWrights", "Acorn Networks", "Quicklogic", + "Kingmax Semiconductor", "BOPS", "Flasys", "BitBlitz Communications", + "eMemory Technology", "Procket Networks", "Purple Ray", "Trebia Networks", + "Delta Electronics", "Onex Communications", "Ample Communications", "Memory Experts Intl", + "Astute Networks", "Azanda Network Devices", "Dibcom", "Tekmos", + "API NetWorks", "Bay Microsystems", "Firecron Ltd", "Resonext Communications", + "Tachys Technologies", "Equator Technology", "Concept Computer", "SILCOM", + "3Dlabs", "c't Magazine", "Sanera Systems", "Silicon Packets", + "Viasystems Group", "Simtek", "Semicon Devices Singapore", "Satron Handelsges", + "Improv Systems", "INDUSYS GmbH", "Corrent", "Infrant Technologies", + "Ritek Corp", "empowerTel Networks", "Hypertec", "Cavium Networks", + "PLX Technology", "Massana Design", "Intrinsity", "Valence Semiconductor", + "Terawave Communications", "IceFyre Semiconductor", "Primarion", "Picochip Designs Ltd", + "Silverback Systems", "Jade Star Technologies", "Pijnenburg Securealink", + "takeMS - Ultron AG (former Memorysolution GmbH)", "Cambridge Silicon Radio", + "Swissbit", "Nazomi Communications", "eWave System", + "Rockwell Collins", "Picocel Co., Ltd.", "Alphamosaic Ltd", "Sandburst", + "SiCon Video", "NanoAmp Solutions", "Ericsson Technology", "PrairieComm", + "Mitac International", "Layer N Networks", "MtekVision", "Allegro Networks", + "Marvell Semiconductors", "Netergy Microelectronic", "NVIDIA", "Internet Machines", + "Peak Electronics", "Litchfield Communication", "Accton Technology", "Teradiant Networks", + "Scaleo Chip (former Europe Technologies)", "Cortina Systems", "RAM Components", "Raqia Networks", + "ClearSpeed", "Matsushita Battery", "Xelerated", "SimpleTech", + "Utron Technology", "Astec International", "AVM gmbH", "Redux Communications", + "Dot Hill Systems", "TeraChip"], +["T-RAM Incorporated", "Innovics Wireless", "Teknovus", "KeyEye Communications", + "Runcom Technologies", "RedSwitch", "Dotcast", "Silicon Mountain Memory", + "Signia Technologies", "Pixim", "Galazar Networks", "White Electronic Designs", + "Patriot Scientific", "Neoaxiom Corporation", "3Y Power Technology", "Scaleo Chip (former Europe Technologies)", + "Potentia Power Systems", "C-guys Incorporated", "Digital Communications Technology Incorporated", "Silicon-Based Technology", + "Fulcrum Microsystems", "Positivo Informatica Ltd", "XIOtech Corporation", "PortalPlayer", + "Zhiying Software", "Parker Vision, Inc. (former Direct2Data)", "Phonex Broadband", "Skyworks Solutions", + "Entropic Communications", "Pacific Force Technology", "Zensys A/S", "Legend Silicon Corp.", + "sci-worx GmbH", "SMSC (former Oasis Silicon Systems)", "Renesas Electronics (former Renesas Technology)", "Raza Microelectronics", + "Phyworks", "MediaTek", "Non-cents Productions", "US Modular", + "Wintegra Ltd", "Mathstar", "StarCore", "Oplus Technologies", + "Mindspeed", "Just Young Computer", "Radia Communications", "OCZ", + "Emuzed", "LOGIC Devices", "Inphi Corporation", "Quake Technologies", + "Vixel", "SolusTek", "Kongsberg Maritime", "Faraday Technology", + "Altium Ltd.", "Insyte", "ARM Ltd.", "DigiVision", + "Vativ Technologies", "Endicott Interconnect Technologies", "Pericom", "Bandspeed", + "LeWiz Communications", "CPU Technology", "Ramaxel Technology", "DSP Group", + "Axis Communications", "Legacy Electronics", "Chrontel", "Powerchip Semiconductor", + "MobilEye Technologies", "Excel Semiconductor", "A-DATA Technology", "VirtualDigm", + "G Skill Intl", "Quanta Computer", "Yield Microelectronics", "Afa Technologies", + "KINGBOX Technology Co. Ltd.", "Ceva", "iStor Networks", "Advance Modules", + "Microsoft", "Open-Silicon", "Goal Semiconductor", "ARC International", + "Simmtec", "Metanoia", "Key Stream", "Lowrance Electronics", + "Adimos", "SiGe Semiconductor", "Fodus Communications", "Credence Systems Corp.", + "Genesis Microchip Inc.", "Vihana, Inc.", "WIS Technologies", "GateChange Technologies", + "High Density Devices AS", "Synopsys", "Gigaram", "Enigma Semiconductor Inc.", + "Century Micro Inc.", "Icera Semiconductor", "Mediaworks Integrated Systems", "O'Neil Product Development", + "Supreme Top Technology Ltd.", "MicroDisplay Corporation", "Team Group Inc.", "Sinett Corporation", + "Toshiba Corporation", "Tensilica", "SiRF Technology", "Bacoc Inc.", + "SMaL Camera Technologies", "Thomson SC", "Airgo Networks", "Wisair Ltd.", + "SigmaTel", "Arkados", "Compete IT gmbH Co. KG", "Eudar Technology Inc.", + "Focus Enhancements", "Xyratex"], +["Specular Networks", "Patriot Memory", "U-Chip Technology Corp.", "Silicon Optix", + "Greenfield Networks", "CompuRAM GmbH", "Stargen, Inc.", "NetCell Corporation", + "Excalibrus Technologies Ltd", "SCM Microsystems", "Xsigo Systems, Inc.", "CHIPS & Systems Inc", + "Tier 1 Multichip Solutions", "CWRL Labs", "Teradici", "Gigaram, Inc.", + "g2 Microsystems", "PowerFlash Semiconductor", "P.A. Semi, Inc.", "NovaTech Solutions, S.A.", + "c2 Microsystems, Inc.", "Level5 Networks", "COS Memory AG", "Innovasic Semiconductor", + "02IC Co. Ltd", "Tabula, Inc.", "Crucial Technology", "Chelsio Communications", + "Solarflare Communications", "Xambala Inc.", "EADS Astrium", "Terra Semiconductor Inc. (former ATO Semicon Co. Ltd.)", + "Imaging Works, Inc.", "Astute Networks, Inc.", "Tzero", "Emulex", + "Power-One", "Pulse~LINK Inc.", "Hon Hai Precision Industry", "White Rock Networks Inc.", + "Telegent Systems USA, Inc.", "Atrua Technologies, Inc.", "Acbel Polytech Inc.", + "eRide Inc.","ULi Electronics Inc.", "Magnum Semiconductor Inc.", "neoOne Technology, Inc.", + "Connex Technology, Inc.", "Stream Processors, Inc.", "Focus Enhancements", "Telecis Wireless, Inc.", + "uNav Microelectronics", "Tarari, Inc.", "Ambric, Inc.", "Newport Media, Inc.", "VMTS", + "Enuclia Semiconductor, Inc.", "Virtium Technology Inc.", "Solid State System Co., Ltd.", "Kian Tech LLC", + "Artimi", "Power Quotient International", "Avago Technologies", "ADTechnology", "Sigma Designs", + "SiCortex, Inc.", "Ventura Technology Group", "eASIC", "M.H.S. SAS", "Micro Star International", + "Rapport Inc.", "Makway International", "Broad Reach Engineering Co.", + "Semiconductor Mfg Intl Corp", "SiConnect", "FCI USA Inc.", "Validity Sensors", + "Coney Technology Co. Ltd.", "Spans Logic", "Neterion Inc.", "Qimonda", + "New Japan Radio Co. Ltd.", "Velogix", "Montalvo Systems", "iVivity Inc.", "Walton Chaintech", + "AENEON", "Lorom Industrial Co. Ltd.", "Radiospire Networks", "Sensio Technologies, Inc.", + "Nethra Imaging", "Hexon Technology Pte Ltd", "CompuStocx (CSX)", "Methode Electronics, Inc.", + "Connect One Ltd.", "Opulan Technologies", "Septentrio NV", "Goldenmars Technology Inc.", + "Kreton Corporation", "Cochlear Ltd.", "Altair Semiconductor", "NetEffect, Inc.", + "Spansion, Inc.", "Taiwan Semiconductor Mfg", "Emphany Systems Inc.", + "ApaceWave Technologies", "Mobilygen Corporation", "Tego", "Cswitch Corporation", + "Haier (Beijing) IC Design Co.", "MetaRAM", "Axel Electronics Co. Ltd.", "Tilera Corporation", + "Aquantia", "Vivace Semiconductor", "Redpine Signals", "Octalica", "InterDigital Communications", + "Avant Technology", "Asrock, Inc.", "Availink", "Quartics, Inc.", "Element CXI", + "Innovaciones Microelectronicas", "VeriSilicon Microelectronics", "W5 Networks"], +["MOVEKING", "Mavrix Technology, Inc.", "CellGuide Ltd.", "Faraday Technology", + "Diablo Technologies, Inc.", "Jennic", "Octasic", "Molex Incorporated", "3Leaf Networks", + "Bright Micron Technology", "Netxen", "NextWave Broadband Inc.", "DisplayLink", "ZMOS Technology", + "Tec-Hill", "Multigig, Inc.", "Amimon", "Euphonic Technologies, Inc.", "BRN Phoenix", + "InSilica", "Ember Corporation", "Avexir Technologies Corporation", "Echelon Corporation", + "Edgewater Computer Systems", "XMOS Semiconductor Ltd.", "GENUSION, Inc.", "Memory Corp NV", + "SiliconBlue Technologies", "Rambus Inc.", "Andes Technology Corporation", "Coronis Systems", + "Achronix Semiconductor", "Siano Mobile Silicon Ltd.", "Semtech Corporation", "Pixelworks Inc.", + "Gaisler Research AB", "Teranetics", "Toppan Printing Co. Ltd.", "Kingxcon", + "Silicon Integrated Systems", "I-O Data Device, Inc.", "NDS Americas Inc.", "Solomon Systech Limited", + "On Demand Microelectronics", "Amicus Wireless Inc.", "SMARDTV SNC", "Comsys Communication Ltd.", + "Movidia Ltd.", "Javad GNSS, Inc.", "Montage Technology Group", "Trident Microsystems", "Super Talent", + "Optichron, Inc.", "Future Waves UK Ltd.", "SiBEAM, Inc.", "Inicore, Inc.", "Virident Systems", + "M2000, Inc.", "ZeroG Wireless, Inc.", "Gingle Technology Co. Ltd.", "Space Micro Inc.", "Wilocity", + "Novafora, Inc.", "iKoa Corporation", "ASint Technology", "Ramtron", "Plato Networks Inc.", + "IPtronics AS", "Infinite-Memories", "Parade Technologies Inc.", "Dune Networks", + "GigaDevice Semiconductor", "Modu Ltd.", "CEITEC", "Northrop Grumman", "XRONET Corporation", + "Sicon Semiconductor AB", "Atla Electronics Co. Ltd.", "TOPRAM Technology", "Silego Technology Inc.", + "Kinglife", "Ability Industries Ltd.", "Silicon Power Computer & Communications", + "Augusta Technology, Inc.", "Nantronics Semiconductors", "Hilscher Gesellschaft", "Quixant Ltd.", + "Percello Ltd.", "NextIO Inc.", "Scanimetrics Inc.", "FS-Semi Company Ltd.", "Infinera Corporation", + "SandForce Inc.", "Lexar Media", "Teradyne Inc.", "Memory Exchange Corp.", "Suzhou Smartek Electronics", + "Avantium Corporation", "ATP Electronics Inc.", "Valens Semiconductor Ltd", "Agate Logic, Inc.", + "Netronome", "Zenverge, Inc.", "N-trig Ltd", "SanMax Technologies Inc.", "Contour Semiconductor Inc.", + "TwinMOS", "Silicon Systems, Inc.", "V-Color Technology Inc.", "Certicom Corporation", "JSC ICC Milandr", + "PhotoFast Global Inc.", "InnoDisk Corporation", "Muscle Power", "Energy Micro", "Innofidei", + "CopperGate Communications", "Holtek Semiconductor Inc.", "Myson Century, Inc.", "FIDELIX", + "Red Digital Cinema", "Densbits Technology", "Zempro", "MoSys", "Provigent", "Triad Semiconductor, Inc."], +["Siklu Communication Ltd.", "A Force Manufacturing Ltd.", "Strontium", "Abilis Systems", "Siglead, Inc.", + "Ubicom, Inc.", "Unifosa Corporation", "Stretch, Inc.", "Lantiq Deutschland GmbH", "Visipro", + "EKMemory", "Microelectronics Institute ZTE", "Cognovo Ltd.", "Carry Technology Co. Ltd.", "Nokia", + "King Tiger Technology", "Sierra Wireless", "HT Micron", "Albatron Technology Co. Ltd.", + "Leica Geosystems AG", "BroadLight", "AEXEA", "ClariPhy Communications, Inc.", "Green Plug", + "Design Art Networks", "Mach Xtreme Technology Ltd.", "ATO Solutions Co. Ltd.", "Ramsta", + "Greenliant Systems, Ltd.", "Teikon", "Antec Hadron", "NavCom Technology, Inc.", + "Shanghai Fudan Microelectronics", "Calxeda, Inc.", "JSC EDC Electronics", "Kandit Technology Co. Ltd.", + "Ramos Technology", "Goldenmars Technology", "XeL Technology Inc.", "Newzone Corporation", + "ShenZhen MercyPower Tech", "Nanjing Yihuo Technology", "Nethra Imaging Inc.", "SiTel Semiconductor BV", + "SolidGear Corporation", "Topower Computer Ind Co Ltd.", "Wilocity", "Profichip GmbH", + "Gerad Technologies", "Ritek Corporation", "Gomos Technology Limited", "Memoright Corporation", + "D-Broad, Inc.", "HiSilicon Technologies", "Syndiant Inc.", "Enverv Inc.", "Cognex", + "Xinnova Technology Inc.", "Ultron AG", "Concord Idea Corporation", "AIM Corporation", + "Lifetime Memory Products", "Ramsway", "Recore Systems BV", "Haotian Jinshibo Science Tech", + "Being Advanced Memory", "Adesto Technologies", "Giantec Semiconductor, Inc.", "HMD Electronics AG", + "Gloway International (HK)", "Kingcore", "Anucell Technology Holding", + "Accord Software & Systems Pvt. Ltd.", "Active-Semi Inc.", "Denso Corporation", "TLSI Inc.", + "Shenzhen Daling Electronic Co. Ltd.", "Mustang", "Orca Systems", "Passif Semiconductor", + "GigaDevice Semiconductor (Beijing) Inc.", "Memphis Electronic", "Beckhoff Automation GmbH", + "Harmony Semiconductor Corp (former ProPlus Design Solutions)", "Air Computers SRL", "TMT Memory", + "Eorex Corporation", "Xingtera", "Netsol", "Bestdon Technology Co. Ltd.", "Baysand Inc.", + "Uroad Technology Co. Ltd. (former Triple Grow Industrial Ltd.)", "Wilk Elektronik S.A.", + "AAI", "Harman", "Berg Microelectronics Inc.", "ASSIA, Inc.", "Visiontek Products LLC", + "OCMEMORY", "Welink Solution Inc.", "Shark Gaming", "Avalanche Technology", + "R&D Center ELVEES OJSC", "KingboMars Technology Co. Ltd.", + "High Bridge Solutions Industria Eletronica", "Transcend Technology Co. Ltd.", + "Everspin Technologies", "Hon-Hai Precision", "Smart Storage Systems", "Toumaz Group", + "Zentel Electronics Corporation", "Panram International Corporation", + "Silicon Space Technology"] +); + +$use_sysfs = -d '/sys/bus'; + +# We consider that no data was written to this area of the SPD EEPROM if +# all bytes read 0x00 or all bytes read 0xff +sub spd_written(@) +{ + my $all_00 = 1; + my $all_ff = 1; + + foreach my $b (@_) { + $all_00 = 0 unless $b == 0x00; + $all_ff = 0 unless $b == 0xff; + return 1 unless $all_00 or $all_ff; + } + + return 0; +} + +sub parity($) +{ + my $n = shift; + my $parity = 0; + + while ($n) { + $parity++ if ($n & 1); + $n >>= 1; + } + + return ($parity & 1); +} + +# New encoding format (as of DDR3) for manufacturer just has a count of +# leading 0x7F rather than all the individual bytes. The count bytes includes +# parity! +sub manufacturer_ddr3($$) +{ + my ($count, $code) = @_; + return "Invalid" if parity($count) != 1; + return "Invalid" if parity($code) != 1; + return (($code & 0x7F) - 1 > $vendors[$count & 0x7F]) ? "Unknown" : + $vendors[$count & 0x7F][($code & 0x7F) - 1]; +} + +sub manufacturer(@) +{ + my @bytes = @_; + my $ai = 0; + my $first; + + return ("Undefined", []) unless spd_written(@bytes); + + while (defined($first = shift(@bytes)) && $first == 0x7F) { + $ai++; + } + + return ("Invalid", []) unless defined $first; + return ("Invalid", [$first, @bytes]) if parity($first) != 1; + if (parity($ai) == 0) { + $ai |= 0x80; + } + return (manufacturer_ddr3($ai, $first), \@bytes); +} + +sub manufacturer_data(@) +{ + my $hex = ""; + my $asc = ""; + + return unless spd_written(@_); + + foreach my $byte (@_) { + $hex .= sprintf("\%02X ", $byte); + $asc .= ($byte >= 32 && $byte < 127) ? chr($byte) : '?'; + } + + return "$hex(\"$asc\")"; +} + +sub part_number(@) +{ + my $asc = ""; + my $byte; + + while (defined ($byte = shift) && $byte >= 32 && $byte < 127) { + $asc .= chr($byte); + } + + return ($asc eq "") ? "Undefined" : $asc; +} + +sub cas_latencies(@) +{ + return "None" unless @_; + return join ', ', map("${_}T", sort { $b <=> $a } @_); +} + +# Real printing functions + +sub html_encode($) +{ + my $text = shift; + $text =~ s//\>/sg; + $text =~ s/ degrees C/\°C/sg; + $text =~ s/\n/
\n/sg; + return $text; +} + +sub same_values(@) +{ + my $value = shift; + while (@_) { + return 0 unless $value eq shift; + } + return 1; +} + +sub real_printl($$) # print a line w/ label and values +{ + my ($label, @values) = @_; + local $_; + my $same_values = same_values(@values); + + # If all values are N/A, don't bother printing + return if $values[0] eq "N/A" and $same_values; + + if ($opt_html) { + $label = html_encode($label); + @values = map { html_encode($_) } @values; + print "$label"; + if ($opt_merge && $same_values) { + print "$values[0]"; + } else { + print "$_" foreach @values; + } + print "\n"; + } else { + if ($opt_merge && $same_values) { + splice(@values, 1); + } + + my $format = "%-47s".((" %-".$sbs_col_width."s") x (scalar @values - 1))." %s\n"; + my $maxl = 0; # Keep track of the max number of lines + + # It's a bit tricky because each value may span over more than + # one line. We can easily extract the values per column, but + # we need them per line at printing time. So we have to + # prepare a 2D array with all the individual string fragments. + my ($col, @lines); + for ($col = 0; $col < @values; $col++) { + my @cells = split /\n/, $values[$col]; + $maxl = @cells if @cells > $maxl; + for (my $l = 0; $l < @cells; $l++) { + $lines[$l]->[$col] = $cells[$l]; + } + } + + # Also make sure there are no holes in the array + for (my $l = 0; $l < $maxl; $l++) { + for ($col = 0; $col < @values; $col++) { + $lines[$l]->[$col] = "" + if not defined $lines[$l]->[$col]; + } + } + + printf $format, $label, @{shift @lines}; + printf $format, "", @{$_} foreach (@lines); + } +} + +sub printl2($$) # print a line w/ label and value (outside a table) +{ + my ($label, $value) = @_; + if ($opt_html) { + $label = html_encode($label); + $value = html_encode($value); + } + print "$label: $value\n"; +} + +sub real_prints($) # print separator w/ given text +{ + my ($label, $ncol) = @_; + $ncol = 1 unless $ncol; + if ($opt_html) { + $label = html_encode($label); + print "$label\n"; + } else { + print "\n---=== $label ===---\n"; + } +} + +sub printh($$) # print header w/ given text +{ + my ($header, $sub) = @_; + if ($opt_html) { + $header = html_encode($header); + $sub = html_encode($sub); + print "

$header

\n"; + print "

$sub

\n"; + } else { + print "\n$header\n$sub\n"; + } +} + +sub printc($) # print comment +{ + my ($comment) = @_; + if ($opt_html) { + $comment = html_encode($comment); + print "\n"; + } else { + print "# $comment\n"; + } +} + +# Fake printing functions +# These don't actually print anything, instead they store the desired +# output for later processing. + +sub printl($$) # print a line w/ label and value +{ + my @output = (\&real_printl, @_); + push @{$dimm[$current]->{output}}, \@output; +} + +sub printl_cond($$$) # same as printl but conditional +{ + my ($cond, $label, $value) = @_; + return unless $cond || $opt_side_by_side; + printl($label, $cond ? $value : "N/A"); +} + +sub prints($) # print separator w/ given text +{ + my @output = (\&real_prints, @_); + push @{$dimm[$current]->{output}}, \@output; +} + +# Helper functions + +sub tns($) # print a time in ns +{ + return sprintf("%3.2f ns", $_[0]); +} + +sub tns3($) # print a time in ns, with 3 decimal digits +{ + return sprintf("%.3f ns", $_[0]); +} + +sub value_or_undefined +{ + my ($value, $unit) = @_; + return "Undefined!" unless $value; + $value .= " $unit" if defined $unit; + return $value; +} + +# Common to SDR, DDR and DDR2 SDRAM +sub sdram_voltage_interface_level($) +{ + my @levels = ( + "TTL (5V tolerant)", # 0 + "LVTTL (not 5V tolerant)", # 1 + "HSTL 1.5V", # 2 + "SSTL 3.3V", # 3 + "SSTL 2.5V", # 4 + "SSTL 1.8V", # 5 + ); + + return ($_[0] < @levels) ? $levels[$_[0]] : "Undefined!"; +} + +# Common to SDR, DDR and DDR2 SDRAM +sub sdram_module_configuration_type($) +{ + my $byte = $_[0] & 0x07; + my @edc; + + return "No Parity" if $byte == 0; + + # Data ECC includes Data Parity so don't print both + push @edc, "Data Parity" if ($byte & 0x03) == 0x01; + push @edc, "Data ECC" if ($byte & 0x02); + # New in DDR2 specification + push @edc, "Address/Command Parity" if ($byte & 0x04); + + return join ", ", @edc; +} + +# Parameter: EEPROM bytes 0-127 (using 3-62) +sub decode_sdr_sdram($) +{ + my $bytes = shift; + my $temp; + +# SPD revision + # Starting with SPD revision 1.2, this byte is encoded in BCD + printl("SPD Revision", $bytes->[62] < 0x12 ? $bytes->[62] : + ($bytes->[62] >> 4) . "." . ($bytes->[62] & 0xf)); + +#size computation + + prints("Memory Characteristics"); + + my $k = 0; + my $ii = 0; + + $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17; + if (($bytes->[5] <= 8) && ($bytes->[17] <= 8)) { + $k = $bytes->[5] * $bytes->[17]; + } + + if ($ii > 0 && $ii <= 12 && $k > 0) { + printl("Size", ((1 << $ii) * $k) . " MB"); + } else { + printl("Size", "INVALID: " . $bytes->[3] . "," . $bytes->[4] . "," . + $bytes->[5] . "," . $bytes->[17]); + } + + my @cas; + for ($ii = 0; $ii < 7; $ii++) { + push(@cas, $ii + 1) if ($bytes->[18] & (1 << $ii)); + } + + my $trcd; + my $trp; + my $tras; + my $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1; + + $trcd = $bytes->[29]; + $trp = $bytes->[27]; + $tras = $bytes->[30]; + + printl("tCL-tRCD-tRP-tRAS", + $cas[$#cas] . "-" . + ceil($trcd/$ctime) . "-" . + ceil($trp/$ctime) . "-" . + ceil($tras/$ctime)); + + if ($bytes->[3] == 0) { $temp = "Undefined!"; } + elsif ($bytes->[3] == 1) { $temp = "1/16"; } + elsif ($bytes->[3] == 2) { $temp = "2/17"; } + elsif ($bytes->[3] == 3) { $temp = "3/18"; } + else { $temp = $bytes->[3]; } + printl("Number of Row Address Bits", $temp); + + if ($bytes->[4] == 0) { $temp = "Undefined!"; } + elsif ($bytes->[4] == 1) { $temp = "1/16"; } + elsif ($bytes->[4] == 2) { $temp = "2/17"; } + elsif ($bytes->[4] == 3) { $temp = "3/18"; } + else { $temp = $bytes->[4]; } + printl("Number of Col Address Bits", $temp); + + printl("Number of Module Rows", value_or_undefined($bytes->[5])); + + if ($bytes->[7] > 1) { $temp = "Undefined!"; } + else { $temp = ($bytes->[7] * 256) + $bytes->[6]; } + printl("Data Width", $temp); + + printl("Voltage Interface Level", + sdram_voltage_interface_level($bytes->[8])); + + printl("Module Configuration Type", + sdram_module_configuration_type($bytes->[11])); + + printl("Refresh Rate", ddr2_refresh_rate($bytes->[12])); + + if ($bytes->[13] & 0x80) { $temp = "Bank2 = 2 x Bank1"; } + else { $temp = "No Bank2 OR Bank2 = Bank1 width"; } + printl("Primary SDRAM Component Bank Config", $temp); + printl("Primary SDRAM Component Widths", + value_or_undefined($bytes->[13] & 0x7f)); + + if ($bytes->[14] & 0x80) { $temp = "Bank2 = 2 x Bank1"; } + else { $temp = "No Bank2 OR Bank2 = Bank1 width"; } + printl("Error Checking SDRAM Component Bank Config", $temp); + printl("Error Checking SDRAM Component Widths", + value_or_undefined($bytes->[14] & 0x7f)); + + printl("Min Clock Delay for Back to Back Random Access", + value_or_undefined($bytes->[15])); + + my @array; + for ($ii = 0; $ii < 4; $ii++) { + push(@array, 1 << $ii) if ($bytes->[16] & (1 << $ii)); + } + push(@array, "Page") if ($bytes->[16] & 128); + if (@array) { $temp = join ', ', @array; } + else { $temp = "None"; } + printl("Supported Burst Lengths", $temp); + + printl("Number of Device Banks", + value_or_undefined($bytes->[17])); + + printl("Supported CAS Latencies", cas_latencies(@cas)); + + @array = (); + for ($ii = 0; $ii < 7; $ii++) { + push(@array, $ii) if ($bytes->[19] & (1 << $ii)); + } + if (@array) { $temp = join ', ', @array; } + else { $temp = "None"; } + printl("Supported CS Latencies", $temp); + + @array = (); + for ($ii = 0; $ii < 7; $ii++) { + push(@array, $ii) if ($bytes->[20] & (1 << $ii)); + } + if (@array) { $temp = join ', ', @array; } + else { $temp = "None"; } + printl("Supported WE Latencies", $temp); + + my ($cycle_time, $access_time); + + if (@cas >= 1) { + $cycle_time = "$ctime ns at CAS ".$cas[$#cas]; + + $temp = ($bytes->[10] >> 4) + ($bytes->[10] & 0xf) * 0.1; + $access_time = "$temp ns at CAS ".$cas[$#cas]; + } + + if (@cas >= 2 && spd_written(@$bytes[23..24])) { + $temp = $bytes->[23] >> 4; + if ($temp == 0) { $temp = "Undefined!"; } + else { + $temp += 15 if $temp < 4; + $temp += ($bytes->[23] & 0xf) * 0.1; + } + $cycle_time .= "\n$temp ns at CAS ".$cas[$#cas-1]; + + $temp = $bytes->[24] >> 4; + if ($temp == 0) { $temp = "Undefined!"; } + else { + $temp += 15 if $temp < 4; + $temp += ($bytes->[24] & 0xf) * 0.1; + } + $access_time .= "\n$temp ns at CAS ".$cas[$#cas-1]; + } + + if (@cas >= 3 && spd_written(@$bytes[25..26])) { + $temp = $bytes->[25] >> 2; + if ($temp == 0) { $temp = "Undefined!"; } + else { + $temp += ($bytes->[25] & 0x3) * 0.25; + } + $cycle_time .= "\n$temp ns at CAS ".$cas[$#cas-2]; + + $temp = $bytes->[26] >> 2; + if ($temp == 0) { $temp = "Undefined!"; } + else { + $temp += ($bytes->[26] & 0x3) * 0.25; + } + $access_time .= "\n$temp ns at CAS ".$cas[$#cas-2]; + } + + printl_cond(defined $cycle_time, "Cycle Time", $cycle_time); + printl_cond(defined $access_time, "Access Time", $access_time); + + $temp = ""; + if ($bytes->[21] & 1) { $temp .= "Buffered Address/Control Inputs\n"; } + if ($bytes->[21] & 2) { $temp .= "Registered Address/Control Inputs\n"; } + if ($bytes->[21] & 4) { $temp .= "On card PLL (clock)\n"; } + if ($bytes->[21] & 8) { $temp .= "Buffered DQMB Inputs\n"; } + if ($bytes->[21] & 16) { $temp .= "Registered DQMB Inputs\n"; } + if ($bytes->[21] & 32) { $temp .= "Differential Clock Input\n"; } + if ($bytes->[21] & 64) { $temp .= "Redundant Row Address\n"; } + if ($bytes->[21] & 128) { $temp .= "Undefined (bit 7)\n"; } + if ($bytes->[21] == 0) { $temp .= "(None Reported)\n"; } + printl("SDRAM Module Attributes", $temp); + + $temp = ""; + if ($bytes->[22] & 1) { $temp .= "Supports Early RAS# Recharge\n"; } + if ($bytes->[22] & 2) { $temp .= "Supports Auto-Precharge\n"; } + if ($bytes->[22] & 4) { $temp .= "Supports Precharge All\n"; } + if ($bytes->[22] & 8) { $temp .= "Supports Write1/Read Burst\n"; } + if ($bytes->[22] & 16) { $temp .= "Lower VCC Tolerance: 5%\n"; } + else { $temp .= "Lower VCC Tolerance: 10%\n"; } + if ($bytes->[22] & 32) { $temp .= "Upper VCC Tolerance: 5%\n"; } + else { $temp .= "Upper VCC Tolerance: 10%\n"; } + if ($bytes->[22] & 64) { $temp .= "Undefined (bit 6)\n"; } + if ($bytes->[22] & 128) { $temp .= "Undefined (bit 7)\n"; } + printl("SDRAM Device Attributes (General)", $temp); + + printl("Minimum Row Precharge Time", + value_or_undefined($bytes->[27], "ns")); + + printl("Row Active to Row Active Min", + value_or_undefined($bytes->[28], "ns")); + + printl("RAS to CAS Delay", + value_or_undefined($bytes->[29], "ns")); + + printl("Min RAS Pulse Width", + value_or_undefined($bytes->[30], "ns")); + + $temp = ""; + if ($bytes->[31] & 1) { $temp .= "4 MByte\n"; } + if ($bytes->[31] & 2) { $temp .= "8 MByte\n"; } + if ($bytes->[31] & 4) { $temp .= "16 MByte\n"; } + if ($bytes->[31] & 8) { $temp .= "32 MByte\n"; } + if ($bytes->[31] & 16) { $temp .= "64 MByte\n"; } + if ($bytes->[31] & 32) { $temp .= "128 MByte\n"; } + if ($bytes->[31] & 64) { $temp .= "256 MByte\n"; } + if ($bytes->[31] & 128) { $temp .= "512 MByte\n"; } + if ($bytes->[31] == 0) { $temp .= "(Undefined! -- None Reported!)\n"; } + printl("Row Densities", $temp); + + $temp = (($bytes->[32] & 0x7f) >> 4) + ($bytes->[32] & 0xf) * 0.1; + printl_cond(($bytes->[32] & 0xf) <= 9, + "Command and Address Signal Setup Time", + (($bytes->[32] >> 7) ? -$temp : $temp) . " ns"); + + $temp = (($bytes->[33] & 0x7f) >> 4) + ($bytes->[33] & 0xf) * 0.1; + printl_cond(($bytes->[33] & 0xf) <= 9, + "Command and Address Signal Hold Time", + (($bytes->[33] >> 7) ? -$temp : $temp) . " ns"); + + $temp = (($bytes->[34] & 0x7f) >> 4) + ($bytes->[34] & 0xf) * 0.1; + printl_cond(($bytes->[34] & 0xf) <= 9, "Data Signal Setup Time", + (($bytes->[34] >> 7) ? -$temp : $temp) . " ns"); + + $temp = (($bytes->[35] & 0x7f) >> 4) + ($bytes->[35] & 0xf) * 0.1; + printl_cond(($bytes->[35] & 0xf) <= 9, "Data Signal Hold Time", + (($bytes->[35] >> 7) ? -$temp : $temp) . " ns"); +} + +# Parameter: EEPROM bytes 0-127 (using 3-62) +sub decode_ddr_sdram($) +{ + my $bytes = shift; + my $temp; + +# SPD revision + printl_cond($bytes->[62] != 0xff, "SPD Revision", + ($bytes->[62] >> 4) . "." . ($bytes->[62] & 0xf)); + +# speed + prints("Memory Characteristics"); + + $temp = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1; + my $ddrclk = 2 * (1000 / $temp); + my $tbits = ($bytes->[7] * 256) + $bytes->[6]; + if (($bytes->[11] == 2) || ($bytes->[11] == 1)) { $tbits = $tbits - 8; } + my $pcclk = int ($ddrclk * $tbits / 8); + $pcclk += 100 if ($pcclk % 100) >= 50; # Round properly + $pcclk = $pcclk - ($pcclk % 100); + $ddrclk = int ($ddrclk); + printl("Maximum module speed", "$ddrclk MHz (PC${pcclk})"); + +#size computation + my $k = 0; + my $ii = 0; + + $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17; + if (($bytes->[5] <= 8) && ($bytes->[17] <= 8)) { + $k = $bytes->[5] * $bytes->[17]; + } + + if ($ii > 0 && $ii <= 12 && $k > 0) { + printl("Size", ((1 << $ii) * $k) . " MB"); + } else { + printl("Size", "INVALID: " . $bytes->[3] . ", " . $bytes->[4] . ", " . + $bytes->[5] . ", " . $bytes->[17]); + } + + printl("Voltage Interface Level", + sdram_voltage_interface_level($bytes->[8])); + + printl("Module Configuration Type", + sdram_module_configuration_type($bytes->[11])); + + printl("Refresh Rate", ddr2_refresh_rate($bytes->[12])); + + my $highestCAS = 0; + my %cas; + for ($ii = 0; $ii < 7; $ii++) { + if ($bytes->[18] & (1 << $ii)) { + $highestCAS = 1+$ii*0.5; + $cas{$highestCAS}++; + } + } + + my $trcd; + my $trp; + my $tras; + my $ctime = ($bytes->[9] >> 4) + ($bytes->[9] & 0xf) * 0.1; + + $trcd = ($bytes->[29] >> 2) + (($bytes->[29] & 3) * 0.25); + $trp = ($bytes->[27] >> 2) + (($bytes->[27] & 3) * 0.25); + $tras = $bytes->[30]; + + printl("tCL-tRCD-tRP-tRAS", + $highestCAS . "-" . + ceil($trcd/$ctime) . "-" . + ceil($trp/$ctime) . "-" . + ceil($tras/$ctime)); + +# latencies + printl("Supported CAS Latencies", cas_latencies(keys %cas)); + + my @array; + for ($ii = 0; $ii < 7; $ii++) { + push(@array, $ii) if ($bytes->[19] & (1 << $ii)); + } + if (@array) { $temp = join ', ', @array; } + else { $temp = "None"; } + printl("Supported CS Latencies", $temp); + + @array = (); + for ($ii = 0; $ii < 7; $ii++) { + push(@array, $ii) if ($bytes->[20] & (1 << $ii)); + } + if (@array) { $temp = join ', ', @array; } + else { $temp = "None"; } + printl("Supported WE Latencies", $temp); + +# timings + my ($cycle_time, $access_time); + + if (exists $cas{$highestCAS}) { + $cycle_time = "$ctime ns at CAS $highestCAS"; + $access_time = (($bytes->[10] >> 4) * 0.1 + ($bytes->[10] & 0xf) * 0.01) + . " ns at CAS $highestCAS"; + } + + if (exists $cas{$highestCAS-0.5} && spd_written(@$bytes[23..24])) { + $cycle_time .= "\n".(($bytes->[23] >> 4) + ($bytes->[23] & 0xf) * 0.1) + . " ns at CAS ".($highestCAS-0.5); + $access_time .= "\n".(($bytes->[24] >> 4) * 0.1 + ($bytes->[24] & 0xf) * 0.01) + . " ns at CAS ".($highestCAS-0.5); + } + + if (exists $cas{$highestCAS-1} && spd_written(@$bytes[25..26])) { + $cycle_time .= "\n".(($bytes->[25] >> 4) + ($bytes->[25] & 0xf) * 0.1) + . " ns at CAS ".($highestCAS-1); + $access_time .= "\n".(($bytes->[26] >> 4) * 0.1 + ($bytes->[26] & 0xf) * 0.01) + . " ns at CAS ".($highestCAS-1); + } + + printl_cond(defined $cycle_time, "Minimum Cycle Time", $cycle_time); + printl_cond(defined $access_time, "Maximum Access Time", $access_time); + +# module attributes + if ($bytes->[47] & 0x03) { + if (($bytes->[47] & 0x03) == 0x01) { $temp = "1.125\" to 1.25\""; } + elsif (($bytes->[47] & 0x03) == 0x02) { $temp = "1.7\""; } + elsif (($bytes->[47] & 0x03) == 0x03) { $temp = "Other"; } + printl("Module Height", $temp); + } +} + +sub ddr2_sdram_ctime($) +{ + my $byte = shift; + my $ctime; + + $ctime = $byte >> 4; + if (($byte & 0xf) <= 9) { $ctime += ($byte & 0xf) * 0.1; } + elsif (($byte & 0xf) == 10) { $ctime += 0.25; } + elsif (($byte & 0xf) == 11) { $ctime += 0.33; } + elsif (($byte & 0xf) == 12) { $ctime += 0.66; } + elsif (($byte & 0xf) == 13) { $ctime += 0.75; } + + return $ctime; +} + +sub ddr2_sdram_atime($) +{ + my $byte = shift; + my $atime; + + $atime = ($byte >> 4) * 0.1 + ($byte & 0xf) * 0.01; + + return $atime; +} + +# Base, high-bit, 3-bit fraction code +sub ddr2_sdram_rtime($$$) +{ + my ($rtime, $msb, $ext) = @_; + my @table = (0, .25, .33, .50, .66, .75); + + return $rtime + $msb * 256 + $table[$ext]; +} + +sub ddr2_module_types($) +{ + my $byte = shift; + my @types = qw(RDIMM UDIMM SO-DIMM Micro-DIMM Mini-RDIMM Mini-UDIMM); + my @widths = (133.35, 133.25, 67.6, 45.5, 82.0, 82.0); + my @suptypes; + local $_; + + foreach (0..5) { + push @suptypes, "$types[$_] ($widths[$_] mm)" + if ($byte & (1 << $_)); + } + + return @suptypes; +} + +# Common to SDR, DDR and DDR2 SDRAM +sub ddr2_refresh_rate($) +{ + my $byte = shift; + my @refresh = qw(Normal Reduced Reduced Extended Extended Extended); + my @refresht = (15.625, 3.9, 7.8, 31.3, 62.5, 125); + + return "$refresh[$byte & 0x7f] ($refresht[$byte & 0x7f] us)". + ($byte & 0x80 ? " - Self Refresh" : ""); +} + +# Parameter: EEPROM bytes 0-127 (using 3-62) +sub decode_ddr2_sdram($) +{ + my $bytes = shift; + my $temp; + my $ctime; + +# SPD revision + if ($bytes->[62] != 0xff) { + printl("SPD Revision", ($bytes->[62] >> 4) . "." . + ($bytes->[62] & 0xf)); + } + +# speed + prints("Memory Characteristics"); + + $ctime = ddr2_sdram_ctime($bytes->[9]); + my $ddrclk = 2 * (1000 / $ctime); + my $tbits = ($bytes->[7] * 256) + $bytes->[6]; + if ($bytes->[11] & 0x03) { $tbits = $tbits - 8; } + my $pcclk = int ($ddrclk * $tbits / 8); + # Round down to comply with Jedec + $pcclk = $pcclk - ($pcclk % 100); + $ddrclk = int ($ddrclk); + printl("Maximum module speed", "$ddrclk MHz (PC2-${pcclk})"); + +#size computation + my $k = 0; + my $ii = 0; + + $ii = ($bytes->[3] & 0x0f) + ($bytes->[4] & 0x0f) - 17; + $k = (($bytes->[5] & 0x7) + 1) * $bytes->[17]; + + if($ii > 0 && $ii <= 12 && $k > 0) { + printl("Size", ((1 << $ii) * $k) . " MB"); + } else { + printl("Size", "INVALID: " . $bytes->[3] . "," . $bytes->[4] . "," . + $bytes->[5] . "," . $bytes->[17]); + } + + printl("Banks x Rows x Columns x Bits:", + join(' x ', $bytes->[17], $bytes->[3], $bytes->[4], $bytes->[6])); + printl("Ranks", ($bytes->[5] & 7) + 1); + + printl("SDRAM Device Width", $bytes->[13]." bits"); + + my @heights = ('< 25.4', '25.4', '25.4 - 30.0', '30.0', '30.5', '> 30.5'); + printl("Module Height", $heights[$bytes->[5] >> 5]." mm"); + + my @suptypes = ddr2_module_types($bytes->[20]); + printl("Module Type".(@suptypes > 1 ? 's' : ''), join(', ', @suptypes)); + + printl("DRAM Package", $bytes->[5] & 0x10 ? "Stack" : "Planar"); + + printl("Voltage Interface Level", + sdram_voltage_interface_level($bytes->[8])); + + printl("Module Configuration Type", + sdram_module_configuration_type($bytes->[11])); + + printl("Refresh Rate", ddr2_refresh_rate($bytes->[12])); + + my @burst; + push @burst, 4 if ($bytes->[16] & 4); + push @burst, 8 if ($bytes->[16] & 8); + $burst[0] = 'None' if !@burst; + printl("Supported Burst Lengths", join(', ', @burst)); + + my $highestCAS = 0; + my %cas; + for ($ii = 2; $ii < 7; $ii++) { + if ($bytes->[18] & (1 << $ii)) { + $highestCAS = $ii; + $cas{$highestCAS}++; + } + } + + my $trcd; + my $trp; + my $tras; + + $trcd = ($bytes->[29] >> 2) + (($bytes->[29] & 3) * 0.25); + $trp = ($bytes->[27] >> 2) + (($bytes->[27] & 3) * 0.25); + $tras = $bytes->[30]; + + printl("tCL-tRCD-tRP-tRAS", + $highestCAS . "-" . + ceil($trcd/$ctime) . "-" . + ceil($trp/$ctime) . "-" . + ceil($tras/$ctime)); + +# latencies + printl("Supported CAS Latencies (tCL)", cas_latencies(keys %cas)); + +# timings + my ($cycle_time, $access_time); + + if (exists $cas{$highestCAS}) { + $cycle_time = tns($ctime) . " at CAS $highestCAS (tCK min)"; + $access_time = tns(ddr2_sdram_atime($bytes->[10])) + . " at CAS $highestCAS (tAC)"; + } + + if (exists $cas{$highestCAS-1} && spd_written(@$bytes[23..24])) { + $cycle_time .= "\n".tns(ddr2_sdram_ctime($bytes->[23])) + . " at CAS ".($highestCAS-1); + $access_time .= "\n".tns(ddr2_sdram_atime($bytes->[24])) + . " at CAS ".($highestCAS-1); + } + + if (exists $cas{$highestCAS-2} && spd_written(@$bytes[25..26])) { + $cycle_time .= "\n".tns(ddr2_sdram_ctime($bytes->[25])) + . " at CAS ".($highestCAS-2); + $access_time .= "\n".tns(ddr2_sdram_atime($bytes->[26])) + . " at CAS ".($highestCAS-2); + } + + printl_cond(defined $cycle_time, "Minimum Cycle Time:", $cycle_time); + printl_cond(defined $access_time, "Maximum Access Time:", $access_time); + + printl("Maximum Cycle Time (tCK max)", + tns(ddr2_sdram_ctime($bytes->[43]))); + +# more timing information + prints("Timing Parameters:"); + printl("Address/Command Setup Time Before Clock (tIS):", + tns(ddr2_sdram_atime($bytes->[32]))); + printl("Address/Command Hold Time After Clock (tIH):", + tns(ddr2_sdram_atime($bytes->[33]))); + printl("Data Input Setup Time Before Strobe (tDS):", + tns(ddr2_sdram_atime($bytes->[34]))); + printl("Data Input Hold Time After Strobe (tDH):", + tns(ddr2_sdram_atime($bytes->[35]))); + printl("Minimum Row Precharge Delay (tRP):", tns($trp)); + printl("Minimum Row Active to Row Active Delay (tRRD):", + tns($bytes->[28]/4)); + printl("Minimum RAS# to CAS# Delay (tRCD):", tns($trcd)); + printl("Minimum RAS# Pulse Width (tRAS):", tns($tras)); + printl("Write Recovery Time (tWR):", tns($bytes->[36]/4)); + printl("Minimum Write to Read CMD Delay (tWTR):", tns($bytes->[37]/4)); + printl("Minimum Read to Pre-charge CMD Delay (tRTP):", tns($bytes->[38]/4)); + printl("Minimum Active to Auto-refresh Delay (tRC):", + tns(ddr2_sdram_rtime($bytes->[41], 0, ($bytes->[40] >> 4) & 7))); + printl("Minimum Recovery Delay (tRFC):", + tns(ddr2_sdram_rtime($bytes->[42], $bytes->[40] & 1, + ($bytes->[40] >> 1) & 7))); + printl("Maximum DQS to DQ Skew (tDQSQ):", tns($bytes->[44]/100)); + printl("Maximum Read Data Hold Skew (tQHS):", tns($bytes->[45]/100)); + printl("PLL Relock Time:", $bytes->[46] . " us") if ($bytes->[46]); +} + +# Parameter: EEPROM bytes 0-127 (using 3-76) +sub decode_ddr3_sdram($) +{ + my $bytes = shift; + my $temp; + my $ctime; + + my @module_types = ("Undefined", "RDIMM", "UDIMM", "SO-DIMM", + "Micro-DIMM", "Mini-RDIMM", "Mini-UDIMM", + "Mini-CDIMM", "72b-SO-UDIMM", "72b-SO-RDIMM", + "72b-SO-CDIMM", "LRDIMM", "16b-SO-DIMM", + "32b-SO-DIMM"); + + printl("Module Type:", ($bytes->[3] <= $#module_types) ? + $module_types[$bytes->[3]] : + sprintf("Reserved (0x%.2X)", $bytes->[3])); + +# speed + prints("Memory Characteristics"); + + my $dividend = ($bytes->[9] >> 4) & 15; + my $divisor = $bytes->[9] & 15; + printl("Fine time base:", sprintf("%.3f", $dividend / $divisor) . " ps"); + + $dividend = $bytes->[10]; + $divisor = $bytes->[11]; + my $mtb = $dividend / $divisor; + printl("Medium time base:", tns3($mtb)); + + $ctime = $bytes->[12] * $mtb; + my $ddrclk = 2 * (1000 / $ctime); + my $tbits = 1 << (($bytes->[8] & 7) + 3); + my $pcclk = int ($ddrclk * $tbits / 8); + # Round down to comply with Jedec + $pcclk = $pcclk - ($pcclk % 100); + $ddrclk = int ($ddrclk); + printl("Maximum module speed:", "$ddrclk MHz (PC3-${pcclk})"); + +# Size computation + + my $cap = ($bytes->[4] & 15) + 28; + $cap += ($bytes->[8] & 7) + 3; + $cap -= ($bytes->[7] & 7) + 2; + $cap -= 20 + 3; + my $k = (($bytes->[7] >> 3) & 31) + 1; + printl("Size:", ((1 << $cap) * $k) . " MB"); + + printl("Banks x Rows x Columns x Bits:", + join(' x ', 1 << ((($bytes->[4] >> 4) & 7) + 3), + ((($bytes->[5] >> 3) & 31) + 12), + ( ($bytes->[5] & 7) + 9), + ( 1 << (($bytes->[8] & 7) + 3)) )); + printl("Ranks:", $k); + + printl("SDRAM Device Width:", (1 << (($bytes->[7] & 7) + 2))." bits"); + + printl("Bus Width Extension:", ($bytes->[8] & 24)." bits"); + + my $taa; + my $trcd; + my $trp; + my $tras; + + $taa = ceil($bytes->[16] / $bytes->[12]); + $trcd = ceil($bytes->[18] / $bytes->[12]); + $trp = ceil($bytes->[20] / $bytes->[12]); + $tras = ceil(((($bytes->[21] & 0x0f) << 8) + $bytes->[22]) / $bytes->[12]); + + printl("tCL-tRCD-tRP-tRAS:", join("-", $taa, $trcd, $trp, $tras)); + +# latencies + my $highestCAS = 0; + my %cas; + my $ii; + my $cas_sup = ($bytes->[15] << 8) + $bytes->[14]; + for ($ii = 0; $ii < 15; $ii++) { + if ($cas_sup & (1 << $ii)) { + $highestCAS = $ii + 4; + $cas{$highestCAS}++; + } + } + printl("Supported CAS Latencies (tCL):", cas_latencies(keys %cas)); + +# more timing information + prints("Timing Parameters"); + + printl("Minimum Write Recovery time (tWR):", tns3($bytes->[17] * $mtb)); + printl("Minimum Row Active to Row Active Delay (tRRD):", + tns3($bytes->[19] * $mtb)); + printl("Minimum Active to Auto-Refresh Delay (tRC):", + tns3((((($bytes->[21] >> 4) & 15) << 8) + $bytes->[23]) * $mtb)); + printl("Minimum Recovery Delay (tRFC):", + tns3((($bytes->[25] << 8) + $bytes->[24]) * $mtb)); + printl("Minimum Write to Read CMD Delay (tWTR):", + tns3($bytes->[26] * $mtb)); + printl("Minimum Read to Pre-charge CMD Delay (tRTP):", + tns3($bytes->[27] * $mtb)); + printl("Minimum Four Activate Window Delay (tFAW):", + tns3(((($bytes->[28] & 15) << 8) + $bytes->[29]) * $mtb)); + +# miscellaneous stuff + prints("Optional Features"); + + my $volts = "1.5V"; + if ($bytes->[6] & 1) { + $volts .= " tolerant"; + } + if ($bytes->[6] & 2) { + $volts .= ", 1.35V "; + } + if ($bytes->[6] & 4) { + $volts .= ", 1.2X V"; + } + printl("Operable voltages:", $volts); + printl("RZQ/6 supported?:", ($bytes->[30] & 1) ? "Yes" : "No"); + printl("RZQ/7 supported?:", ($bytes->[30] & 2) ? "Yes" : "No"); + printl("DLL-Off Mode supported?:", ($bytes->[30] & 128) ? "Yes" : "No"); + printl("Operating temperature range:", sprintf "0-%d degrees C", + ($bytes->[31] & 1) ? 95 : 85); + printl("Refresh Rate in extended temp range:", + ($bytes->[31] & 2) ? "2X" : "1X"); + printl("Auto Self-Refresh?:", ($bytes->[31] & 4) ? "Yes" : "No"); + printl("On-Die Thermal Sensor readout?:", + ($bytes->[31] & 8) ? "Yes" : "No"); + printl("Partial Array Self-Refresh?:", + ($bytes->[31] & 128) ? "Yes" : "No"); + printl("Thermal Sensor Accuracy:", + ($bytes->[32] & 128) ? sprintf($bytes->[32] & 127) : + "Not implemented"); + printl("SDRAM Device Type:", + ($bytes->[33] & 128) ? sprintf($bytes->[33] & 127) : + "Standard Monolithic"); + if ($bytes->[3] >= 1 && $bytes->[3] <= 6) { + + prints("Physical Characteristics"); + printl("Module Height (mm):", ($bytes->[60] & 31) + 15); + printl("Module Thickness (mm):", sprintf("%d front, %d back", + ($bytes->[61] & 15) + 1, + (($bytes->[61] >> 4) & 15) +1)); + printl("Module Width (mm):", ($bytes->[3] <= 2) ? 133.5 : + ($bytes->[3] == 3) ? 67.6 : "TBD"); + + my $alphabet = "ABCDEFGHJKLMNPRTUVWY"; + my $ref = $bytes->[62] & 31; + my $ref_card; + if ($ref == 31) { + $ref_card = "ZZ"; + } else { + if ($bytes->[62] & 128) { + $ref += 31; + } + if ($ref < length $alphabet) { + $ref_card = substr $alphabet, $ref, 1; + } else { + my $ref1 = int($ref / (length $alphabet)); + $ref -= (length $alphabet) * $ref1; + $ref_card = (substr $alphabet, $ref1, 1) . + (substr $alphabet, $ref, 1); + } + } + printl("Module Reference Card:", $ref_card); + } + if ($bytes->[3] == 1 || $bytes->[3] == 5) { + prints("Registered DIMM"); + + my @rows = ("Undefined", 1, 2, 4); + printl("# DRAM Rows:", $rows[($bytes->[63] >> 2) & 3]); + printl("# Registers:", $rows[$bytes->[63] & 3]); + printl("Register manufacturer:", + manufacturer_ddr3($bytes->[65], $bytes->[66])); + printl("Register device type:", + (($bytes->[68] & 7) == 0) ? "SSTE32882" : + "Undefined"); + printl("Register revision:", sprintf("0x%.2X", $bytes->[67])); + printl("Heat spreader characteristics:", + ($bytes->[64] < 128) ? "Not incorporated" : + sprintf("%.2X", ($bytes->[64] & 127))); + my $regs; + for (my $i = 0; $i < 8; $i++) { + $regs = sprintf("SSTE32882 RC%d/RC%d", + $i * 2, $i * 2 + 1); + printl($regs, sprintf("%.2X", $bytes->[$i + 69])); + } + } +} + +# Parameter: EEPROM bytes 0-127 (using 4-5) +sub decode_direct_rambus($) +{ + my $bytes = shift; + +#size computation + prints("Memory Characteristics"); + + my $ii; + + $ii = ($bytes->[4] & 0x0f) + ($bytes->[4] >> 4) + ($bytes->[5] & 0x07) - 13; + + if ($ii > 0 && $ii < 16) { + printl("Size:", (1 << $ii) . " MB"); + } else { + printl("Size:", sprintf("INVALID: 0x%02x, 0x%02x", + $bytes->[4], $bytes->[5])); + } +} + +# Parameter: EEPROM bytes 0-127 (using 3-5) +sub decode_rambus($) +{ + my $bytes = shift; + +#size computation + prints("Memory Characteristics"); + + my $ii; + + $ii = ($bytes->[3] & 0x0f) + ($bytes->[3] >> 4) + ($bytes->[5] & 0x07) - 13; + + if ($ii > 0 && $ii < 16) { + printl("Size:", (1 << $ii) . " MB"); + } else { + printl("Size:", "INVALID: " . sprintf("0x%02x, 0x%02x", + $bytes->[3], $bytes->[5])); + } +} + +%decode_callback = ( + "SDR SDRAM" => \&decode_sdr_sdram, + "DDR SDRAM" => \&decode_ddr_sdram, + "DDR2 SDRAM" => \&decode_ddr2_sdram, + "DDR3 SDRAM" => \&decode_ddr3_sdram, + "Direct Rambus" => \&decode_direct_rambus, + "Rambus" => \&decode_rambus, +); + +# Parameter: Manufacturing year/week bytes +sub manufacture_date($$) +{ + my ($year, $week) = @_; + + # In theory the year and week are in BCD format, but + # this is not always true in practice :( + if (($year & 0xf0) <= 0x90 && ($year & 0x0f) <= 0x09 + && ($week & 0xf0) <= 0x90 && ($week & 0x0f) <= 0x09) { + # Note that this heuristic will break in year 2080 + return sprintf("%d%02X-W%02X", + $year >= 0x80 ? 19 : 20, $year, $week); + # Fallback to binary format if it seems to make sense + } elsif ($year <= 99 && $week >= 1 && $week <= 53) { + return sprintf("%d%02d-W%02d", + $year >= 80 ? 19 : 20, $year, $week); + } else { + return sprintf("0x%02X%02X", $year, $week); + } +} + +sub printl_mfg_location_code($) +{ + my $code = shift; + my $letter = chr($code); + + # Try the location code as ASCII first, as earlier specifications + # suggested this. As newer specifications don't mention it anymore, + # we still fall back to binary. + printl_cond(spd_written($code), "Manufacturing Location Code:", + $letter =~ m/^[\w\d]$/ ? $letter : sprintf("0x%.2X", $code)); +} + +sub printl_mfg_assembly_serial(@) +{ + printl_cond(spd_written(@_), "Assembly Serial Number:", + sprintf("0x%02X%02X%02X%02X", @_)); +} + +# Parameter: EEPROM bytes 0-175 (using 117-149) +sub decode_ddr3_mfg_data($) +{ + my $bytes = shift; + + prints("Manufacturer Data"); + + printl("Module Manufacturer:", + manufacturer_ddr3($bytes->[117], $bytes->[118])); + + if (spd_written(@{$bytes}[148..149])) { + printl("DRAM Manufacturer:", + manufacturer_ddr3($bytes->[148], $bytes->[149])); + } + + printl_mfg_location_code($bytes->[119]); + + if (spd_written(@{$bytes}[120..121])) { + printl("Manufacturing Date:", + manufacture_date($bytes->[120], $bytes->[121])); + } + + printl_mfg_assembly_serial(@{$bytes}[122..125]); + + printl("Part Number:", part_number(@{$bytes}[128..145])); + + if (spd_written(@{$bytes}[146..147])) { + printl("Revision Code:", + sprintf("0x%02X%02X", $bytes->[146], $bytes->[147])); + } +} + +# Parameter: EEPROM bytes 0-127 (using 64-98) +sub decode_manufacturing_information($) +{ + my $bytes = shift; + my ($temp, $extra); + + prints("Manufacturing Information"); + + # $extra is a reference to an array containing up to + # 7 extra bytes from the Manufacturer field. Sometimes + # these bytes are filled with interesting data. + ($temp, $extra) = manufacturer(@{$bytes}[64..71]); + printl("Manufacturer", $temp); + $temp = manufacturer_data(@{$extra}); + printl_cond(defined $temp, "Custom Manufacturer Data", $temp); + + printl_mfg_location_code($bytes->[72]); + + printl("Part Number:", part_number(@{$bytes}[73..90])); + + printl_cond(spd_written(@{$bytes}[91..92]), "Revision Code:", + sprintf("0x%02X%02X", @{$bytes}[91..92])); + + printl_cond(spd_written(@{$bytes}[93..94]), "Manufacturing Date:", + manufacture_date($bytes->[93], $bytes->[94])); + + printl_mfg_assembly_serial(@{$bytes}[95..98]); +} + +# Parameter: EEPROM bytes 0-127 (using 126-127) +sub decode_intel_spec_freq($) +{ + my $bytes = shift; + my $temp; + + prints("Intel Specification"); + + if ($bytes->[126] == 0x66) { $temp = "66 MHz"; } + elsif ($bytes->[126] == 100) { $temp = "100 MHz or 133 MHz"; } + elsif ($bytes->[126] == 133) { $temp = "133 MHz"; } + else { $temp = "Undefined!"; } + printl("Frequency:", $temp); + + $temp = ""; + if ($bytes->[127] & 1) { $temp .= "Intel Concurrent Auto-precharge\n"; } + if ($bytes->[127] & 2) { $temp .= "CAS Latency = 2\n"; } + if ($bytes->[127] & 4) { $temp .= "CAS Latency = 3\n"; } + if ($bytes->[127] & 8) { $temp .= "Junction Temp A (100 degrees C)\n"; } + else { $temp .= "Junction Temp B (90 degrees C)\n"; } + if ($bytes->[127] & 16) { $temp .= "CLK 3 Connected\n"; } + if ($bytes->[127] & 32) { $temp .= "CLK 2 Connected\n"; } + if ($bytes->[127] & 64) { $temp .= "CLK 1 Connected\n"; } + if ($bytes->[127] & 128) { $temp .= "CLK 0 Connected\n"; } + if (($bytes->[127] & 192) == 192) { $temp .= "Double-sided DIMM\n"; } + elsif (($bytes->[127] & 192) != 0) { $temp .= "Single-sided DIMM\n"; } + printl("Details for 100 MHz Support:", $temp); +} + +# Read various hex dump style formats: hexdump, hexdump -C, i2cdump, eeprog +# note that normal 'hexdump' format on a little-endian system byte-swaps +# words, using hexdump -C is better. +sub read_hexdump($) +{ + my $addr = 0; + my $repstart = 0; + my @bytes; + my $header = 1; + my $word = 0; + + # Look in the cache first + return @{$hexdump_cache{$_[0]}} if exists $hexdump_cache{$_[0]}; + + open F, '<', $_[0] or die "Unable to open: $_[0]"; + while () { + chomp; + if (/^\*$/) { + $repstart = $addr; + next; + } + /^(?:0000 )?([a-f\d]{2,8}):?\s+((:?[a-f\d]{4}\s*){8}|(:?[a-f\d]{2}\s*){16})/i || + /^(?:0000 )?([a-f\d]{2,8}):?\s*$/i; + next if (!defined $1 && $header); # skip leading unparsed lines + + defined $1 or die "Unable to parse input"; + $header = 0; + + $addr = hex $1; + if ($repstart) { + @bytes[$repstart .. ($addr-1)] = + (@bytes[($repstart-16)..($repstart-1)]) x (($addr-$repstart)/16); + $repstart = 0; + } + last unless defined $2; + foreach (split(/\s+/, $2)) { + if (/^(..)(..)$/) { + $word |= 1; + if ($use_hexdump eq LITTLEENDIAN) { + $bytes[$addr++] = hex($2); + $bytes[$addr++] = hex($1); + } else { + $bytes[$addr++] = hex($1); + $bytes[$addr++] = hex($2); + } + } else { + $bytes[$addr++] = hex($_); + } + } + } + close F; + $header and die "Unable to parse any data from hexdump '$_[0]'"; + $word and printc("Using $use_hexdump 16-bit hex dump"); + + # Cache the data for later use + $hexdump_cache{$_[0]} = \@bytes; + return @bytes; +} + +# Returns the (total, used) number of bytes in the EEPROM, +# assuming it is a non-Rambus SPD EEPROM. +sub spd_sizes($) +{ + my $bytes = shift; + + if ($bytes->[2] >= 9) { + # For FB-DIMM and newer, decode number of bytes written + my $spd_len = ($bytes->[0] >> 4) & 7; + my $size = 64 << ($bytes->[0] & 15); + if ($spd_len == 0) { + return ($size, 128); + } elsif ($spd_len == 1) { + return ($size, 176); + } elsif ($spd_len == 2) { + return ($size, 256); + } else { + return (64, 64); + } + } else { + my $size; + if ($bytes->[1] <= 14) { + $size = 1 << $bytes->[1]; + } elsif ($bytes->[1] == 0) { + $size = "RFU"; + } else { $size = "ERROR!" } + + return ($size, ($bytes->[0] < 64) ? 64 : $bytes->[0]); + } +} + +# Read bytes from SPD-EEPROM +# Note: offset must be a multiple of 16! +sub readspd($$$) +{ + my ($offset, $size, $dimm_i) = @_; + my @bytes; + if ($use_hexdump) { + @bytes = read_hexdump($dimm_i); + return @bytes[$offset..($offset + $size - 1)]; + } elsif ($use_sysfs) { + # Kernel 2.6 with sysfs + sysopen(HANDLE, "$dimm_i/eeprom", O_RDONLY) + or die "Cannot open $dimm_i/eeprom"; + binmode HANDLE; + sysseek(HANDLE, $offset, SEEK_SET) + or die "Cannot seek $dimm_i/eeprom"; + sysread(HANDLE, my $eeprom, $size) + or die "Cannot read $dimm_i/eeprom"; + close HANDLE; + @bytes = unpack("C*", $eeprom); + } else { + # Kernel 2.4 with procfs + for my $i (0 .. ($size-1)/16) { + my $hexoff = sprintf('%02x', $offset + $i * 16); + push @bytes, split(" ", `cat $dimm_i/$hexoff`); + } + } + return @bytes; +} + +# Calculate and verify checksum of first 63 bytes +sub checksum($) +{ + my $bytes = shift; + my $dimm_checksum = 0; + local $_; + + $dimm_checksum += $bytes->[$_] foreach (0 .. 62); + $dimm_checksum &= 0xff; + + return ("EEPROM Checksum of bytes 0-62", + ($bytes->[63] == $dimm_checksum) ? 1 : 0, + sprintf('0x%02X', $bytes->[63]), + sprintf('0x%02X', $dimm_checksum)); +} + +# Calculate and verify CRC +sub check_crc($) +{ + my $bytes = shift; + my $crc = 0; + my $crc_cover = $bytes->[0] & 0x80 ? 116 : 125; + my $crc_ptr = 0; + my $crc_bit; + + while ($crc_ptr <= $crc_cover) { + $crc = $crc ^ ($bytes->[$crc_ptr] << 8); + for ($crc_bit = 0; $crc_bit < 8; $crc_bit++) { + if ($crc & 0x8000) { + $crc = ($crc << 1) ^ 0x1021; + } else { + $crc = $crc << 1 + } + } + $crc_ptr++; + } + $crc &= 0xffff; + + my $dimm_crc = ($bytes->[127] << 8) | $bytes->[126]; + return ("EEPROM CRC of bytes: 0-$crc_cover", + ($dimm_crc == $crc) ? 1 : 0, + sprintf("0x%04X", $dimm_crc), + sprintf("0x%04X", $crc)); +} + +# Parse command-line +foreach (@ARGV) { + if ($_ eq '-h' || $_ eq '--help') { + print "Usage: $0 [-c] [-f [-b]] [-x|-X file [files..]]\n", + " $0 -h\n\n", + " -f, --format Print nice html output\n", + " -b, --bodyonly Don't print html header\n", + " (useful for postprocessing the output)\n", + " --side-by-side Display all DIMMs side-by-side if possible\n", + " --merge-cells Merge neighbour cells with identical values\n", + " (side-by-side output only)\n", + " -c, --checksum Decode completely even if checksum fails\n", + " -x, Read data from hexdump files\n", + " -X, Same as -x except treat multibyte hex\n", + " data as little endian\n", + " -h, --help Display this usage summary\n"; + print <<"EOF"; + +Hexdumps can be the output from hexdump, hexdump -C, i2cdump, eeprog and +likely many other progams producing hex dumps of one kind or another. Note +that the default output of "hexdump" will be byte-swapped on little-endian +systems and you must use -X instead of -x, otherwise the dump will not be +parsed correctly. It is better to use "hexdump -C", which is not ambiguous. +EOF + exit; + } + + if ($_ eq '-f' || $_ eq '--format') { + $opt_html = 1; + next; + } + if ($_ eq '-b' || $_ eq '--bodyonly') { + $opt_bodyonly = 1; + next; + } + if ($_ eq '--side-by-side') { + $opt_side_by_side = 1; + next; + } + if ($_ eq '--merge-cells') { + $opt_merge = 1; + next; + } + if ($_ eq '-c' || $_ eq '--checksum') { + $opt_igncheck = 1; + next; + } + if ($_ eq '-x') { + $use_hexdump = BIGENDIAN; + next; + } + if ($_ eq '-X') { + $use_hexdump = LITTLEENDIAN; + next; + } + + if (m/^-/) { + print STDERR "Unrecognized option $_\n"; + exit; + } + + push @dimm, { eeprom => basename($_), file => $_ } if $use_hexdump; +} + +if ($opt_html && !$opt_bodyonly) { + print "\n", + "\n", + "\t\n", + "\tPC DIMM Serial Presence Detect Tester/Decoder Output\n", + "\n"; +} + +printc("decode-dimms version $revision"); +printh('Memory Serial Presence Detect Decoder', +'By Philip Edelbrock, Christian Zuckschwerdt, Burkart Lingner, +Jean Delvare, Trent Piepho and others'); + + +# From a sysfs device path and an attribute name, return the attribute +# value, or undef (stolen from sensors-detect) +sub sysfs_device_attribute +{ + my ($device, $attr) = @_; + my $value; + + open(local *FILE, "$device/$attr") or return ""; + $value = ; + close(FILE); + return unless defined $value; + + chomp($value); + return $value; +} + +sub get_dimm_list +{ + my (@dirs, $dir, $opened, $file, @files); + + if ($use_sysfs) { + @dirs = ('/sys/bus/i2c/drivers/eeprom', '/sys/bus/i2c/drivers/at24'); + } else { + @dirs = ('/proc/sys/dev/sensors'); + } + + foreach $dir (@dirs) { + next unless opendir(local *DIR, $dir); + $opened++; + while (defined($file = readdir(DIR))) { + if ($use_sysfs) { + # We look for I2C devices like 0-0050 or 2-0051 + next unless $file =~ /^\d+-[\da-f]+$/i; + next unless -d "$dir/$file"; + + # Device name must be eeprom (driver eeprom) + # or spd (driver at24) + my $attr = sysfs_device_attribute("$dir/$file", "name"); + next unless defined $attr && + ($attr eq "eeprom" || $attr eq "spd"); + } else { + next unless $file =~ /^eeprom-/; + } + push @files, { eeprom => "$file", + file => "$dir/$file" }; + } + close(DIR); + } + + if (!$opened) { + print STDERR "No EEPROM found, try loading the eeprom or at24 module\n"; + exit; + } + + return sort { $a->{file} cmp $b->{file} } @files; +} + +# @dimm is a list of hashes. There's one hash for each EEPROM we found. +# Each hash has the following keys: +# * eeprom: Name of the eeprom data file +# * file: Full path to the eeprom data file +# * bytes: The EEPROM data (array) +# * is_rambus: Whether this is a RAMBUS DIMM or not (boolean) +# * chk_label: The label to display for the checksum or CRC +# * chk_valid: Whether the checksum or CRC is valid or not (boolean) +# * chk_spd: The checksum or CRC value found in the EEPROM +# * chk_calc: The checksum or CRC computed from the EEPROM data +# Keys are added over time. +@dimm = get_dimm_list() unless $use_hexdump; + +for my $i (0 .. $#dimm) { + my @bytes = readspd(0, 128, $dimm[$i]->{file}); + $dimm[$i]->{bytes} = \@bytes; + $dimm[$i]->{is_rambus} = $bytes[0] < 4; # Simple heuristic + if ($dimm[$i]->{is_rambus} || $bytes[2] < 9) { + ($dimm[$i]->{chk_label}, $dimm[$i]->{chk_valid}, + $dimm[$i]->{chk_spd}, $dimm[$i]->{chk_calc}) = + checksum(\@bytes); + } else { + ($dimm[$i]->{chk_label}, $dimm[$i]->{chk_valid}, + $dimm[$i]->{chk_spd}, $dimm[$i]->{chk_calc}) = + check_crc(\@bytes); + } +} + +# Checksum or CRC validation +if (!$opt_igncheck) { + for (my $i = 0; $i < @dimm; ) { + if ($dimm[$i]->{chk_valid}) { + $i++; + } else { + splice(@dimm, $i, 1); + } + } +} + +# Process the valid entries +for $current (0 .. $#dimm) { + my @bytes = @{$dimm[$current]->{bytes}}; + + if ($opt_side_by_side) { + printl("Decoding EEPROM", $dimm[$current]->{eeprom}); + } + + if (!$use_hexdump) { + if ($dimm[$current]->{file} =~ /-([\da-f]+)$/i) { + my $dimm_num = hex($1) - 0x50 + 1; + if ($dimm_num >= 1 && $dimm_num <= 8) { + printl("Guessing DIMM is in:", "bank $dimm_num"); + } + } + } + +# Decode first 3 bytes (0-2) + prints("SPD EEPROM Information"); + + printl($dimm[$current]->{chk_label}, ($dimm[$current]->{chk_valid} ? + sprintf("OK (%s)", $dimm[$current]->{chk_calc}) : + sprintf("Bad\n(found %s, calculated %s)", + $dimm[$current]->{chk_spd}, $dimm[$current]->{chk_calc}))); + + my $temp; + if ($dimm[$current]->{is_rambus}) { + if ($bytes[0] == 1) { $temp = "0.7"; } + elsif ($bytes[0] == 2) { $temp = "1.0"; } + elsif ($bytes[0] == 0) { $temp = "Invalid"; } + else { $temp = "Reserved"; } + printl("SPD Revision", $temp); + } else { + my ($spd_size, $spd_used) = spd_sizes(\@bytes); + printl("# of bytes written to SDRAM EEPROM:", $spd_used); + printl("Total number of bytes in EEPROM:", $spd_size); + + # If there's more data than what we've read, let's + # read it now. DDR3 will need this data. + if ($spd_used > @bytes) { + push (@bytes, + readspd(@bytes, $spd_used - @bytes, + $dimm[$current]->{file})); + } + } + + my $type = sprintf("Unknown (0x%02x)", $bytes[2]); + if ($dimm[$current]->{is_rambus}) { + if ($bytes[2] == 1) { $type = "Direct Rambus"; } + elsif ($bytes[2] == 17) { $type = "Rambus"; } + } else { + my @type_list = ( + "Reserved", "FPM DRAM", # 0, 1 + "EDO", "Pipelined Nibble", # 2, 3 + "SDR SDRAM", "Multiplexed ROM", # 4, 5 + "DDR SGRAM", "DDR SDRAM", # 6, 7 + "DDR2 SDRAM", "FB-DIMM", # 8, 9 + "FB-DIMM Probe", "DDR3 SDRAM", # 10, 11 + ); + if ($bytes[2] < @type_list) { + $type = $type_list[$bytes[2]]; + } + } + printl("Fundamental Memory type:", $type); + +# Decode next 61 bytes (3-63, depend on memory type) + $decode_callback{$type}->(\@bytes) + if exists $decode_callback{$type}; + + if ($type eq "DDR3 SDRAM") { + # Decode DDR3-specific manufacturing data in bytes + # 117-149 + decode_ddr3_mfg_data(\@bytes) + } else { + # Decode next 35 bytes (64-98, common to most + # memory types) + decode_manufacturing_information(\@bytes); + } + +# Next 27 bytes (99-125) are manufacturer specific, can't decode + +# Last 2 bytes (126-127) are reserved, Intel used them as an extension + if ($type eq "SDR SDRAM") { + decode_intel_spec_freq(\@bytes); + } +} + +# Side-by-side output format is only possible if all DIMMs have a similar +# output structure +if ($opt_side_by_side) { + for $current (1 .. $#dimm) { + my @ref_output = @{$dimm[0]->{output}}; + my @test_output = @{$dimm[$current]->{output}}; + my $line; + + if (scalar @ref_output != scalar @test_output) { + $opt_side_by_side = 0; + last; + } + + for ($line = 0; $line < @ref_output; $line++) { + my ($ref_func, $ref_label, @ref_dummy) = @{$ref_output[$line]}; + my ($test_func, $test_label, @test_dummy) = @{$test_output[$line]}; + + if ($ref_func != $test_func || $ref_label ne $test_label) { + $opt_side_by_side = 0; + last; + } + } + } + + if (!$opt_side_by_side) { + printc("Side-by-side output only possible if all DIMMS are similar\n"); + + # Discard "Decoding EEPROM" entry from all outputs + for $current (0 .. $#dimm) { + shift(@{$dimm[$current]->{output}}); + } + } +} + +# Find out the longest value string to adjust the column width +# Note: this could be improved a bit by not taking into account strings +# which will end up being merged. +$sbs_col_width = 15; +if ($opt_side_by_side && !$opt_html) { + for $current (0 .. $#dimm) { + my @output = @{$dimm[$current]->{output}}; + my $line; + my @strings; + + for ($line = 0; $line < @output; $line++) { + my ($func, $label, $value) = @{$output[$line]}; + push @strings, split("\n", $value) if defined $value; + } + + foreach $line (@strings) { + my $len = length($line); + $sbs_col_width = $len if $len > $sbs_col_width; + } + } +} + +# Print the decoded information for all DIMMs +for $current (0 .. $#dimm) { + if ($opt_side_by_side) { + print "\n\n"; + } else { + print "" if $opt_html; + printl2("\n\nDecoding EEPROM", $dimm[$current]->{file}); + print "" if $opt_html; + } + print "\n" if $opt_html; + + my @output = @{$dimm[$current]->{output}}; + for (my $line = 0; $line < @output; $line++) { + my ($func, @param) = @{$output[$line]}; + + if ($opt_side_by_side) { + foreach ($current+1 .. $#dimm) { + my @xoutput = @{$dimm[$_]->{output}}; + if (@{$xoutput[$line]} == 3) { + # Line with data, stack all values + push @param, @{$xoutput[$line]}[2]; + } else { + # Separator, make it span + push @param, scalar @dimm; + } + } + } + + $func->(@param); + } + + print "
\n" if $opt_html; + last if $opt_side_by_side; +} +printl2("\n\nNumber of SDRAM DIMMs detected and decoded", scalar @dimm); + +print "\n" if ($opt_html && !$opt_bodyonly); diff --git a/Makefile b/Makefile index dbc4000..fceb15c 100755 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ make: build-inex build-json build-pixmaps install: install-pastebinit-and-other \ install-pixmaps install-changelogs install-manpages \ install-docs install-json install-inex install-scripts \ - link-inex + install-udev-rule link-inex clean: clean-pixmaps clean-json clean-inex clean-all @@ -88,7 +88,11 @@ clean-json: clean-inex: if test -f "I-Nex/Makefile" ; then $(MAKE) -C I-Nex distclean ; fi - + +install-udev-rule: + + $(INSTALL_DM) 600 i2c_smbus.rules $(DESTDIR)$(PREFIX)$(UDEV_RULES_DIR)/i2c_smbus.rules + clean-all: $(RM_COM) $(RMDIR_OPT) `find . -name ".gambas"` diff --git a/i-nex.mk b/i-nex.mk index 34d877c..f1d46a5 100644 --- a/i-nex.mk +++ b/i-nex.mk @@ -25,8 +25,8 @@ COMPRESS = gzip -9 RM_COM = rm RMFILE_OPT = -f RMDIR_OPT = -Rf -STATIC = true - +STATIC = false +UDEV_RULES_DIR = /lib/udev/rules.d ifeq ($(STATIC),true) YESNO_LINK = -static else diff --git a/i2c_smbus.rules b/i2c_smbus.rules index f08ebf1..86bbbdc 100644 --- a/i2c_smbus.rules +++ b/i2c_smbus.rules @@ -1,5 +1,4 @@ SUBSYSTEMS=="pci", ATTRS{vendor}=="0x8086", ATTRS{device}=="0x7113", RUN+="/sbin/modprobe i2c-piix4" -SUBSYSTEMS=="pci", ATTRS{vendor}=="0x8086", ATTRS{device}=="0x7603", RUN+="/sbin/modprobe to-be-written" SUBSYSTEMS=="pci", ATTRS{vendor}=="0x8086", ATTRS{device}=="0x719b", RUN+="/sbin/modprobe i2c-piix4" SUBSYSTEMS=="pci", ATTRS{vendor}=="0x8086", ATTRS{device}=="0x2413", RUN+="/sbin/modprobe i2c-i801" SUBSYSTEMS=="pci", ATTRS{vendor}=="0x8086", ATTRS{device}=="0x2423", RUN+="/sbin/modprobe i2c-i801"