2022年7月30日 星期六

透過 Intel SMBus Controller 訪問 DDR4 SPD

BIOS 在POST的過程中會透過SMBus 去讀取DIMM上的 EEPROM中的 SPD (serial presence detect) 的資訊來配置記憶體控制器(memory controller)。不太熟悉 SMBus的話可以先參考 初學 SMBus

在DDR4的時候,JEDEC 定義了一種新的非標準 EE1004 類型,SPD大小增加到4-Kbit (4 * 1024 / 8 = 512 Bytes),分為兩個Page,各256Bytes。(可以參考 4-Kbit Serial Presence Detect (SPD) EEPROM compatible with JEDEC EE1004 (st.com))

如果需要讀取資料,要採用切page的方式,使用一個Byte表示要讀取的位置。因此

  • 讀取EEPROM 前256 Bytes的資料,需要切到page0
  • 讀取EEPROM 後256 Bytes的資料,需要切到page1

如下圖,如果要讀EEPROM中的offset 0x135的位置,page和要帶入的offset關係可以套用以下公式:

        #define EE1004_PAGE_SHIFT 8
        page = offset >> EE1004_PAGE_SHIFT;
offset &= (1 << EE1004_PAGE_SHIFT) - 1;

        page = 0x135 >>8 = 0x01  -->切到page1
        offset = 0x135 & ((1<<8) -1) = 0x135 & 0xFF = 0x35 --> offset為 0x35


由此可知,讀取DDR4的SPD做法和DDR3差不多,只是多了切page的動作。切page的方式是對 0x36 (SPA0) 或 0x37 (SPA1) 的7 bits address寫入一個byte (這邊的data可以忽略,填0x00就好)。這個指令對照SMBus Spec 就是Send Byte 

(*0x36 是7bit address, 要加上一個Wr bit=0 才會是最後送出的address,所以 0x36 <<1 | 0 = 0x6C)


切完page之後,就可以使用Read Byte將值讀出來,這邊Wr bit要記得設為1

最後讀出來的Data可以對照 JESD21-C Annex L: Serial Presence Detect for DDR4 SDRAM Modules 翻成明文

圖片擷取自 Serial presence detect - Wikipedia


以上可以另外參考

----

最後,如果想要透過操作SMBus Controller 的方式將SPD給讀出來,步驟可以分為三個部分

  1. 初始化SMBus (只需要做一次)
  2. 切PAGE
  3. 讀SPD (可以用Byte、Byte Data或是Block)

初始化SMBus





// SMBus Controller addr = PCI_BASE | (PCI_SMBUS_BUS << 16) | (PCI_SMBUS_DEV << 11) | (PCI_SMBUS_FUN << 8); // Set the I/O Space enable bit // PCICMD_OFFSET 0x04 outpd(0xCF8, addr + PCICMD_OFFSET); data = inpd(0xCFC); if ((data & 0x01) == 0) { outpd(0xCFC, data | 0x01); } // Set the HST_EN bit // HOSTC_OFFSET 0x40 outpd(0xCF8, addr + HOSTC_OFFSET); data = inpd(0xCFC); if ((data & 0x01) == 0) { data = outpd(0xCFC, data | 0x01); } // Program the SMBus Base Address Register // SMB_BASE_OFFSET 0x20 outpd(0xCF8, addr + SMB_BASE_OFFSET); SMBBASE = inpd(0xCFC) & 0xFFE0;













切 PAGE



// Clear the status bits // HST_STS (Host Status Register) 0x00 outp(SMBBASE + HST_STS, 0x1E); // ee1004 set current page // XMIT_SLVA (Transmit Slave Address Register) 0x04 // 0x6C for page0, and 0x6E for page1 outp(SMBBASE + XMIT_SLVA, 0x6C); // Data is ignored // HST_CMD (Host Command Register) 0x03 outp(SMBBASE + HST_CMD, 0x00); // Execute the Byte command // HST_CNT (Host Control Register) 0x02 outp(SMBBASE + HST_CNT, 0x44); // Error handling

讀SPD (這邊使用Byte Data)



// Clear the status bits // HST_STS (Host Status Register) 0x00 outp(SMBBASE + HST_STS, 0x1E); // Set Address // XMIT_SLVA (Transmit Slave Address Register) 0x04 outp(SMBBASE + XMIT_SLVA, Addr << 1 | 1); // HST_CMD (Host Command Register) 0x03 outp(SMBBASE + HST_CMD, offset2read); // Execute the Byte Data command // HST_CNT (Host Control Register) 0x02 outp(SMBBASE + HST_CNT, 0x48); // Error handling // Read data from Data0 register // HST_D0 (Host Data 0 Register) 0x05 SPDData = inp(SMBBASE + HST_D0);



















沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。