#!/usr/bin/env python3
# Copyright (c) 2010, 2011 Arastra, Inc.  All rights reserved.
# Arista Networks, Inc. Confidential and Proprietary.

# pylint: disable=consider-using-f-string
# pylint: disable=multiple-statements

import sys, errno, struct
import Pci

def field( value, hibit, lobit ):
   return ( value >> lobit ) & ( ( 1 << (hibit - lobit + 1) ) - 1 )

print( "AMD Family 10h CPU Settings" )
print( "===========================" )
print()

dev = Pci.Device( Pci.Address( slot=0x18, function=2 ) )

try:
   print( "Memory controller at PCI address {} (id {})".format( dev.address(),
      dev.id() ) )
except OSError as e:
   if e.errno != errno.ENOENT: raise
   print( "No memory controller found at PCI address %s" % dev.address() )
   sys.exit( 1 )

config = dev.config()
dramCsBaseAddr0 = (config.read32( 0x40 ), config.read32( 0x140 ))
dramCsBaseAddr1 = (config.read32( 0x44 ), config.read32( 0x144 ))
dramCsBaseAddr2 = (config.read32( 0x48 ), config.read32( 0x148 ))
dramCsBaseAddr3 = (config.read32( 0x4c ), config.read32( 0x14c ))
dramCsBaseAddr4 = (config.read32( 0x50 ), config.read32( 0x150 ))
dramCsBaseAddr5 = (config.read32( 0x54 ), config.read32( 0x154 ))
dramCsBaseAddr6 = (config.read32( 0x58 ), config.read32( 0x158 ))
dramCsBaseAddr7 = (config.read32( 0x5c ), config.read32( 0x15c ))
dramControl = (config.read32( 0x78 ), config.read32( 0x178 ))
dramBankAddrMapping = (config.read32( 0x80 ), config.read32( 0x180 ))
dramMrs = (config.read32( 0x84 ), config.read32( 0x184 ))
dramTimingLow = (config.read32( 0x88 ), config.read32( 0x188 ))
dramTimingHigh = (config.read32( 0x8c ), config.read32( 0x18c ))
dramConfigurationLow = (config.read32( 0x90 ), config.read32( 0x190 ))
dramConfigurationHigh = (config.read32( 0x94 ), config.read32( 0x194 ))
config.write32( 0x98, 0x00 )
# pylint: disable-next=superfluous-parens
while ( not (config.read32( 0x98 ) & 0x80000000) ): pass
config.write32( 0x198, 0x00 )
# pylint: disable-next=superfluous-parens
while ( not (config.read32( 0x198 ) & 0x80000000) ): pass
dramOutputDrvCompControl = (config.read32( 0x9c ), config.read32( 0x19c ))
config.write32( 0x98, 0x04 )
# pylint: disable-next=superfluous-parens
while ( not (config.read32( 0x98 ) & 0x80000000) ): pass
config.write32( 0x198, 0x04 )
# pylint: disable-next=superfluous-parens
while ( not (config.read32( 0x198 ) & 0x80000000) ): pass
dramAddrTimingControl = (config.read32( 0x9c ), config.read32( 0x19c ))
dramControllerSelectLow = config.read32( 0x110 )
dramControllerSelectHigh = config.read32( 0x114 )

assert field(dramConfigurationHigh[0], 8, 8), "This tool does not support DDR2 mode"

try:
   f = open( "/dev/cpu/0/msr" ) # pylint: disable=consider-using-with
   f.seek( 0xc0010071 )
   fidvid = struct.unpack( "<Q", f.read( 8 ) )[0]
   f.close()
except OSError as e:
   if e.errno != errno.ENOENT: raise
   print( "Could not read CPU FID/VID from MSR; try 'modprobe msr'" )
   sys.exit( 1 )
print( "CPU FID 0x{:02x} DID 0x{:01x} VID 0x{:02x}".format(
   field( fidvid, 5, 0 ), field( fidvid, 8, 6 ), field( fidvid, 15, 9 ) ) )
print()

s = dict() # pylint: disable=use-dict-literal

s["CPU Core Clock Frequency (based on FID and DID)"] = (
   "%d MHz" % ( 100 * ( field(fidvid, 5, 0) + 0x10 ) >> field(fidvid, 8, 6) ) )

