FIXED Reading SPD data from DDR2 and DDR3 RAM memory modules

Share your advanced PureBasic knowledge/code with the community.
User avatar
doctorized
Addict
Addict
Posts: 882
Joined: Fri Mar 27, 2009 9:41 am
Location: Athens, Greece

FIXED Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by doctorized »

Updated for 5.20+

This code reads Serial Presence Detect (SPD) data from DDR2 and DDR3 RAM memory modules and shows all info included EPP 1.0, EPP 2.0 and XMP memory profiles. No WMI usage, only driver. Test it and tell me how it works.
http://kc2000labs.shadowtavern.com/pb/z ... PD_520.rar


EDIT: The code updated for PB 5.20 and uploaded: 11/30/2013.
Last edited by doctorized on Sat Nov 30, 2013 3:33 pm, edited 9 times in total.
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Re: Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by rsts »

It seems to work fine (but I need to open up my PC to be sure) :)
User avatar
doctorized
Addict
Addict
Posts: 882
Joined: Fri Mar 27, 2009 9:41 am
Location: Athens, Greece

Re: Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by doctorized »

I wrote a pdf file explaining the EPP 2.0 memory profiles, as NVidia has a pdf that it is not very helpful (with some errors).
http://kc2000labs.shadowtavern.com/EPP2.0.pdf
Mesa
Enthusiast
Enthusiast
Posts: 433
Joined: Fri Feb 24, 2012 10:19 am

Re: Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by Mesa »

It doesn't work on Xp32b and purebasic 5.20b6 x86.

In callingprocedures.pb
line: ModuleData(lDimm)\EPP[0]\ProfileType = "Basic"

-> error: "ProfileType" desn't exist.

Mesa.
User avatar
doctorized
Addict
Addict
Posts: 882
Joined: Fri Mar 27, 2009 9:41 am
Location: Athens, Greece

Re: Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by doctorized »

Tested again on another compurer. Somewhere there is a bug. Let me find it and then try again.
User avatar
doctorized
Addict
Addict
Posts: 882
Joined: Fri Mar 27, 2009 9:41 am
Location: Athens, Greece

Re: Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by doctorized »

In some computers works file but in some others it doesn't: it can read the Base Address but it cannot scan it for EEPROM devices. I cannot find out why. Can anyone help?

@Mesa: please download the code and run it again.
Helle
Enthusiast
Enthusiast
Posts: 178
Joined: Wed Apr 12, 2006 7:59 pm
Location: Germany
Contact:

Re: Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by Helle »

Works for Intel Z87-Chip-Set with

Code: Select all

...
Data.i $80868C22, ?ID80868C22
...
;and 
...
ID80868C22: Data.s "Intel 8 Series/C220 Series SMBus Controller"
...
But is this necessary? I think, the values for bus=0, device=31 and function=3 are fix for the SM-Bus.

Helle
Mesa
Enthusiast
Enthusiast
Posts: 433
Joined: Fri Feb 24, 2012 10:19 am

Re: Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by Mesa »

Hi,

I've got an another bug, on line 155 of CallingProcedures.pb:
ReDim spd.c(Dimm,255)

-> "only the last dimension of the array can be modified by Redim"

Mesa.
User avatar
doctorized
Addict
Addict
Posts: 882
Joined: Fri Mar 27, 2009 9:41 am
Location: Athens, Greece

Re: Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by doctorized »

Helle wrote:But is this necessary? I think, the values for bus=0, device=31 and function=3 are fix for the SM-Bus.
No, it is not fix. For example my ATI/AMD SMBus controller is at: bus=0, device=20 and function=0. A friend's pc with NVIDIA 750 has: bus=0, device=1 and function=1.

@Mesa: As I think that something bad is happening with memory allocation/writing, try to use Purifier. It may help us find what is going on.
Helle
Enthusiast
Enthusiast
Posts: 178
Joined: Wed Apr 12, 2006 7:59 pm
Location: Germany
Contact:

Re: Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by Helle »

Ah, yes. Please check this:

Code: Select all

;- Scan PCI-Bus for Base-Address SM-Bus. SMB is Class $0C05 in Bus0 (PCI Local Bus Specification Revision 2.3).
;- "Helle" Klaus Helbing, 21.07.2013, PB v5.10 (x64) 
;- Use WinRing0.dll/.sys or WinRing0x64.dll/.sys. DLL and SYS must be in this folder (make an EXE!)! 

Datas.l
Config_Address.l              = $0CF8
Config_Data.l                 = $0CFC
#PROCESSOR_ARCHITECTURE_AMD64 = $9

