Various fixes on SPD decoding algorithms (#152)

* [DDR5] Fix rounding errors on SPD Timings

* [DDR5] Add a rounding factor of ~0.3% according to JEDEC to solve the last rounding issue found on NETAC Modules

* [DDR5] Add missing package ranks per channel parameter in total module capacity algorithm

* [DDR4] Fix rounding issues in SPD timings & frequency

* [DDR3] Fix rounding issues in SPD timings & frequency decoding. Check XMP Profile #2. Add a quirk for Kingston based on very early XMP 1.0 specs

* [DDR2] Fix CAS detection & rounding issues in SPD timings w/ EPP

* [DDR] Correct SPD timings rounding issues & add support for x.5 CAS latencies

* [SDR] Correct SPD Timings decoding due to rounding errors

* Add various JEP106 Manufacturers found while debugging

* Update timings display function to handle x.5 CAS
This commit is contained in:
Sam Demeulemeester 2022-08-26 21:56:12 +02:00 committed by GitHub
parent 93051adfc2
commit 0f8981412c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 148 additions and 103 deletions

View File

@ -266,7 +266,7 @@ void post_display_init(void)
if (false) {
// Try to get RAM information from IMC (TODO)
display_spec_mode("IMC: ");
display_spec_ddr(ram.freq, ram.type, ram.tCL, ram.tRCD, ram.tRP, ram.tRAS);
display_spec_ddr(ram.freq, ram.type, ram.tCL, ram.tCL_dec, ram.tRCD, ram.tRP, ram.tRAS);
display_mode = DISPLAY_MODE_IMC;
} else if (ram.freq > 0 && ram.tCL > 0) {
// If not available, grab max memory specs from SPD
@ -274,7 +274,7 @@ void post_display_init(void)
if (ram.freq <= 166) {
display_spec_sdr(ram.freq, ram.type, ram.tCL, ram.tRCD, ram.tRP, ram.tRAS);
} else {
display_spec_ddr(ram.freq, ram.type, ram.tCL, ram.tRCD, ram.tRP, ram.tRAS);
display_spec_ddr(ram.freq, ram.type, ram.tCL, ram.tCL_dec, ram.tRCD, ram.tRP, ram.tRAS);
}
display_mode = DISPLAY_MODE_SPD;
} else {

View File

@ -102,9 +102,9 @@ typedef enum {
#define display_spec_mode(mode) \
prints(8,0, mode);
#define display_spec_ddr(freq, type, cl, rcd, rp, ras) \
printf(8,5, "%uMHz (%s-%u) CAS %u-%u-%u-%u", \
freq / 2, type, freq, cl, rcd, rp, ras);
#define display_spec_ddr(freq, type, cl, cl_dec, rcd, rp, ras) \
printf(8,5, "%uMHz (%s-%u) CAS %u%s-%u-%u-%u", \
freq / 2, type, freq, cl, cl_dec?".5":"", rcd, rp, ras);
#define display_spec_sdr(freq, type, cl, rcd, rp, ras) \
printf(8,5, "%uMHz (%s PC%u) CAS %u-%u-%u-%u", \

View File

@ -79,7 +79,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x003D, "Tektronix" },
// { 0x003E, "Oracle Corporation" },
// { 0x003F, "Silicon Storage Technology" },
// { 0x0040, "ProMos/Mosel Vitelic" },
{ 0x0040, "MOSEL" },
{ 0x0041, "Infineon" },
{ 0x0042, "Macronix" },
// { 0x0043, "Xerox" },
@ -161,7 +161,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x0111, "DATARAM" },
// { 0x0112, "United Microelectronics Corp" },
// { 0x0113, "TCSI" },
// { 0x0114, "Smart Modular" },
{ 0x0114, "Smart Modular" },
// { 0x0115, "Hughes Aircraft" },
// { 0x0116, "Lanstar Semiconductor" },
// { 0x0117, "Qlogic" },
@ -392,7 +392,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x027B, "Accelerant Networks" },
// { 0x027C, "Silicon Wave" },
// { 0x027D, "SandCraft" },
// { 0x027E, "Elpida" },
{ 0x027E, "Elpida" },
// { 0x0301, "Solectron" },
// { 0x0302, "Optosys Technologies" },
// { 0x0303, "Buffalo (Formerly Melco)" },
@ -407,14 +407,14 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x030C, "Elite Flash Storage" },
// { 0x030D, "Mysticom" },
// { 0x030E, "LightSand Communications" },
// { 0x030F, "ATI Technologies" },
{ 0x030F, "ATI" },
// { 0x0310, "Agere Systems" },
// { 0x0311, "NeoMagic" },
// { 0x0312, "AuroraNetics" },
{ 0x0313, "Golden Empire" },
{ 0x0313, "GEIL" },
{ 0x0314, "Mushkin" },
// { 0x0315, "Tioga Technologies" },
// { 0x0316, "Netlist" },
{ 0x0316, "Netlist" },
// { 0x0317, "TeraLogic" },
// { 0x0318, "Cicada Semiconductor" },
// { 0x0319, "Centon Electronics" },
@ -480,7 +480,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x0355, "Silverback Systems" },
// { 0x0356, "Jade Star Technologies" },
// { 0x0357, "Pijnenburg Securealink" },
// { 0x0358, "takeMS - Ultron AG" },
{ 0x0358, "takeMS" }, // Ultron AG
// { 0x0359, "Cambridge Silicon Radio" },
{ 0x035A, "Swissbit" },
// { 0x035B, "Nazomi Communications" },
@ -540,7 +540,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x0413, "Digital Communications Technology Inc" },
// { 0x0414, "Silicon-Based Technology" },
// { 0x0415, "Fulcrum Microsystems" },
// { 0x0416, "Positivo Informatica Ltd" },
{ 0x0416, "Positivo" },
// { 0x0417, "XIOtech Corporation" },
// { 0x0418, "PortalPlayer" },
// { 0x0419, "Zhiying Software" },
@ -585,7 +585,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x0440, "Bandspeed" },
// { 0x0441, "LeWiz Communications" },
// { 0x0442, "CPU Technology" },
// { 0x0443, "Ramaxel Technology" },
{ 0x0443, "Ramaxel" },
// { 0x0444, "DSP Group" },
// { 0x0445, "Axis Communications" },
// { 0x0446, "Legacy Electronics" },
@ -706,7 +706,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x053B, "Solid State System Co Ltd" },
// { 0x053C, "Kian Tech LLC" },
// { 0x053D, "Artimi" },
// { 0x053E, "Power Quotient International" },
{ 0x053E, "PQI" },
// { 0x053F, "Avago Technologies" },
// { 0x0540, "ADTechnology" },
// { 0x0541, "Sigma Designs" },
@ -730,20 +730,20 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x0553, "Velogix" },
// { 0x0554, "Montalvo Systems" },
// { 0x0555, "iVivity Inc" },
// { 0x0556, "Walton Chaintech" },
// { 0x0557, "AENEON" },
{ 0x0556, "Walton Chaintech" },
{ 0x0557, "AENEON" },
// { 0x0558, "Lorom Industrial Co Ltd" },
// { 0x0559, "Radiospire Networks" },
// { 0x055A, "Sensio Technologies Inc" },
// { 0x055B, "Nethra Imaging" },
// { 0x055C, "Hexon Technology Pte Ltd" },
{ 0x055C, "Hexon" },
// { 0x055D, "CompuStocx (CSX)" },
// { 0x055E, "Methode Electronics Inc" },
// { 0x055F, "Connect One Ltd" },
// { 0x0560, "Opulan Technologies" },
// { 0x0561, "Septentrio NV" },
// { 0x0562, "Goldenmars Technology Inc" },
// { 0x0563, "Kreton Corporation" },
{ 0x0563, "Kreton Corp." },
// { 0x0564, "Cochlear Ltd" },
// { 0x0565, "Altair Semiconductor" },
// { 0x0566, "NetEffect Inc" },
@ -835,7 +835,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x063E, "Wilocity" },
// { 0x063F, "Novafora Inc" },
// { 0x0640, "iKoa Corporation" },
// { 0x0641, "ASint Technology" },
{ 0x0641, "ASint" },
{ 0x0642, "Ramtron" },
// { 0x0643, "Plato Networks Inc" },
// { 0x0644, "IPtronics AS" },
@ -874,7 +874,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x0666, "Netronome" },
// { 0x0667, "Zenverge Inc" },
// { 0x0668, "N-trig Ltd" },
// { 0x0669, "SanMax Technologies Inc" },
{ 0x0669, "SanMax" },
// { 0x066A, "Contour Semiconductor Inc" },
{ 0x066B, "TwinMOS" },
// { 0x066C, "Silicon Systems Inc" },
@ -987,7 +987,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x075A, "Bestdon Technology Co Ltd" },
// { 0x075B, "Baysand Inc" },
// { 0x075C, "Uroad Technology Co Ltd" },
// { 0x075D, "Wilk Elektronik S.A." },
{ 0x075D, "Wilk Elektronik" },
// { 0x075E, "AAI" },
// { 0x075F, "Harman" },
// { 0x0760, "Berg Microelectronics Inc" },
@ -1038,12 +1038,12 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x0810, "Exelis" },
// { 0x0811, "Satixfy Ltd" },
// { 0x0812, "Galaxy Microsystems Ltd" },
// { 0x0813, "Gloway International Co Ltd" },
{ 0x0813, "Gloway" },
// { 0x0814, "Lab" },
// { 0x0815, "Smart Energy Instruments" },
// { 0x0816, "Approved Memory Corporation" },
// { 0x0817, "Axell Corporation" },
// { 0x0818, "Essencore Limited" },
{ 0x0818, "KLEVV" },
// { 0x0819, "Phytium" },
// { 0x081A, "Xi'an UniIC Semiconductors Co Ltd" },
// { 0x081B, "Ambiq Micro" },
@ -1208,7 +1208,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x093F, "Pegasus Semiconductor (Shanghai) Co" },
// { 0x0940, "Mythic Inc" },
// { 0x0941, "Elmos Semiconductor AG" },
// { 0x0942, "Kllisre" },
{ 0x0942, "Kllisre" },
// { 0x0943, "Shenzhen Winconway Technology" },
// { 0x0944, "Shenzhen Xingmem Technology Corp" },
// { 0x0945, "Gold Key Technology Co Ltd" },
@ -1261,7 +1261,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x0974, "Hyundai Inc" },
// { 0x0975, "EXCELERAM" },
// { 0x0976, "PsiKick" },
// { 0x0977, "Netac Technology Co Ltd" },
{ 0x0977, "Netac" },
{ 0x0978, "PCCOOLER" },
// { 0x0979, "Jiangsu Huacun Electronic Technology" },
// { 0x097A, "Shenzhen Micro Innovation Industry" },
@ -1335,7 +1335,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x0A42, "Thermaltake Technology Co Ltd" },
// { 0x0A43, "Shenzhen O?Yang Maile Technology Ltd" },
// { 0x0A44, "UPMEM" },
// { 0x0A45, "Chun Well Technology Holding Limited" },
{ 0x0A45, "Chun Well" },
// { 0x0A46, "Astera Labs Inc" },
// { 0x0A47, "Winconway" },
// { 0x0A48, "Advantech Co Ltd" },
@ -1384,7 +1384,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x0A73, "MCLogic Inc" },
// { 0x0A74, "Eorex Corporation" },
// { 0x0A75, "Arm Technology (China) Co Ltd" },
// { 0x0A76, "Lexar Co Limited" },
{ 0x0A76, "Lexar" },
// { 0x0A77, "QinetiQ Group plc" },
// { 0x0A78, "Exascend" },
// { 0x0A79, "Hong Kong Hyunion Electronics Co Ltd" },
@ -1410,7 +1410,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x0B0F, "Quadratica LLC" },
// { 0x0B10, "Anpec Electronics" },
// { 0x0B11, "Xi'an Morebeck Semiconductor Tech Co" },
// { 0x0B12, "Kingbank Technology Co Ltd" },
{ 0x0B12, "Kingbank" },
// { 0x0B13, "ITRenew Inc" },
// { 0x0B14, "Shenzhen Eaget Innovation Tech Ltd" },
// { 0x0B15, "Jazer" },
@ -1556,7 +1556,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x0C23, "Tangem AG" },
// { 0x0C24, "FuturePath Technology (Shenzhen) Co" },
// { 0x0C25, "RC Module" },
// { 0x0C26, "Timetec International Inc" },
{ 0x0C26, "Timetec" },
// { 0x0C27, "ICMAX Technologies Co Limited" },
// { 0x0C28, "Lynxi Technologies Ltd Co" },
// { 0x0C29, "Guangzhou Taisupanke Computer Equipment" },
@ -1611,7 +1611,7 @@ static const struct spd_jedec_manufacturer jep106[] = {
// { 0x0C5A, "Fraunhofer IPMS" },
// { 0x0C5B, "Shenzhen Daxinlang Electronic Tech Co" },
// { 0x0C5C, "Abacus Peripherals Private Limited" },
// { 0x0C5D, "OLOy Technology" },
{ 0x0C5D, "OLOy" },
// { 0x0C5E, "Wuhan P&S Semiconductor Co Ltd" },
// { 0x0C5F, "Sitrus Technology" },
// { 0x0C60, "AnHui Conner Storage Co Ltd" },

View File

@ -17,7 +17,7 @@
#define LINE_SPD 13
#define MAX_SPD_SLOT 8
ram_info ram = { 0, 0, 0, 0, 0, "N/A"};
ram_info ram = { 0, 0, 0, 0, 0, 0, "N/A"};
int smbdev, smbfun;
unsigned short smbusbase = 0;
@ -175,10 +175,11 @@ static void print_spdi(spd_info spdi, uint8_t lidx)
ram.freq = spdi.freq;
}
if (ram.tCL < spdi.tCL) {
ram.tCL = spdi.tCL;
ram.tRCD = spdi.tRCD;
ram.tRP = spdi.tRP;
ram.tRAS = spdi.tRAS;
ram.tCL = spdi.tCL;
ram.tCL_dec = spdi.tCL_dec;
ram.tRCD = spdi.tRCD;
ram.tRP = spdi.tRP;
ram.tRAS = spdi.tRAS;
}
}
@ -191,6 +192,7 @@ static spd_info parse_spd_ddr5(uint8_t slot_idx)
spdi.slot_num = slot_idx;
spdi.sku_len = 0;
spdi.module_size = 0;
spdi.tCL_dec = 0;
// Compute module size for symmetric & asymmetric configuration
for (int sbyte_adr = 1; sbyte_adr <= 2; sbyte_adr++) {
@ -248,11 +250,16 @@ static spd_info parse_spd_ddr5(uint8_t slot_idx)
sbyte = get_spd(slot_idx, (sbyte_adr * 4) + 2);
cur_rank /= 1U << (((sbyte >> 5) & 3) + 2);
sbyte = get_spd(slot_idx, 234);
// Package ranks per Channel
cur_rank *= 1U << ((sbyte >> 3) & 7);
// Add current rank to total package size
spdi.module_size += cur_rank;
// If not Asymmetrical, don't process the second rank
if ((get_spd(slot_idx, 234) >> 6) == 0) {
if ((sbyte >> 6) == 0) {
break;
}
}
@ -297,27 +304,28 @@ static spd_info parse_spd_ddr5(uint8_t slot_idx)
// CAS# Latency
tns = (uint16_t)get_spd(slot_idx, 718 + xmp_offset) << 8 |
(uint16_t)get_spd(slot_idx, 717 + xmp_offset);
spdi.tCL = (uint16_t)(tns/tCK + 0.5f);
spdi.tCL = (tns + tCK - DDR5_ROUNDING_FACTOR) / tCK;
spdi.tCL += spdi.tCL % 2; // if tCL is odd, round to upper even.
// RAS# to CAS# Latency
tns = (uint16_t)get_spd(slot_idx, 720 + xmp_offset) << 8 |
(uint16_t)get_spd(slot_idx, 719 + xmp_offset);
spdi.tRCD = (uint16_t)(tns/tCK + 0.5f);
spdi.tRCD = (tns + tCK - DDR5_ROUNDING_FACTOR) / tCK;
// RAS# Precharge
tns = (uint16_t)get_spd(slot_idx, 722 + xmp_offset) << 8 |
(uint16_t)get_spd(slot_idx, 721 + xmp_offset);
spdi.tRP = (uint16_t)(tns/tCK + 0.5f);
spdi.tRP = (tns + tCK - DDR5_ROUNDING_FACTOR) / tCK;
// Row Active Time
tns = (uint16_t)get_spd(slot_idx, 724 + xmp_offset) << 8 |
(uint16_t)get_spd(slot_idx, 723 + xmp_offset);
spdi.tRAS = (uint16_t)(tns/tCK + 0.5f);
spdi.tRAS = (tns + tCK - DDR5_ROUNDING_FACTOR) / tCK;
// Row Cycle Time
tns = (uint16_t)get_spd(slot_idx, 726 + xmp_offset) << 8 |
(uint16_t)get_spd(slot_idx, 725 + xmp_offset);
spdi.tRC = (uint16_t)(tns/tCK + 0.5f);
spdi.tRC = (tns + tCK - DDR5_ROUNDING_FACTOR) / tCK;
} else {
// --------------------
// JEDEC Specifications
@ -326,27 +334,28 @@ static spd_info parse_spd_ddr5(uint8_t slot_idx)
// CAS# Latency
tns = (uint16_t)get_spd(slot_idx, 31) << 8 |
(uint16_t)get_spd(slot_idx, 30);
spdi.tCL = (uint16_t)(tns/tCK + 0.5f);
spdi.tCL = (tns + tCK - DDR5_ROUNDING_FACTOR) / tCK;
spdi.tCL += spdi.tCL % 2;
// RAS# to CAS# Latency
tns = (uint16_t)get_spd(slot_idx, 33) << 8 |
(uint16_t)get_spd(slot_idx, 32);
spdi.tRCD = (uint16_t)(tns/tCK + 0.5f);
spdi.tRCD = (tns + tCK - DDR5_ROUNDING_FACTOR) / tCK;
// RAS# Precharge
tns = (uint16_t)get_spd(slot_idx, 35) << 8 |
(uint16_t)get_spd(slot_idx, 34);
spdi.tRP = (uint16_t)(tns/tCK + 0.5f);
spdi.tRP = (tns + tCK - DDR5_ROUNDING_FACTOR) / tCK;
// Row Active Time
tns = (uint16_t)get_spd(slot_idx, 37) << 8 |
(uint16_t)get_spd(slot_idx, 36);
spdi.tRAS = (uint16_t)(tns/tCK + 0.5f);
spdi.tRAS = (tns + tCK - DDR5_ROUNDING_FACTOR) / tCK;
// Row Cycle Time
tns = (uint16_t)get_spd(slot_idx, 39) << 8 |
(uint16_t)get_spd(slot_idx, 38);
spdi.tRC = (uint16_t)(tns/tCK + 0.5f);
spdi.tRC = (tns + tCK - DDR5_ROUNDING_FACTOR) / tCK;
}
// Module manufacturer
@ -387,6 +396,7 @@ static spd_info parse_spd_ddr4(uint8_t slot_idx)
spdi.type = "DDR4";
spdi.slot_num = slot_idx;
spdi.sku_len = 0;
spdi.tCL_dec = 0;
// Compute module size in MB with shifts
spdi.module_size = 1U << (
@ -400,31 +410,37 @@ static spd_info parse_spd_ddr4(uint8_t slot_idx)
spdi.hasECC = (((get_spd(slot_idx, 13) >> 3) & 1) == 1);
// Module max clock
float tns, tckns, ramfreq;
float tns, tCK, ramfreq, fround;
if (get_spd(slot_idx, 384) == 0x0C && get_spd(slot_idx, 385) == 0x4A) {
// Max XMP
tckns = (uint8_t)get_spd(slot_idx, 396) * 0.125f +
(int8_t)get_spd(slot_idx, 431) * 0.001f;
ramfreq = 1.0f / tckns * 2.0f * 1000.0f;
spdi.freq = (ramfreq+50)/100;
spdi.freq *= 100;
tCK = (uint8_t)get_spd(slot_idx, 396) * 0.125f +
(int8_t)get_spd(slot_idx, 431) * 0.001f;
spdi.XMP = 2;
} else {
// Max JEDEC
tckns = (uint8_t)get_spd(slot_idx, 18) * 0.125f +
(int8_t)get_spd(slot_idx, 125) * 0.001f;
ramfreq = 1.0f / tckns * 2.0f * 1000.0f;
spdi.freq = (uint16_t)ramfreq;
tCK = (uint8_t)get_spd(slot_idx, 18) * 0.125f +
(int8_t)get_spd(slot_idx, 125) * 0.001f;
spdi.XMP = 0;
}
ramfreq = 1.0f / tCK * 2.0f * 1000.0f;
// Round DRAM Freq to nearest x00/x33/x66
fround = ((int)(ramfreq * 0.01 + .5) / 0.01) - ramfreq;
ramfreq += fround;
if (fround < -16.5) {
ramfreq += 33;
} else if (fround > 16.5) {
ramfreq -= 34;
}
spdi.freq = ramfreq;
// Module Timings
if (spdi.XMP == 2) {
// ------------------
@ -434,28 +450,28 @@ static spd_info parse_spd_ddr4(uint8_t slot_idx)
// CAS# Latency
tns = (uint8_t)get_spd(slot_idx, 401) * 0.125f +
(int8_t)get_spd(slot_idx, 430) * 0.001f;
spdi.tCL = (uint16_t)(tns/tckns);
spdi.tCL = (uint16_t)(tns/tCK + ROUNDING_FACTOR);
// RAS# to CAS# Latency
tns = (uint8_t)get_spd(slot_idx, 402) * 0.125f +
(int8_t)get_spd(slot_idx, 429) * 0.001f;
spdi.tRCD = (uint16_t)(tns/tckns);
spdi.tRCD = (uint16_t)(tns/tCK + ROUNDING_FACTOR);
// RAS# Precharge
tns = (uint8_t)get_spd(slot_idx, 403) * 0.125f +
(int8_t)get_spd(slot_idx, 428) * 0.001f;
spdi.tRP = (uint16_t)(tns/tckns);
spdi.tRP = (uint16_t)(tns/tCK + ROUNDING_FACTOR);
// Row Active Time
tns = (uint8_t)get_spd(slot_idx, 405) * 0.125f +
(int8_t)get_spd(slot_idx, 427) * 0.001f +
(uint8_t)(get_spd(slot_idx, 404) & 0x0F) * 32.0f;
spdi.tRAS = (uint16_t)(tns/tckns);
spdi.tRAS = (uint16_t)(tns/tCK + ROUNDING_FACTOR);
// Row Cycle Time
tns = (uint8_t)get_spd(slot_idx, 406) * 0.125f +
(uint8_t)(get_spd(slot_idx, 404) >> 4) * 32.0f;
spdi.tRC = (uint16_t)(tns/tckns);
spdi.tRC = (uint16_t)(tns/tCK + ROUNDING_FACTOR);
} else {
// --------------------
// JEDEC Specifications
@ -464,27 +480,27 @@ static spd_info parse_spd_ddr4(uint8_t slot_idx)
// CAS# Latency
tns = (uint8_t)get_spd(slot_idx, 24) * 0.125f +
(int8_t)get_spd(slot_idx, 123) * 0.001f;
spdi.tCL = (uint16_t)(tns/tckns);
spdi.tCL = (uint16_t)(tns/tCK + ROUNDING_FACTOR);
// RAS# to CAS# Latency
tns = (uint8_t)get_spd(slot_idx, 25) * 0.125f +
(int8_t)get_spd(slot_idx, 122) * 0.001f;
spdi.tRCD = (uint16_t)(tns/tckns);
spdi.tRCD = (uint16_t)(tns/tCK + ROUNDING_FACTOR);
// RAS# Precharge
tns = (uint8_t)get_spd(slot_idx, 26) * 0.125f +
(int8_t)get_spd(slot_idx, 121) * 0.001f;
spdi.tRP = (uint16_t)(tns/tckns);
spdi.tRP = (uint16_t)(tns/tCK + ROUNDING_FACTOR);
// Row Active Time
tns = (uint8_t)get_spd(slot_idx, 28) * 0.125f +
(uint8_t)(get_spd(slot_idx, 27) & 0x0F) * 32.0f;
spdi.tRAS = (uint16_t)(tns/tckns);
spdi.tRAS = (uint16_t)(tns/tCK + ROUNDING_FACTOR);
// Row Cycle Time
tns = (uint8_t)get_spd(slot_idx, 29) * 0.125f +
(uint8_t)(get_spd(slot_idx, 27) >> 4) * 32.0f;
spdi.tRC = (uint16_t)(tns/tckns);
spdi.tRC = (uint16_t)(tns/tCK + ROUNDING_FACTOR);
}
// Module manufacturer
@ -526,6 +542,7 @@ static spd_info parse_spd_ddr3(uint8_t slot_idx)
spdi.slot_num = slot_idx;
spdi.sku_len = 0;
spdi.XMP = 0;
spdi.tCL_dec = 0;
// Compute module size in MB with shifts
spdi.module_size = 1U << (
@ -538,9 +555,16 @@ static spd_info parse_spd_ddr3(uint8_t slot_idx)
spdi.hasECC = (((get_spd(slot_idx, 8) >> 3) & 1) == 1);
uint8_t tck = get_spd(slot_idx, 12);
uint8_t tck2 = get_spd(slot_idx, 221);
if (get_spd(slot_idx, 176) == 0x0C && get_spd(slot_idx, 177) == 0x4A) {
tck = get_spd(slot_idx, 186);
// Check if profile #2 is faster
if (tck2 > 5 && tck2 < tck) {
tck = tck2;
}
spdi.XMP = 1;
}
@ -560,6 +584,10 @@ static spd_info parse_spd_ddr3(uint8_t slot_idx)
break;
case 10:
spdi.freq = 1600;
// Quirk for early Kingston DDR3-1866 with XMP < 1.1
if (spdi.XMP == 1 && get_spd(slot_idx, 181) == 0x0E) {
spdi.freq = 1866;
}
break;
case 9:
spdi.freq = 1866;
@ -585,57 +613,57 @@ static spd_info parse_spd_ddr3(uint8_t slot_idx)
// CAS# Latency
tns = get_spd(slot_idx, 187);
spdi.tCL = (uint16_t)(tns/tckns);
spdi.tCL = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
// RAS# to CAS# Latency
tns = get_spd(slot_idx, 192);
spdi.tRCD = (uint16_t)(tns/tckns);
spdi.tRCD = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
// RAS# Precharge
tns = get_spd(slot_idx, 191);
spdi.tRP = (uint16_t)(tns/tckns);
spdi.tRP = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
// Row Active Time
tns = (uint16_t)(get_spd(slot_idx, 194) & 0xF0) << 4 |
get_spd(slot_idx, 195);
tns = (uint16_t)((get_spd(slot_idx, 194) & 0xF0) << 3 |
get_spd(slot_idx, 195));
;
spdi.tRAS = (uint16_t)(tns/tckns);
spdi.tRAS = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
// Row Cycle Time
tns = (uint16_t)(get_spd(slot_idx, 194) & 0x0F) << 8 |
get_spd(slot_idx, 196);
spdi.tRC = (uint16_t)(tns/tckns);
tns = (uint16_t)((get_spd(slot_idx, 194) & 0x0F) << 8 |
get_spd(slot_idx, 196));
spdi.tRC = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
} else {
// --------------------
// JEDEC Specifications
// --------------------
tckns = (uint8_t)get_spd(slot_idx, 12) * 0.125f +
(int8_t)get_spd(slot_idx, 134) * 0.001f;
(int8_t)get_spd(slot_idx, 34) * 0.001f;
// CAS# Latency
tns = (uint8_t)get_spd(slot_idx, 16) * 0.125f +
(int8_t)get_spd(slot_idx, 35) * 0.001f;
spdi.tCL = (uint16_t)(tns/tckns);
spdi.tCL = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
// RAS# to CAS# Latency
tns = (uint8_t)get_spd(slot_idx, 18) * 0.125f +
(int8_t)get_spd(slot_idx, 36) * 0.001f;
spdi.tRCD = (uint16_t)(tns/tckns);
spdi.tRCD = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
// RAS# Precharge
tns = (uint8_t)get_spd(slot_idx, 20) * 0.125f +
(int8_t)get_spd(slot_idx, 37) * 0.001f;
spdi.tRP = (uint16_t)(tns/tckns);
spdi.tRP = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
// Row Active Time
tns = (uint8_t)get_spd(slot_idx, 22) * 0.125f +
(uint8_t)(get_spd(slot_idx, 21) & 0x0F) * 32.0f;
spdi.tRAS = (uint16_t)(tns/tckns);
spdi.tRAS = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
// Row Cycle Time
tns = (uint8_t)get_spd(slot_idx, 23) * 0.125f +
(uint8_t)(get_spd(slot_idx, 21) >> 4) * 32.0f;
spdi.tRC = (uint16_t)(tns/tckns);
(uint8_t)(get_spd(slot_idx, 21) >> 4) * 32.0f + 1;
spdi.tRC = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
}
// Module manufacturer
@ -676,6 +704,7 @@ static spd_info parse_spd_ddr2(uint8_t slot_idx)
spdi.slot_num = slot_idx;
spdi.sku_len = 0;
spdi.XMP = 0;
spdi.tCL_dec = 0;
// Compute module size in MB
switch (get_spd(slot_idx, 31)) {
@ -750,23 +779,22 @@ static spd_info parse_spd_ddr2(uint8_t slot_idx)
for (int shft = 0; shft < 7; shft++) {
if ((tbyte >> shft) & 1) {
spdi.tCL = shft;
break;
}
}
// RAS# to CAS# Latency
tbyte = get_spd(slot_idx, 111 + epp_offset);
tns = ((tbyte & 0xFC) >> 2) + (tbyte & 0x3) * 0.25f;
spdi.tRCD = (uint16_t)(tns/tckns);
spdi.tRCD = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
// RAS# Precharge
tbyte = get_spd(slot_idx, 112 + epp_offset);
tns = ((tbyte & 0xFC) >> 2) + (tbyte & 0x3) * 0.25f;
spdi.tRP = (uint16_t)(tns/tckns);
spdi.tRP = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
// Row Active Time
tns = get_spd(slot_idx, 113 + epp_offset);
spdi.tRAS = (uint16_t)(tns/tckns);
spdi.tRAS = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
// Row Cycle Time
spdi.tRC = 0;
@ -777,23 +805,22 @@ static spd_info parse_spd_ddr2(uint8_t slot_idx)
for (int shft = 0; shft < 7; shft++) {
if ((tbyte >> shft) & 1) {
spdi.tCL = shft;
break;
}
}
// RAS# to CAS# Latency
tbyte = get_spd(slot_idx, 29);
tns = ((tbyte & 0xFC) >> 2) + (tbyte & 0x3) * 0.25f;
spdi.tRCD = (uint16_t)(tns/tckns);
spdi.tRCD = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
// RAS# Precharge
tbyte = get_spd(slot_idx, 27);
tns = ((tbyte & 0xFC) >> 2) + (tbyte & 0x3) * 0.25f;
spdi.tRP = (uint16_t)(tns/tckns);
spdi.tRP = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
// Row Active Time
tns = get_spd(slot_idx, 30);
spdi.tRAS = (uint16_t)(tns/tckns);
spdi.tRAS = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
// Row Cycle Time
spdi.tRC = 0;
@ -888,23 +915,29 @@ static spd_info parse_spd_ddr(uint8_t slot_idx)
spdi.freq = (uint16_t)(1.0f / tckns * 1000.0f * 2.0f);
// Module Timings
spdi.tCL_dec = 0;
uint8_t spd_byte18 = get_spd(slot_idx, 18);
for (int shft = 0; shft < 7; shft++) {
if ((spd_byte18 >> shft) & 1) {
spdi.tCL = 1.0f + shft * 0.5f; // TODO: .5 CAS
break;
spdi.tCL = 1.0f + shft * 0.5f;
// Check tCL decimal (x.5 CAS)
if (shft == 1 || shft == 3 || shft == 5) {
spdi.tCL_dec = 5;
} else {
spdi.tCL_dec = 0;
}
}
}
tns = (get_spd(slot_idx, 29) >> 2) +
(get_spd(slot_idx, 29) & 0x3) * 0.25f;
spdi.tRCD = (uint16_t)(tns/tckns);
spdi.tRCD = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
tns = (get_spd(slot_idx, 27) >> 2) +
(get_spd(slot_idx, 27) & 0x3) * 0.25f;
spdi.tRP = (uint16_t)(tns/tckns);
spdi.tRP = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
spdi.tRAS = (uint16_t)(get_spd(slot_idx, 30)/tckns);
spdi.tRAS = (uint16_t)((float)get_spd(slot_idx, 30)/tckns + ROUNDING_FACTOR);
spdi.tRC = 0;
// Module manufacturer
@ -1000,6 +1033,7 @@ static spd_info parse_spd_rdram(uint8_t slot_idx)
// Module Timings
spdi.tCL = get_spd(slot_idx, 14);
spdi.tCL_dec = 0;
spdi.tRCD = get_spd(slot_idx, 12);
spdi.tRP = get_spd(slot_idx, 10);
spdi.tRAS = get_spd(slot_idx, 11);
@ -1052,6 +1086,7 @@ static spd_info parse_spd_sdram(uint8_t slot_idx)
spdi.slot_num = slot_idx;
spdi.sku_len = 0;
spdi.XMP = 0;
spdi.tCL_dec = 0;
uint8_t spd_byte3 = get_spd(slot_idx, 3) & 0x0F; // Number of Row Addresses (2 x 4 bits, upper part used if asymmetrical banking used)
uint8_t spd_byte4 = get_spd(slot_idx, 4) & 0x0F; // Number of Column Addresses (2 x 4 bits, upper part used if asymmetrical banking used)
@ -1085,17 +1120,16 @@ static spd_info parse_spd_sdram(uint8_t slot_idx)
for (int shft = 0; shft < 7; shft++) {
if ((spd_byte18 >> shft) & 1) {
spdi.tCL = shft + 1;
break;
}
}
tns = get_spd(slot_idx, 29);
spdi.tRCD = (uint16_t)(tns/tckns);
spdi.tRCD = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
tns = get_spd(slot_idx, 27);
spdi.tRP = (uint16_t)(tns/tckns);
spdi.tRP = (uint16_t)(tns/tckns + ROUNDING_FACTOR);
spdi.tRAS = (uint16_t)(get_spd(slot_idx, 30)/tckns);
spdi.tRAS = (uint16_t)(get_spd(slot_idx, 30)/tckns + ROUNDING_FACTOR);
spdi.tRC = 0;
// Module manufacturer

View File

@ -84,6 +84,15 @@
#define NVSMBSTS_RES 0x20
#define NVSMBSTS_STATUS 0x1f
/** Rounding factors for timing computation
*
* These factors are used as a configurable CEIL() function
* to get the upper int from a float past a specific decimal point.
*/
#define DDR5_ROUNDING_FACTOR 30
#define ROUNDING_FACTOR 0.9f
struct pci_smbus_controller {
unsigned vendor;
unsigned device;
@ -104,6 +113,7 @@ typedef struct spd_infos {
uint8_t fab_year;
uint8_t fab_week;
uint16_t tCL;
uint8_t tCL_dec;
uint16_t tRCD;
uint16_t tRP;
uint16_t tRAS;
@ -113,6 +123,7 @@ typedef struct spd_infos {
typedef struct ram_infos {
uint16_t freq;
uint16_t tCL;
uint8_t tCL_dec;
uint16_t tRCD;
uint16_t tRP;
uint16_t tRAS;