s["CPU Voltage (based on VID)"] = (
   "%0.3f V" % ( 1.550 - field(fidvid, 15, 9) * 0.0125 ) )

for dct in (0, 1):
   for reg, cs in ( (dramCsBaseAddr0, "CS0"),
                    (dramCsBaseAddr1, "CS1"),
                    (dramCsBaseAddr2, "CS2"),
                    (dramCsBaseAddr3, "CS3"),
                    (dramCsBaseAddr4, "CS4"),
                    (dramCsBaseAddr5, "CS5"),
                    (dramCsBaseAddr6, "CS6"),
                    (dramCsBaseAddr7, "CS7") ):
      s["CSEnable: chip select bank enable (%s) (DCT%d)" % (cs, dct)] = (
         "%d" % field(reg[dct], 0, 0) )

   for f, cs in ( (0, "DIMM0"),
                  (4, "DIMM1"),
                  (8, "DIMM2"),
                  (12, "DIMM3") ):
      s["%s device size, width (DCT%d)" % (cs, dct)] = (
         { (0x0, 0x0): "NA",
           (0x0, 0x1): "256 MB (512 Mb x16)",
           (0x0, 0x2): "512 MB (512 Mb x8, 1 Gb x16)",
           (0x0, 0x3): "NA",
           (0x0, 0x4): "NA",
           (0x0, 0x5): "1 GB (1 Gb x8, 2 Gb x16)",
           (0x0, 0x6): "1 GB (512 Mb, x4)",
           (0x0, 0x7): "2 GB (2 Gb x8)",
           (0x0, 0x8): "2 GB (1 Gb x4)",
           (0x0, 0x9): "4 GB (2 Gb x4)",
           (0x0, 0xa): "4 GB (4 Gb x8, 8 Gb x16)",
           (0x0, 0xb): "8 GB (4 Gb x4, 8 Gb x8)",
           (0x0, 0xc): "NA",
           (0x0, 0xd): "NA",
           (0x0, 0xe): "NA",
           (0x0, 0xf): "NA",
           (0x1, 0x0): "NA",
           (0x1, 0x1): "512 MB (512 Mb x16)",
           (0x1, 0x2): "1 GB (512 Mb x8, 1 Gb x16)",
           (0x1, 0x3): "NA",
           (0x1, 0x4): "NA",
           (0x1, 0x5): "2 GB (1 Gb x8, 2 Gb x16)",
           (0x1, 0x6): "2 GB (512 Mb, x4)",
           (0x1, 0x7): "4 GB (2 Gb x8)",
           (0x1, 0x8): "4 GB (1 Gb x4)",
           (0x1, 0x9): "8 GB (2 Gb x4)",
           (0x1, 0xa): "8 GB (4 Gb x8, 8 Gb x16)",
           (0x1, 0xb): "16 GB (4 Gb x4, 8 Gb x8)",
           (0x1, 0xc): "NA",
           (0x1, 0xd): "NA",
           (0x1, 0xe): "NA",
           (0x1, 0xf): "NA"
           }.get( (field(dramConfigurationLow[dct], 11, 11),
                   field(dramBankAddrMapping[dct], f+3, f)) ) )

   s["Tcl: CAS latency (DCT%d)" % dct] = (
      "%d clocks" % (field(dramTimingLow[dct], 3, 0) + 4) )

   s["Trcd: RAS to CAS delay (DCT%d)" % dct] = (
      "%d clocks" % (field(dramTimingLow[dct], 6, 4) + 5) )

   s["Trp: row precharge time (DCT%d)" % dct] = (
      "%d clocks" % (field(dramTimingLow[dct], 9, 7) + 5) )

   s["Trtp: read to precharge time (DCT%d)" % dct] = (
      "%d clocks" % (field(dramTimingLow[dct], 11, 10) + 4
                     + (0 if field(dramConfigurationLow[dct], 10, 10) else 2)) )

   s["Tras: row active strobe (DCT%d)" % dct] = (
      "%d clocks" % (field(dramTimingLow[dct], 15, 12) + 15) )

   s["Trc: row cycle time (DCT%d)" % dct] = (
      "%d clocks" % (field(dramTimingLow[dct], 20, 16) + 11) )

   s["Twr: write recovery time (DCT%d)" % dct] = (
      "%d clocks" % (field(dramTimingLow[dct], 21, 20) + 3) )

   s["Trrd: row to row delay (or RAS to RAS delay) (DCT%d)" % dct] = (
      "%d clocks" % (field(dramTimingLow[dct], 23, 22) + 4) )

   s["MemClkDis: MEMCLK disable (DCT%d)" % dct] = (
      "0x%02x" % field(dramTimingLow[dct], 31, 24) )

   s["TrwtWB: read to write turnaround for opportunistic write bursting (DCT%d)" %
     dct] = (
      "%d clocks" % (field(dramTimingHigh[dct], 3, 0) + 3) )

   s["TrwtTO: read to write turnaround for data, DQS contention (DCT%d)" % dct] = (
      "%d clocks" % (field(dramTimingHigh[dct], 7, 4) + 2) )

   s["Twtr: internal DRAM write to read command delay (DCT%d)" % dct] = (
      "%d clocks" % (field(dramTimingHigh[dct], 9, 8) + 4) )

   s["Twrrd: write to read DIMM termination turnaround (DCT%d)" % dct] = (
      "%d clocks" % ( (field(dramControl[dct], 9, 8) << 2)
                      | field(dramTimingHigh[dct], 11, 10) ) )

   s["Twrwr: write to write timing (DCT%d)" % dct] = (
      "%d clocks" % ( (field(dramControl[dct], 11, 10) << 2)
                      | field(dramTimingHigh[dct], 13, 12) ) )

   s["Trdrd: read to read timing (DCT%d)" % dct] = (
      "%d clocks" % ( (field(dramControl[dct], 13, 12) << 2)
                      | field(dramTimingHigh[dct], 15, 14 ) + 2 ) )

   s["Tref: refresh rate (DCT%d)" % dct] = (
      { 0x0: "Undefined behavior",
        0x1: "Reserved",
        0x2: "Every 7.8 microseconds",
        0x3: "Every 3.9 microseconds"
        }.get( field(dramTimingHigh[dct], 17, 16) ) )

   s["DisAutoRefresh: disable automatic refresh (DCT%d)" % dct] = (
      { 0x0: "Automatic refresh is enabled",
        0x1: "Automatic refresh is disabled"
        }.get( field(dramTimingHigh[dct], 18, 18) ) )

   for i in range(0, 4):
      s["Trfc%d: auto-refresh row cycle time for logical DIMM %d (DCT%d)"
        % (i, i, dct)] = (
         { 0x0: "Reserved",
           0x1: "90 ns (all speeds, 512 Mbit)",
           0x2: "110 ns (all speeds, 1 Gbit)",
           0x3: "160 ns (all speeds, 2 Gbit)",
           0x4: "300 ns (all speeds, 4 Gbit)",
           0x5: "350 ns (all speeds, 8 Gbit)",
           0x6: "Reserved",
           0x7: "Reserved"
           }.get( field(dramTimingHigh[dct], 22 + i*3, 20 + i*3) ) )

   s["BurstCtrl: burst length control (DCT%d)" % dct] = (
      { 0x0: "8-beat burst length; 64-byte access",
        0x1: "Reserved",
        0x2: "4-beat burst length; 64-byte access",
        0x3: "Reserved"
        }.get( field(dramMrs[dct], 1, 0) ) )

   s["DrvImpCtrl: drive impedance control (DCT%d)" % dct] = (
      { 0x0: "40 ohm driver; Ron40 = Rzq/6 (40 ohm with nominal Rzq=240 ohms)",
        0x1: "34 ohm driver; Ron34 = Rzq/7 (34 ohm with nominal Rzq=240 ohms)",
        0x2: "Reserved for 30 ohm driver; Ron30 = Rzq/8 "
             "(30 ohm with nominal Rzq=240 ohms)",
        0x3: "Reserved"
        }.get( field(dramMrs[dct], 3, 2) ) )

   s["Twr: write recovery (DCT%d)" % dct] = (
      { 0x0: "Reserved",
        0x1: "5 MEMCLK cycles",
        0x2: "6 MEMCLK cycles",
        0x3: "7 MEMCLK cycles",
        0x4: "8 MEMCLK cycles",
        0x5: "10 MEMCLK cycles",
        0x6: "12 MEMCLK cycles",
        0x7: "Reserved"
        }.get( field(dramMrs[dct], 6, 4) ) )

   s["DramTerm: DRAM nominal termination (DCT%d)" % dct] = (
      { 0x0: "On die termination disabled",
        0x1: "RZQ/4",
        0x2: "RZQ/2",
        0x3: "RZQ/6",
        0x4: "RZQ/12",
        0x5: "RZQ/8",
        0x6: "Reserved",
        0x7: "Reserved" }.get( field(dramMrs[dct], 9, 7) ) )

   s["DramTermDyn: DRAM dynamic termination (DCT%d)" % dct] = (
      { 0x0: "Dynamic termination for writes disabled",
        0x1: "RZQ/4",
        0x2: "RZQ/2",
        0x3: "Reserved"
        }.get( field(dramMrs[dct], 11, 10) ) )

   s["Qoff: output disable (DCT%d)" % dct] = (
      { 0x0: "Output buffers enabled",
        0x1: "Output buffers disabled"
        }.get( field(dramMrs[dct], 13, 13) ) )

   s["Tcwl: CAS write latency (DCT%d)" % dct] = (
      "%d clocks" % (field(dramMrs[dct], 22, 20) + 5) )

   s["ParEn: parity enable (DCT%d)" % dct] = (
      "%d" % field(dramConfigurationLow[dct], 8, 8) )

   s["Width128: width of DRAM interface in 128-bit mode (DCT%d)" % dct] = (
      { 0x0: "1 DIMM wide",
        0x1: "2 DIMMs wide" }.get( field(dramConfigurationLow[dct], 11, 11) ) )

   for i in range(0, 4):
      s["X4Dimm%d: x4 (by 4) logical DIMM %d (DCT%d)" % (i, i, dct)] = (
         { 0x0: "x4 DIMM not present",
           0x1: "x4 DIMM present"
           }.get( field(dramConfigurationLow[dct], 12+i, 12+i ) ) )

   s["UnbuffDimm: unbuffered DIMMs (DCT%d)" % dct] = (
      { 0x0: "Registered DIMMs",
        0x1: "Unbuffered DIMMs" }.get( field(dramConfigurationLow[dct], 16, 16) ) )

   s["DimmEccEn: DIMM ECC enable (DCT%d)" % dct] = (
      "%d" % field(dramConfigurationLow[dct], 19, 19) )

   s["DynPageCloseEn: dynamic page close enable (DCT%d)" % dct] = (
      "%d" % field(dramConfigurationLow[dct], 20, 20) )

   s["IdleCycLowLimit: idle cycle low limit (DCT%d)" % dct] = (
      { 0x0: "16 clocks",
        0x1: "32 clocks",
        0x2: "64 clocks",
        0x3: "96 clocks"
        }.get( field(dramConfigurationLow[dct], 22, 21) ) )

   s["ForceAutoPchg: force auto precharging (DCT%d)" % dct] = (
      { 0x0: "Do not force auto-precharge cycles with every read or write command",
        0x1: "Force auto-precharge cycles with every read or write command"
        }.get( field(dramConfigurationLow[dct], 23, 23) ) )

   s["DisDllShutdownSR: disable DLL shutdown in self-refresh mode (DCT%d)" % dct] = (
      { 0x0: "Do not disable DLL shutdown",
        0x1: "Disable DLL shutdown"
        }.get( field(dramConfigurationLow[dct], 27, 27) ) )

   s["MemClkFreq: memory clock frequency (DCT%d)" % dct] = (
      { 0x0: "Reserved",
        0x1: "Reserved",
        0x2: "333 MHz",
        0x3: "400 MHz",
        0x4: "533 MHz",
        0x5: "667 MHz",
        0x6: "800 MHz",
        0x7: "Reserved" }.get( field(dramConfigurationHigh[dct], 2, 0) ) )

   s["Ddr3Mode (DCT%d)" % dct] = (
      { 0x0: "DDR2 mode",
        0x1: "DDR3 mode"
        }.get( field(dramConfigurationHigh[dct], 8, 8) ) )

   s["ZqcsInterval: ZQ calibration short interval (DCT%d)" % dct] = (
      { 0x0: "ZQ calibration short command is disabled",
        0x1: "64 ms",
        0x2: "128 ms",
        0x3: "256 ms"
        }.get( field(dramConfigurationHigh[dct], 11, 10) ) )

   s["RDqsEn: read DQS enable (DCT%d)" % dct] = (
      { 0x0: "DM pins function as data mask pins",
        0x1: "DM and DQS#[17:9] pins provide DQS termination of x8 based DIMMs"
        }.get( field(dramConfigurationHigh[dct], 12, 12) ) )

   s["DisSimulRdWr: disable simultaneous read from one DCT and write to "
     "the other DCT (DCT%d)" % dct] = (
      { 0x0: "Simultaneous read and write enabled",
        0x1: "Simultaneous read and write disabled"
        }.get( field(dramConfigurationHigh[dct], 13, 13) ) )

   s["DisDramInterface: disable the DRAM interface (DCT%d)" % dct] = (
      { 0x0: "DRAM controller is enabled",
        0x1: "DRAM controller is disabled"
        }.get( field(dramConfigurationHigh[dct], 14, 14) ) )

   s["PowerDownEn: power down mode enable (DCT%d)" % dct] = (
      { 0x0: "Power down mode is disabled",
        0x1: "Power down mode is enabled"
        }.get( field(dramConfigurationHigh[dct], 15, 15) ) )

   s["PowerDownMode: power down mode (DCT%d)" % dct] = (
      { 0x0: "Channel CKE control mode",
        0x1: "Chip select CKE control mode"
        }.get( field(dramConfigurationHigh[dct], 16, 16) ) )

   s["FourRankRDimm: four rank registered DIMM connected (DCT%d)" % dct] = (
      "%d" % field(dramConfigurationHigh[dct], 18, 18) )

   s["SlowAccessMode: slow access mode (aka 2T mode) (DCT%d)" % dct] = (
      { 0x0: "1T Mode",
        0x1: "2T Mode"
        }.get( field(dramConfigurationHigh[dct], 20, 20) ) )

   s["BankSwizzleMode: bank swizzle mode (DCT%d)" % dct] = (
      { 0x0: "Disabled",
        0x1: "Enabled"
        }.get( field(dramConfigurationHigh[dct], 22, 22) ) )

   s["ProcOdtDis: processor on-die termination disable (DCT%d)" % dct] = (
      { 0x0: "Processor on-die termination enabled",
        0x1: "Processor on-die termination disabled"
        }.get( field(dramConfigurationHigh[dct], 23, 23) ) )

   s["DcqBypassMax: DRAM controller queue bypass maximum (DCT%d)" % dct] = (
      "The oldest request may be bypassed no more than %d times" %
      field(dramConfigurationHigh[dct], 27, 24) )

   s["FourActWindow: four bank activate window (DCT%d)" % dct] = (
      "%d MEMCLK cycles" % (field(dramConfigurationHigh[dct], 31, 28) + 15) )

   s["RdPtrInit: read pointer initial value (DCT%d)" % dct] = (
      { 0x0: "Reserved",
        0x1: "Reserved",
        0x2: "Reserved",
        0x3: "Reserved",
        0x4: "2 MEMCLKs",
        0x5: "1.5 MEMCLKs",
        0x6: "1 MEMCLK",
        0x7: "Reserved",
        0x8: "Reserved",
        0x9: "Reserved",
        0xa: "Reserved",
        0xb: "Reserved",
        0xc: "Reserved",
        0xd: "Reserved",
        0xe: "Reserved",
        0xf: "Reserved"
        }.get( field(dramControl[dct], 3, 0) ) )

   s["ChSetupSync: channel setup synchronize (DCT%d)" % dct] = (
      "%d" % field(dramControl[dct], 15, 15) )

   s["EarlyArbEn: early arbitration enable (DCT%d)" % dct] = (
      "%d" % field(dramControl[dct], 19, 19) )

   s["MaxRdLatency: maximum read latency (DCT%d)" % dct] = (
      "%d NB clocks" % field(dramControl[dct], 31, 22) )

   s["CkeDrvStren: CKE drive strength (DCT%d)" % dct] = (
      { 0x0: "1.0x",
        0x1: "1.25x",
        0x2: "1.5x",
        0x3: "2.0x"
        }.get( field(dramOutputDrvCompControl[dct], 1, 0) ) )

   s["CsOdtDrvStren: CS/ODT drive strength (DCT%d)" % dct] = (
      { 0x0: "1.0x",
        0x1: "1.25x",
        0x2: "1.5x",
        0x3: "2.0x"
        }.get( field(dramOutputDrvCompControl[dct], 5, 4) ) )

   s["AddrCmdDrvStren: address/command drive strength (DCT%d)" % dct] = (
      { 0x0: "1.0x",
        0x1: "1.25x",
        0x2: "1.5x",
        0x3: "2.0x"
        }.get( field(dramOutputDrvCompControl[dct], 9, 8) ) )

   s["ClkDrvStren: MEMCLK drive strength (DCT%d)" % dct] = (
      { 0x0: "0.75x",
        0x1: "1.0x",
        0x2: "1.25x",
        0x3: "1.5x"
        }.get( field(dramOutputDrvCompControl[dct], 13, 12) ) )

   s["DataDrvStren: data drive strength (DCT%d)" % dct] = (
      { 0x0: "0.75x",
        0x1: "1.0x",
        0x2: "1.25x",
        0x3: "1.5x"
        }.get( field(dramOutputDrvCompControl[dct], 17, 16) ) )

   s["DqsDrvStren: DQS drive strength (DCT%d)" % dct] = (
      { 0x0: "0.75x",
        0x1: "1.0x",
        0x2: "1.25x",
        0x3: "1.5x"
        }.get( field(dramOutputDrvCompControl[dct], 21, 20) ) )

   s["ProcOdt: processor on-die termination (DCT%d)" % dct] = (
      { 0x0: "240 ohms +/- 10%",
        0x1: "120 ohms +/- 10%",
        0x2: "60 ohms +/- 10%",
        0x3: "Reserved"
        }.get( field(dramOutputDrvCompControl[dct], 29, 28) ) )

   s["CkeFineDelay: CKE fine delay (DCT%d)" % dct] = (
      "%d/64 MEMCLK delay" % field(dramAddrTimingControl[dct], 4, 0) )

   s["CkeSetup: CKE setup time (DCT%d)" % dct] = (
      { 0x0: "1/2 MEMCLK",
        0x1: "1 MEMCLK"
        }.get( field(dramAddrTimingControl[dct], 5, 5) ) )

   s["CsOdtFineDelay: CS/ODT fine delay (DCT%d)" % dct] = (
      "%d/64 MEMCLK delay" % field(dramAddrTimingControl[dct], 12, 8) )

   s["CsOdtSetup: CS/ODT setup time (DCT%d)" % dct] = (
      { 0x0: "1/2 MEMCLK",
        0x1: "1 MEMCLK"
        }.get( field(dramAddrTimingControl[dct], 13, 13) ) )

   s["AddrCmdFineDelay: address/command fine delay (DCT%d)" % dct] = (
      "%d/64 MEMCLK delay" % field(dramAddrTimingControl[dct], 20, 16) )

   s["AddrCmdSetup: address/command setup time (DCT%d)" % dct] = (
      { 0x0: "1/2 MEMCLK",
        0x1: "1 MEMCLK"
        }.get( field(dramAddrTimingControl[dct], 21, 21) ) )

s["DctSelHiRngEn: DRAM controller select high range enable"] = (
   "%d" % field(dramControllerSelectLow, 0, 0) )

s["DctSelHi: DRAM controller high select"] = (
   "%d" % field(dramControllerSelectLow, 1, 1) )

s["DctSelIntLvEn: DRAM controller interleave enable"] = (
   "%d" % field(dramControllerSelectLow, 2, 2) )

s["DctGangEn: DRAM controller ganging enable"] = (
   "%d" % field(dramControllerSelectLow, 4, 4) )

s["DctDatIntLv: DRAM controller data interleave enable"] = (
   "%d" % field(dramControllerSelectLow, 5, 5) )

for x, y in sorted( s.items() ):
   print( x + ":" )
   print( "  " + ( y or "N/A" ) )