SI.SYSTEM_INFO                              ;Structure System_Info
GetSystemInfo_(@SI)

If SI\wProcessorArchitecture = #PROCESSOR_ARCHITECTURE_AMD64
  DLLOK = OpenLibrary(0, "WinRing0x64.dll") ;64-Bit-Version
 Else
  DLLOK = OpenLibrary(0, "WinRing0.dll")    ;32-Bit-Version
EndIf 

If DLLOK
  Prototype.i ProtoWinRing0_0()
  WR0_InitializeOls.ProtoWinRing0_0   = GetFunction(0, "InitializeOls")
  WR0_DeinitializeOls.ProtoWinRing0_0 = GetFunction(0, "DeinitializeOls")

  Prototype.i ProtoWinRing0_2(V1.l, V2.l)
  WR0_ReadIoPortDword.ProtoWinRing0_2 = GetFunction(0, "ReadIoPortDwordEx")  
  WR0_WriteIoPortDword.ProtoWinRing0_2 = GetFunction(0, "WriteIoPortDwordEx")

  If WR0_InitializeOls()
    Reg_Address.l = $80000000                                   ;set Bit31 
    For Device = 0 To 31
      Reg_Address & $FFFFF8FF                                   ;set Functions=0 
      For Function = 0 To 7
        WR0_WriteIoPortDword(Config_Address, Reg_Address + $A)  ;$A=Sub Class, $B=Base Class 
        WR0_ReadIoPortDword(Config_Data, @Datas)
        Datas = (Datas >> 16) & $FFFF
        If Datas = $0C05                                        ;SMB is Class $0C05
          WR0_WriteIoPortDword(Config_Address, Reg_Address + $20) ;$20=Base Address Register (4) ... 
          WR0_ReadIoPortDword(Config_Data, @Datas)
          Break 2
        EndIf
        Reg_Address + $100 
      Next    
    Next 
    WR0_DeinitializeOls()
    MessageRequester("SMB Base-Address", "$" + Hex(Datas & $FFFFFFFC))
   Else 
    MessageRequester("Error!", "No WinRing0!") 
  EndIf 
EndIf 

Notice: I use DLL and SYS from WinRing0!
My result: $F000.

Helle
User avatar
doctorized
Addict
Addict
Posts: 882
Joined: Fri Mar 27, 2009 9:41 am
Location: Athens, Greece

Re: Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by doctorized »

Helle wrote:Ah, yes. Please check this:
YESS! That's what I wanted!! I had forgoten that each device type has its own ClassID. Now I can find SMBus controller without the need of the "ChipsAndBasses.pb" file. The first problem is solved. The second is to find out what is going on with NVidia SMBuses.
Helle
Enthusiast
Enthusiast
Posts: 178
Joined: Wed Apr 12, 2006 7:59 pm
Location: Germany
Contact:

Re: Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by Helle »

Third :) : Don´t work with AMD (e.g. FX8120 with SB950; must be $b00). I will take a look in the AMD-BKDG (BIOS and Kernel Developer’s Guide).
User avatar
doctorized
Addict
Addict
Posts: 882
Joined: Fri Mar 27, 2009 9:41 am
Location: Athens, Greece

Re: Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by doctorized »

Helle wrote: must be $b00.
What do you get? I have SB750 and I get BaseAddress = $B00. Do you use my way with the chips to find the base address or yours? If you use mine, then SB950 is not in the list. Replace the procedure with this:

Code: Select all

Procedure.l GetSMBusBaseAddress()
	OnErrorGoto(?ExitDevs)
	bus.l: dev.l: func.l
	pci_address.l
	id.l
	type.b
	ClassID.l
	For bus = 0 To 255
		For dev = 0 To $1F
			For func = 0 To 7
				pci_address = ((bus & $ff)<<8) | ((dev & $1f)<<3) | (func & 7)
				id = PciConfigReadLong(pci_address, $A)
				ClassID = id & $ffff				
				If ClassID = $0C05
					lAddress = PciConfigReadLong(pci_address, $20) & $FFF0
					If lAddress = 0
						lAddress = PciConfigReadLong(pci_address, $24) & $FFF0
						If lAddress = 0
							lAddress = PciConfigReadLong(pci_address, $90) & $FFF0
							If lAddress = 0
								lAddress = PciConfigReadLong(pci_address, $94) & $FFF0
							EndIf
						EndIf
					EndIf
					If lAddress <> 0
						ProcedureReturn lAddress
					EndIf								 
				EndIf
				type = PciConfigReadChar(pci_address, $0e);
				If func =0 And (type & $80) =0 :Break: EndIf
			Next
		Next
	Next
