Page 1 of 2

FIXED Reading SPD data from DDR2 and DDR3 RAM memory modules

Posted: Sun Jul 14, 2013 10:12 am
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.

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

Posted: Sun Jul 14, 2013 12:37 pm
by rsts
It seems to work fine (but I need to open up my PC to be sure) :)

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

Posted: Thu Jul 18, 2013 9:04 pm
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

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

Posted: Fri Jul 19, 2013 10:02 am
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.

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

Posted: Sat Jul 20, 2013 10:12 am
by doctorized
Tested again on another compurer. Somewhere there is a bug. Let me find it and then try again.

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

Posted: Sat Jul 20, 2013 12:32 pm
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.

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

Posted: Sat Jul 20, 2013 7:27 pm
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

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

Posted: Sun Jul 21, 2013 10:18 am
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.

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

Posted: Sun Jul 21, 2013 12:14 pm
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.

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

Posted: Sun Jul 21, 2013 5:53 pm
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

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

Posted: Mon Jul 22, 2013 10:34 am
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.

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

Posted: Mon Jul 22, 2013 12:08 pm
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).

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

Posted: Mon Jul 22, 2013 12:28 pm
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.

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

Posted: Mon Jul 22, 2013 2:27 pm
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

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

Posted: Mon Jul 22, 2013 2:36 pm
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