Rework Line 9. Add DDR1->DDR5 Timing Detection to display on this line

This commit is contained in:
Sam Demeulemeester 2022-04-14 01:40:31 +02:00 committed by Sam Demeulemeester
parent 979b85548d
commit 6fca9bedc9
5 changed files with 397 additions and 54 deletions

View File

@ -66,6 +66,8 @@ static bool timed_update_done = false; // update cycle status
int scroll_message_row;
int display_mode = 0; // RAM Info from: 0 = N/A - 1 = SPD - 2 = IMC
int max_cpu_temp = 0;
//------------------------------------------------------------------------------
@ -217,6 +219,31 @@ void post_display_init(void)
{
print_smbios_startup_info();
print_smbus_startup_info();
if(false) {
// Try to get RAM information from IMC (TODO)
printf(8,0, "IMC: %uMHz (%s-%u) CAS %u-%u-%u-%u", ram.freq / 2
, ram.type
, ram.freq
, ram.tCL
, ram.tRCD
, ram.tRP
, ram.tRAS);
display_mode = 2;
} else if (ram.freq > 0 && ram.tCL > 1) {
// If not available, grab max memory specs from SPD
printf(8,0, "RAM: %uMHz (%s-%u) CAS %u-%u-%u-%u", ram.freq / 2
, ram.type
, ram.freq
, ram.tCL
, ram.tRCD
, ram.tRP
, ram.tRAS);
display_mode = 1;
} else {
// If nothing avilable, fallback to "Using Core" Display
display_mode = 0;
}
}
void display_start_run(void)

View File

@ -73,7 +73,7 @@
prints(8, 7, "Core #"); \
printi(8, 13, cpu_num, 3, false, true)
#define display_all_active \
#define display_all_active() \
prints(8, 7, "All Cores")
#define display_spinner(spin_state) \
@ -182,6 +182,8 @@
extern int scroll_message_row;
extern int display_mode;
void display_init(void);
void display_cpu_topology(void);

View File

@ -346,11 +346,15 @@ static void test_all_windows(int my_cpu)
if (!dummy_run) {
if (parallel_test) {
num_active_cpus = num_enabled_cpus;
display_all_active;
if(display_mode == 0) {
display_all_active();
}
} else {
if(display_mode == 0) {
display_active_cpu(my_cpu);
}
}
}
barrier_reset(run_barrier, num_active_cpus);
}

View File

