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

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

import seeprom, sys, Tac
import array, optparse # pylint: disable=deprecated-module

usage = """
%prog [options] read
  read Serial Presence Detects (spd) seeprom  for DDR SDRAM and write to stdout
"""

parser = optparse.OptionParser(usage=usage)

parser.add_option( "-d", "--device", action="append",
                   help="specify device (bus.address)" )

( options, args ) = parser.parse_args()
if len( args ) < 1:
   parser.error( "Too few arguments" )

busId = 0
offset = 0
deviceList = list() # pylint: disable=use-list-literal
# pylint: disable-next=consider-using-in,singleton-comparison
if options.device == ["all"] or options.device == None:
   for seepromId in [0x50, 0x51]:
      deviceList.append(( busId, seepromId ))
else:
   for device in options.device:
      (busId, seepromId) = device.split(".")
      deviceList.append((seeprom.strToInt( busId ),
                         seeprom.strToInt( seepromId ))) 

op = args[0]
l = len( args )
MemoryTypeStr = ["None", "FPM DRAM", "EDO", "Pipeline Nibble", "SDRAM", "ROM",
                 "DDR SGRAM", "DDR SDRAM", 
                 "DDR2 SDRAM", "DDR2 SDRAM FB", "DDR2 SDRAM FB PROBE",
                 "DDR3 SDRAM"]

totalSize = 0

if op == "read":
   if l != 1:
      parser.error( "Too many arguments" )
   
   for (busId, seepromId) in deviceList:
      print( "\n--- Memory Module at %d/0x%x ----" % ( busId, seepromId ) )
      try:
         result = seeprom.doRead( busId, seepromId, offset, length=256 )
      except Tac.SystemCommandError as e:
         sys.stdout.write( "Read Error: %s  (Invalid device?)\n" % e )
         continue      
      spd = array.array('B', result)

      ddrType = spd[2]
      ddrTypeStr = MemoryTypeStr[ddrType]
      print( "type=%s" % ddrTypeStr )
      if ddrType  == 11: #DDR3
         spdSize = [128, 176, 256] [spd[0] & 0x0f]
         rows = ((spd[5] & 0x38)>>3) + 12
         cols =  (spd[5] & 0x07) + 9
         ranks = ((spd[7] & 0x38) >> 3) + 1
         width =  2**((spd[7] & 0x07) + 2)
         busWidth = 2**((spd[8] & 0x07)+3)
         totalBusWidth = busWidth + [0, 8][(spd[8]&0x38)>>3]  
         banks = 2**(((spd[4] & 0x70)>>4) + 3)
         capacityMb = 2**((spd[4] & 0xf) + 8)
         mfgJEDECId =  spd[118:119] + spd[117:118]
         mfgLocation = spd[119]
         mfgPartNo = spd[128:146]
         mfgRev = spd[146:148]
         mfgYY = spd[120] 
         mfgWK = spd[121] 
         mfgSerialNo = spd[122:126]
         mfgInfo = spd[150:176]
         
      elif ddrType in [8, 9, 10]: #DDR2
         spdSize = spd[0]
         rows = spd[3]
         cols = spd[4]
         ranks = (spd[5] & 0x07) + 1
         totalBusWidth = spd[6]
         banks = spd[17]
         width = spd[13]
         errBusWidth = spd[14]
         busWidth = totalBusWidth - errBusWidth
         density = spd[31]
         if density & 0x1f: # in 1GB
            capacityMb = density*1024 #in GB->MB
         elif density & 0xe0:  # < 1GB
            capacityMb =  density * 4  # 128MB unit
  
         mfgJEDECId =  spd[64:72]
         mfgLocation = spd[72]
         mfgPartNo = spd[73:91]
         mfgRev = spd[91:93]
         mfgYY = spd[93] 
         mfgWK = spd[94] 
         mfgSerialNo = spd[95:99]
         mfgInfo = spd[99:128]


      #totalModuleSize = sdram capacity (Mb) * busWidth / 8 bit / width * ranks
      totalSize = capacityMb * busWidth // 8 // width * ranks
      print( "spd size=", spdSize )
      print( "rows=%d cols=%d banks=%d ranks=%d width=%d busWidth=%d"
             % ( rows, cols, banks, ranks, width, busWidth ) )
      print( "capacity=%dMb total size=%dMB " % ( capacityMb, totalSize ) )
      print( "\nMfg JEDEC Id=0x" + ''.join( [ "%02x" %i for i in mfgJEDECId ] ) )
      print( "Mfg Location=0x%02x" % mfgLocation )
      print( "Mfg Part Number="+''.join( [ "%c" %i for i in mfgPartNo ] ) ) 
      print( "Mfg Revision=0x"+''.join( [ "%02x" %i for i in  mfgRev ] ) )
      print( "Mfg Date=week %2x in year 20%02x" % ( mfgWK, mfgYY ) ) #BCD
      print( "Mfg Serial No="+''.join( [ "%02x" %i for i in mfgSerialNo ] ) )
      print( "Mfg Info="+''.join( [ "%c" %i for i in mfgInfo ] ) )
      
      print( "\nSPD seeprom content: (raw format) %d Bytes" % spdSize )
      print( "     ", ' '.join( [ "%2x" %ii for ii in range( 0, 16 ) ] ) )
      for i in range(0, spdSize//16):
         k = i << 4
         print( "0x%02x:" % k, ' '.join( [ "%02x" % spd[ ii ]
                                      for ii in range( k, k+16 ) ] ) )
      print( "" )
else:
   parser.error( "Unknown operation: '" + op + "'" )