ExitDevs:
	ProcedureReturn 0
EndProcedure
I also found this: http://kc2000labs.shadowtavern.com/Unit1.cpp. It is from a C++ project and I am trying to find out what could I retrieve from it without any luck.
Helle
Enthusiast
Enthusiast
Posts: 178
Joined: Wed Apr 12, 2006 7:59 pm
Location: Germany
Contact:

Re: Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by Helle »

With all codes no chance for the SB950. Class $0C05 is found on Device = $14, Function = $0. Vendor = $1002, Device = $4385. This is in your list as "ATI RD600/RS600". The Win7-Device-Manager says for $1002_4385: "ATI I/O-Communicationprocessor SMBus-Controller". A scan in the Reg_Address-Area is without success (for $B00). No second found of $0C05.
Helle
User avatar
doctorized
Addict
Addict
Posts: 882
Joined: Fri Mar 27, 2009 9:41 am
Location: Athens, Greece

Re: Reading SPD data from DDR2 and DDR3 RAM memory modules

Post by doctorized »

Helle wrote:With all codes no chance for the SB950.
If you take a look at the cpp file I refer above, you will see that it is using a seperate procedure for each vendor: ATI, NVidia, SIS, VIA, INTEL. I tried to use the code for NVidia with no success. I cannot find out what is wrong.
The initial code:

Code: Select all

int Get_NVCK804_SMBUS(BYTE * SPD,WORD BASE_ADDRESS,BYTE Slave_Address)
{
  for(int i=0;i<0x80;i++)
  {
   int error=0;
   do
     {
      outportb(BASE_ADDRESS+0x00,0xFF);
      flag=inportb(BASE_ADDRESS+0x00);
      error++;
      if(error>0x8000)
      {
         MessageBox(Application->Handle,"Reset Nvidia CK804 SMBUS Error!","nForce CK804",MB_OK|MB_ICONINFORMATION);
         return 2;
      }
     }while((flag&0x9F)!=0);

   error=0;
   outportb((BASE_ADDRESS+0x02),Slave_Address-1);
   outportb((BASE_ADDRESS+0x03),i);
   outportb((BASE_ADDRESS+0x00),0x07);
   do
   {
    error++;
    if(error>0x100)
    {
       MessageBox(Application->Handle,"Read Nvidia CK804 SMBUS Error!","nForce CK804",MB_OK|MB_ICONINFORMATION);
       return 2;
    }
    if((inportb(BASE_ADDRESS+0x01)&0x10)==0x10)
    {
       char buffer[256];
       sprintf(buffer,"No Module in channel 0x%02X!",Slave_Address-1);
       //MessageBox(Application->Handle,buffer,"nForce CK804",MB_OK|MB_ICONINFORMATION);
       return 1;
    }
    Sleep(1);
   }while(inportb(BASE_ADDRESS+0x01)!=0x80);
   SPD[i]=inportb(BASE_ADDRESS+0x04);
  }
  return 0;
  //quaere(SPD);
}
and mine:

Code: Select all

Procedure.l smbCallBus(BaseAddr.l, Cmd.b, Slave.b, RW.b)
	For i=0 To $80
		error.l=0
		While (flag & $9F) <> 0
			WriteIOPortByte(BaseAddr, $FF)
			flag = ReadIOPortByte(BaseAddr)
			error + 1
			If error > $80
				Debug "i = " + Hex(i) + " / Reset Nvidia SMBUS Error!"
				ProcedureReturn 2
			EndIf
		Wend
		;Slave = Slave - 1
		error = 0
		WriteIoPortByte(BaseAddr + 2, Slave -1)
		WriteIoPortByte(BaseAddr + 3, i)
		WriteIoPortByte(BaseAddr + 0, 7)
		Delay(20)
		While ReadIOPortByte(BaseAddr + 1) <> $80
			error + 1
			If error > $100
				Debug "i = " + Hex(i) + " / Read Nvidia SMBUS Error!"
				ProcedureReturn 3
			EndIf
			If ReadIOPortByte(BaseAddr + 1) & $10 = $10
				Debug "i = " + Hex(i) + " / No Module in channel " + Hex(Slave - 1)
				ProcedureReturn 1
			EndIf
			Delay(20)
		Wend
		Debug Hex(ReadIOPortByte(BaseAddr + 4))
	Next
EndProcedure
Post Reply