@ -17,6 +17,8 @@
#define LINE_SPD 13
#define MAX_SPD_SLOT 8
ram_info ram = { 0, 0, 0, 0, 0, "N/A"};
int smbdev, smbfun;
unsigned short smbusbase;
@ -106,6 +108,7 @@ void print_smbus_startup_info(void) {
uint8_t spdidx = 0, spd_line_idx = 0;
spd_info curspd;
ram.freq = 0;
index = find_smb_controller();
@ -170,6 +173,13 @@ static void print_spdi(spd_info spdi, uint8_t lidx)
curcol = prints(LINE_SPD+lidx, curcol, " ECC");
}
// Print XMP/EPP Status
if (spdi.XMP > 0 && spdi.XMP < 20) {
curcol = prints(LINE_SPD+lidx, curcol, " XMP");
} else if (spdi.XMP == 20) {
curcol = prints(LINE_SPD+lidx, curcol, " EPP");
}
// Print Manufacturer from JEDEC106
for (i = 0; i < JEP106_CNT ; i++) {
@ -194,9 +204,16 @@ static void print_spdi(spd_info spdi, uint8_t lidx)
curcol = printf(LINE_SPD+lidx, curcol, " (W%02i'%02i)", spdi.fab_week, spdi.fab_year);
}
// Print XMP Status
if(spdi.XMP > 0) {
curcol = prints(LINE_SPD+lidx, curcol, " *XMP*");
// Populate global ram var
ram.type = spdi.type;
if (ram.freq == 0 || ram.freq > spdi.freq) {
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;
}
}
@ -275,7 +292,7 @@ static spd_info parse_spd_ddr5(uint8_t smb_idx, uint8_t slot_idx)
}
// Compute Frequency (including XMP)
uint16_t tCK;
uint16_t tCK, tns;
spdi.XMP = ((get_spd(smb_idx, slot_idx, 640) == 0x0C && get_spd(smb_idx, slot_idx, 641) == 0x4A)) ? 3 : 0;
@ -292,11 +309,72 @@ static spd_info parse_spd_ddr5(uint8_t smb_idx, uint8_t slot_idx)
spdi.freq = (float)(1.0f / tCK * 2.0f * 1000.0f * 1000.0f);
spdi.freq = (spdi.freq + 50) / 100 * 100;
// Module Timings
if(spdi.XMP == 3) {
// ------------------
// XMP Specifications
// ------------------
// CAS# Latency
tns = (uint16_t)get_spd(smb_idx, slot_idx, 718) << 8 |
(uint16_t)get_spd(smb_idx, slot_idx, 717);
spdi.tCL = tns / tCK;
// RAS# to CAS# Latency
tns = (uint16_t)get_spd(smb_idx, slot_idx, 720) << 8 |
(uint16_t)get_spd(smb_idx, slot_idx, 719);
spdi.tRCD = (uint16_t)(tns/tCK);
// RAS# Precharge
tns = (uint16_t)get_spd(smb_idx, slot_idx, 722) << 8 |
(uint16_t)get_spd(smb_idx, slot_idx, 721);
spdi.tRP= (uint16_t)(tns/tCK);
// Row Active Time
tns = (uint16_t)get_spd(smb_idx, slot_idx, 724) << 8 |
(uint16_t)get_spd(smb_idx, slot_idx, 723);
spdi.tRAS = (uint16_t)(tns/tCK);
// Row Cycle Time
tns = (uint16_t)get_spd(smb_idx, slot_idx, 726) << 8 |
(uint16_t)get_spd(smb_idx, slot_idx, 725);
spdi.tRC = (uint16_t)(tns/tCK);
} else {
// --------------------
// JEDEC Specifications
// --------------------
// CAS# Latency
tns = (uint16_t)get_spd(smb_idx, slot_idx, 31) << 8 |
(uint16_t)get_spd(smb_idx, slot_idx, 30);
spdi.tCL = tns / tCK;
// RAS# to CAS# Latency
tns = (uint16_t)get_spd(smb_idx, slot_idx, 33) << 8 |
(uint16_t)get_spd(smb_idx, slot_idx, 32);
spdi.tRCD = (uint16_t)(tns/tCK);
// RAS# Precharge
tns = (uint16_t)get_spd(smb_idx, slot_idx, 35) << 8 |
(uint16_t)get_spd(smb_idx, slot_idx, 34);
spdi.tRP= (uint16_t)(tns/tCK);
// Row Active Time
tns = (uint16_t)get_spd(smb_idx, slot_idx, 37) << 8 |
(uint16_t)get_spd(smb_idx, slot_idx, 36);
spdi.tRAS = (uint16_t)(tns/tCK);
// Row Cycle Time
tns = (uint16_t)get_spd(smb_idx, slot_idx, 39) << 8 |
(uint16_t)get_spd(smb_idx, slot_idx, 38);
spdi.tRC = (uint16_t)(tns/tCK);
}
// Module manufacturer
spdi.jedec_code = (get_spd(smb_idx, slot_idx, 512) & 0x1F) << 8;
spdi.jedec_code |= get_spd(smb_idx, slot_idx, 513) & 0x7F;
// Module SKU
// Module Timings
uint8_t sku_byte;
for (int j = 0; j <= 29; j++) {
sku_byte = get_spd(smb_idx, slot_idx, 521+j);
@ -341,47 +419,92 @@ static spd_info parse_spd_ddr4(uint8_t smb_idx, uint8_t slot_idx)
spdi.hasECC = (((get_spd(smb_idx, slot_idx, 13) >> 3) & 1) == 1);
uint8_t tck = get_spd(smb_idx, slot_idx, 18);
// Module max clock
float tns, tckns, ramfreq;
if (get_spd(smb_idx, slot_idx, 384) == 0x0C && get_spd(smb_idx, slot_idx, 385) == 0x4A) {
// Max XMP
uint8_t tck_mtb = get_spd(smb_idx, slot_idx, 396);
int8_t tck_ftb = get_spd(smb_idx, slot_idx, 431);
tckns = (uint8_t)get_spd(smb_idx, slot_idx, 396) * 0.125f +
(int8_t)get_spd(smb_idx, slot_idx, 431) * 0.001f;
float tckavg = 1.0f / ((tck_mtb * 0.125f) + (tck_ftb * 0.001f)) * 2.0f * 1000.0f;
ramfreq = 1.0f / tckns * 2.0f * 1000.0f;
spdi.freq = (tckavg+50)/100;
spdi.freq = (ramfreq+50)/100;
spdi.freq *= 100;
spdi.XMP = 2;
} else {
// Max JEDEC
spdi.XMP = 0;
tckns = (uint8_t)get_spd(smb_idx, slot_idx, 18) * 0.125f +
(int8_t)get_spd(smb_idx, slot_idx, 125) * 0.001f;
switch(tck) {
default:
spdi.freq = 0;
break;
case 10:
spdi.freq = 1600;
break;
case 9:
spdi.freq = 1866;
break;
case 8:
spdi.freq = 2133;
break;
case 7:
spdi.freq = 2400;
break;
case 6:
spdi.freq = 2666;
break;
case 5:
spdi.freq = 3200;
break;
ramfreq = 1.0f / tckns * 2.0f * 1000.0f;
spdi.freq = (uint16_t)ramfreq;
spdi.XMP = 0;
}
// Module Timings
if (spdi.XMP == 2) {
// ------------------
// XMP Specifications
// ------------------
// CAS# Latency
tns = (uint8_t)get_spd(smb_idx, slot_idx, 401) * 0.125f +
(int8_t)get_spd(smb_idx, slot_idx, 430) * 0.001f;
spdi.tCL = (uint16_t)(tns/tckns);
// RAS# to CAS# Latency
tns = (uint8_t)get_spd(smb_idx, slot_idx, 402) * 0.125f +
(int8_t)get_spd(smb_idx, slot_idx, 429) * 0.001f;
spdi.tRCD = (uint16_t)(tns/tckns);
// RAS# Precharge
tns = (uint8_t)get_spd(smb_idx, slot_idx, 403) * 0.125f +
(int8_t)get_spd(smb_idx, slot_idx, 428) * 0.001f;
spdi.tRP= (uint16_t)(tns/tckns);
// Row Active Time
tns = (uint8_t)get_spd(smb_idx, slot_idx, 405) * 0.125f +
(int8_t)get_spd(smb_idx, slot_idx, 427) * 0.001f +
(uint8_t)(get_spd(smb_idx, slot_idx, 404) & 0x0F) * 32.0f;
spdi.tRAS = (uint16_t)(tns/tckns);
// Row Cycle Time
tns = (uint8_t)get_spd(smb_idx, slot_idx, 406) * 0.125f +
(uint8_t)(get_spd(smb_idx, slot_idx, 404) >> 4) * 32.0f;
spdi.tRC = (uint16_t)(tns/tckns);
} else {
// --------------------
// JEDEC Specifications
// --------------------
// CAS# Latency
tns = (uint8_t)get_spd(smb_idx, slot_idx, 24) * 0.125f +
(int8_t)get_spd(smb_idx, slot_idx, 123) * 0.001f;
spdi.tCL = (uint16_t)(tns/tckns);
// RAS# to CAS# Latency
tns = (uint8_t)get_spd(smb_idx, slot_idx, 25) * 0.125f +
(int8_t)get_spd(smb_idx, slot_idx, 122) * 0.001f;
spdi.tRCD = (uint16_t)(tns/tckns);
// RAS# Precharge
tns = (uint8_t)get_spd(smb_idx, slot_idx, 26) * 0.125f +
(int8_t)get_spd(smb_idx, slot_idx, 121) * 0.001f;
spdi.tRP= (uint16_t)(tns/tckns);
// Row Active Time
tns = (uint8_t)get_spd(smb_idx, slot_idx, 28) * 0.125f +
(uint8_t)(get_spd(smb_idx, slot_idx, 27) & 0x0F) * 32.0f;
spdi.tRAS = (uint16_t)(tns/tckns);
// Row Cycle Time
tns = (uint8_t)get_spd(smb_idx, slot_idx, 29) * 0.125f +
(uint8_t)(get_spd(smb_idx, slot_idx, 27) >> 4) * 32.0f;
spdi.tRC = (uint16_t)(tns/tckns);
}
// Module manufacturer
@ -471,6 +594,69 @@ static spd_info parse_spd_ddr3(uint8_t smb_idx, uint8_t slot_idx)
break;
}
// Module Timings
float tckns, tns;
if (spdi.XMP == 1) {
// ------------------
// XMP Specifications
// ------------------
tckns = get_spd(smb_idx, slot_idx, 186);
// CAS# Latency
tns = get_spd(smb_idx, slot_idx, 187);
spdi.tCL = (uint16_t)(tns/tckns);
// RAS# to CAS# Latency
tns = get_spd(smb_idx, slot_idx, 192);
spdi.tRCD = (uint16_t)(tns/tckns);
// RAS# Precharge
tns = get_spd(smb_idx, slot_idx, 191);
spdi.tRP= (uint16_t)(tns/tckns);
// Row Active Time
tns = (uint16_t)(get_spd(smb_idx, slot_idx, 194) & 0xF0) << 4 |
get_spd(smb_idx, slot_idx, 195);
;
spdi.tRAS = (uint16_t)(tns/tckns);
// Row Cycle Time
tns = (uint16_t)(get_spd(smb_idx, slot_idx, 194) & 0x0F) << 8 |
get_spd(smb_idx, slot_idx, 196);
spdi.tRC = (uint16_t)(tns/tckns);
} else {
// --------------------
// JEDEC Specifications
// --------------------
tckns = (uint8_t)get_spd(smb_idx, slot_idx, 12) * 0.125f +
(int8_t)get_spd(smb_idx, slot_idx, 134) * 0.001f;
// CAS# Latency
tns = (uint8_t)get_spd(smb_idx, slot_idx, 16) * 0.125f +
(int8_t)get_spd(smb_idx, slot_idx, 35) * 0.001f;
spdi.tCL = (uint16_t)(tns/tckns);
// RAS# to CAS# Latency
tns = (uint8_t)get_spd(smb_idx, slot_idx, 18) * 0.125f +
(int8_t)get_spd(smb_idx, slot_idx, 36) * 0.001f;
spdi.tRCD = (uint16_t)(tns/tckns);
// RAS# Precharge
tns = (uint8_t)get_spd(smb_idx, slot_idx, 20) * 0.125f +
(int8_t)get_spd(smb_idx, slot_idx, 37) * 0.001f;
spdi.tRP= (uint16_t)(tns/tckns);
// Row Active Time
tns = (uint8_t)get_spd(smb_idx, slot_idx, 22) * 0.125f +
(uint8_t)(get_spd(smb_idx, slot_idx, 21) & 0x0F) * 32.0f;
spdi.tRAS = (uint16_t)(tns/tckns);
// Row Cycle Time
tns = (uint8_t)get_spd(smb_idx, slot_idx, 23) * 0.125f +
(uint8_t)(get_spd(smb_idx, slot_idx, 21) >> 4) * 32.0f;
spdi.tRC = (uint16_t)(tns/tckns);
}
// Module manufacturer
spdi.jedec_code= (get_spd(smb_idx, slot_idx, 117) & 0x1F) << 8;
spdi.jedec_code |= get_spd(smb_idx, slot_idx, 118) & 0x7F;
@ -542,9 +728,94 @@ static spd_info parse_spd_ddr2(uint8_t smb_idx, uint8_t slot_idx)
spdi.hasECC = ((get_spd(smb_idx, slot_idx, 11) >> 1) == 1);
float tckns, tns;
uint8_t tbyte;
// Module EPP Detection (we only support Full profiles
uint8_t epp_offset = 0;
if (get_spd(smb_idx, slot_idx, 99) == 0x6D && get_spd(smb_idx, slot_idx, 102) == 0xB1) {
epp_offset = (get_spd(smb_idx, slot_idx, 103) & 0x3) * 12;
tbyte = get_spd(smb_idx, slot_idx, 109 + epp_offset);
spdi.XMP = 20;
} else {
tbyte = get_spd(smb_idx, slot_idx, 9);
}
// Module speed
uint8_t spd_byte9 = get_spd(smb_idx, slot_idx, 9);
spdi.freq = (float)(1.0f / (((spd_byte9 >> 4) * 10.0f) + (spd_byte9 & 0xF)) * 10000.0f * 2.0f);
tckns = (tbyte & 0xF0) >> 4;
tbyte &= 0xF;
if (tbyte < 10) {
tckns += (tbyte & 0xF) * 0.1f;
} else if (tbyte == 10) {
tckns += 0.25f;
} else if (tbyte == 11) {
tckns += 0.33f;
} else if (tbyte == 12) {
tckns += 0.66f;
} else if (tbyte == 13) {
tckns += 0.75f;
} else if (tbyte == 14) { // EPP Specific
tckns += 0.875f;
}
spdi.freq = (float)(1.0f / tckns * 1000.0f * 2.0f);
if (spdi.XMP == 20) {
// Module Timings (EPP)
// CAS# Latency
tbyte = get_spd(smb_idx, slot_idx, 110 + epp_offset);
for (int shft = 0; shft < 7; shft++) {
if ((tbyte >> shft) & 1) {
spdi.tCL = shft;
break;
}
}
// RAS# to CAS# Latency
tbyte = get_spd(smb_idx, slot_idx, 111 + epp_offset);
tns = ((tbyte & 0xFC) >> 2) + (tbyte & 0x3) * 0.25f;
spdi.tRCD = (uint16_t)(tns/tckns);
// RAS# Precharge
tbyte = get_spd(smb_idx, slot_idx, 112 + epp_offset);
tns = ((tbyte & 0xFC) >> 2) + (tbyte & 0x3) * 0.25f;
spdi.tRP= (uint16_t)(tns/tckns);
// Row Active Time
tns = get_spd(smb_idx, slot_idx, 113 + epp_offset);
spdi.tRAS = (uint16_t)(tns/tckns);
// Row Cycle Time
tns = 0;
} else {
// Module Timings (JEDEC)
// CAS# Latency
tbyte = get_spd(smb_idx, slot_idx, 18);
for (int shft = 0; shft < 7; shft++) {
if ((tbyte >> shft) & 1) {
spdi.tCL = shft;
break;
}
}
// RAS# to CAS# Latency
tbyte = get_spd(smb_idx, slot_idx, 29);
tns = ((tbyte & 0xFC) >> 2) + (tbyte & 0x3) * 0.25f;
spdi.tRCD = (uint16_t)(tns/tckns);
// RAS# Precharge
tbyte = get_spd(smb_idx, slot_idx, 27);
tns = ((tbyte & 0xFC) >> 2) + (tbyte & 0x3) * 0.25f;
spdi.tRP= (uint16_t)(tns/tckns);
// Row Active Time
tns = get_spd(smb_idx, slot_idx, 30);
spdi.tRAS = (uint16_t)(tns/tckns);
// Row Cycle Time
tns = 0;
}
// Module manufacturer
uint8_t contcode;
@ -627,8 +898,31 @@ static spd_info parse_spd_ddr(uint8_t smb_idx, uint8_t slot_idx)
spdi.hasECC = ((get_spd(smb_idx, slot_idx, 11) >> 1) == 1);
// Module speed
float tns, tckns;
uint8_t spd_byte9 = get_spd(smb_idx, slot_idx, 9);
spdi.freq = (float)(1.0f / (((spd_byte9 >> 4) * 10.0f) + (spd_byte9 & 0xF)) * 10000.0f * 2.0f);
tckns = (spd_byte9 >> 4) + (spd_byte9 & 0xF) * 0.1f;
spdi.freq = (uint16_t)(1.0f / tckns* 1000.0f * 2.0f );
// Module Timings
uint8_t spd_byte18 = get_spd(smb_idx, 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;
}
}
tns = (get_spd(smb_idx, slot_idx, 29) >> 2) +
(get_spd(smb_idx, slot_idx, 29) & 0x3) * 0.25f;
spdi.tRCD = (uint16_t)(tns/tckns);
tns = (get_spd(smb_idx, slot_idx, 27) >> 2) +
(get_spd(smb_idx, slot_idx, 27) & 0x3) * 0.25f;
spdi.tRP= (uint16_t)(tns/tckns);
spdi.tRAS = (uint16_t)(get_spd(smb_idx, slot_idx, 30)/tckns);
spdi.tRC = 0;
// Module manufacturer
uint8_t contcode;

View File

@ -63,7 +63,6 @@ typedef struct spd_infos {
bool isValid;
uint32_t module_size;
uint8_t slot_num;
char *type;
uint16_t jedec_code;
char sku[32];
uint8_t sku_len;
@ -72,8 +71,25 @@ typedef struct spd_infos {
bool hasECC;
uint8_t fab_year;
uint8_t fab_week;
uint16_t tCL;
uint16_t tRCD;
uint16_t tRP;
uint16_t tRAS;
uint16_t tRC;
char *type;
} spd_info;
typedef struct ram_infos {
uint16_t freq;
uint16_t tCL;
uint16_t tRCD;
uint16_t tRP;
uint16_t tRAS;
char *type;
} ram_info;
extern ram_info ram;
#define get_spd(smb_idx, slot_idx, spd_adr) \
smbcontrollers[smb_idx].read_spd_byte(slot_idx, spd_adr)