Angel Pons has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/47681 )
Change subject: [WIP] plot 2D read margins ......................................................................
[WIP] plot 2D read margins
Change-Id: I7cda8d19b78f6265c8e5919ce942f709738e5e44 Signed-off-by: Angel Pons th3fanbus@gmail.com --- A configs/config.asus_p8h61-m_pro.debug_read_2d_margins M src/northbridge/intel/sandybridge/Kconfig M src/northbridge/intel/sandybridge/raminit_common.c 3 files changed, 141 insertions(+), 14 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/81/47681/1
diff --git a/configs/config.asus_p8h61-m_pro.debug_read_2d_margins b/configs/config.asus_p8h61-m_pro.debug_read_2d_margins new file mode 100644 index 0000000..8a46605 --- /dev/null +++ b/configs/config.asus_p8h61-m_pro.debug_read_2d_margins @@ -0,0 +1,6 @@ +CONFIG_VENDOR_ASUS=y +CONFIG_BOARD_ASUS_P8H61_M_PRO=y +CONFIG_NATIVE_RAMINIT_IGNORE_MAX_MEM_FUSES=y +CONFIG_NATIVE_RAMINIT_IGNORE_XMP_MAX_DIMMS=y +CONFIG_RAMINIT_PLOT_READ_MARGINS=y +CONFIG_DEBUG_RAM_SETUP=y diff --git a/src/northbridge/intel/sandybridge/Kconfig b/src/northbridge/intel/sandybridge/Kconfig index ef6dc3d..8f53656 100644 --- a/src/northbridge/intel/sandybridge/Kconfig +++ b/src/northbridge/intel/sandybridge/Kconfig @@ -105,6 +105,13 @@ help Enable ECC if supported by both, host and RAM.
+config RAMINIT_PLOT_READ_MARGINS + bool "Plot 2D read margins for DQS edges" + depends on DEBUG_RAM_SETUP + default n + help + Plot 2D margins instead of doing aggressive read training. + endif # USE_NATIVE_RAMINIT
if !USE_NATIVE_RAMINIT diff --git a/src/northbridge/intel/sandybridge/raminit_common.c b/src/northbridge/intel/sandybridge/raminit_common.c index 8f60b8c..8276ba1 100644 --- a/src/northbridge/intel/sandybridge/raminit_common.c +++ b/src/northbridge/intel/sandybridge/raminit_common.c @@ -2440,6 +2440,8 @@ { const int rd_vref_offsets[] = { 0, 0xc, 0x2c };
+ const bool sweep_vref = CONFIG(RAMINIT_PLOT_READ_MARGINS); + u32 raw_stats[MAX_EDGE_TIMING + 1]; int lower[NUM_LANES]; int upper[NUM_LANES]; @@ -2450,16 +2452,39 @@ upper[lane] = MAX_EDGE_TIMING; }
- for (i = 0; i < ARRAY_SIZE(rd_vref_offsets); i++) { + const int max_vref_idx = sweep_vref ? 63 : ARRAY_SIZE(rd_vref_offsets); + for (i = 0; i < max_vref_idx; i++) { + const int vref = i >= 32 ? i : 31 - i; const union gdcr_training_mod_reg training_mod = { - .vref_gen_ctl = rd_vref_offsets[i], + .vref_gen_ctl = sweep_vref ? vref : rd_vref_offsets[i], }; MCHBAR32(GDCRTRAININGMOD_ch(channel)) = training_mod.raw; printram("[%x] = 0x%08x\n", GDCRTRAININGMOD_ch(channel), training_mod.raw);
+ if (sweep_vref) + printram("% 5d:\t", 31 - i); + for (pat = 0; pat < NUM_PATTERNS; pat++) { fill_pattern5(ctrl, channel, pat); - printram("using pattern %d\n", pat); + + if (!sweep_vref) + printram("using pattern %d\n", pat); + + /* Center read timing to ensure memory is correctly written */ + read_pi = MAX_EDGE_TIMING / 2; + + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].rising = read_pi; + ctrl->timings[channel][slotrank].lanes[lane].falling = read_pi; + } + program_timings(ctrl, channel); + + /* Write test pattern to memory */ + iosav_write_data_write_sequence(ctrl, channel, slotrank); + + iosav_run_once(channel); + + wait_for_iosav(channel);
for (read_pi = 0; read_pi <= MAX_EDGE_TIMING; read_pi++) { FOR_ALL_LANES { @@ -2474,22 +2499,100 @@ MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)) = 0; MCHBAR32(IOSAV_By_BW_SERROR_C_ch(channel, lane)); } - wait_for_iosav(channel);
- iosav_write_data_write_sequence(ctrl, channel, slotrank); + /* Program read sequence */ + const struct iosav_ssq sequence[] = { + /* DRAM command ACT */ + [0] = { + .sp_cmd_ctrl = { + .command = IOSAV_ACT, + .ranksel_ap = 1, + }, + .subseq_ctrl = { + .cmd_executions = 4, + .cmd_delay_gap = MAX(ctrl->tRRD, (ctrl->tFAW >> 2) + 1), + .post_ssq_wait = ctrl->tRCD, + .data_direction = SSQ_NA, + }, + .sp_cmd_addr = { + .address = 0, + .rowbits = 6, + .bank = 0, + .rank = slotrank, + }, + .addr_update = { + .inc_bank = 0, + .addr_wrap = 18, + }, + }, + /* DRAM command RD */ + [1] = { + .sp_cmd_ctrl = { + .command = IOSAV_RD, + .ranksel_ap = 1, + }, + .subseq_ctrl = { + .cmd_executions = 32, + .cmd_delay_gap = 20, + .post_ssq_wait = MAX(ctrl->tRTP, 8), + .data_direction = SSQ_RD, + }, + .sp_cmd_addr = { + .address = 0, + .rowbits = 0, + .bank = 0, + .rank = slotrank, + }, + .addr_update = { + .inc_addr_8 = 1, + .addr_wrap = 18, + }, + }, + /* DRAM command PREA */ + [2] = { + .sp_cmd_ctrl = { + .command = IOSAV_PRE, + .ranksel_ap = 1, + }, + .subseq_ctrl = { + .cmd_executions = 1, + .cmd_delay_gap = 3, + .post_ssq_wait = ctrl->tRP, + .data_direction = SSQ_NA, + }, + .sp_cmd_addr = { + .address = 1024, + .rowbits = 6, + .bank = 0, + .rank = slotrank, + }, + }, + }; + iosav_write_sequence(channel, sequence, ARRAY_SIZE(sequence));
- /* Execute command queue */ iosav_run_once(channel);
wait_for_iosav(channel); + FOR_ALL_LANES { MCHBAR32(IOSAV_By_ERROR_COUNT_ch(channel, lane)); }
/* FIXME: This register only exists on Ivy Bridge */ raw_stats[read_pi] = MCHBAR32(IOSAV_BYTE_SERROR_C_ch(channel)); + + if (!sweep_vref) + continue; + + if (i == 31 && read_pi == (MAX_EDGE_TIMING + 1) / 2) + printram("%c", raw_stats[read_pi] & 0xff ? 'X' : '+'); + else + printram("%c", raw_stats[read_pi] & 0xff ? '#' : '.'); }
+ if (sweep_vref) + printram(" "); + FOR_ALL_LANES { int stats[MAX_EDGE_TIMING + 1]; struct run rn; @@ -2499,18 +2602,26 @@
rn = get_longest_zero_run(stats, MAX_EDGE_TIMING + 1);
- printram("edges: %d, %d, %d: 0x%02x-0x%02x-0x%02x, " - "0x%02x-0x%02x\n", channel, slotrank, i, rn.start, - rn.middle, rn.end, rn.start + ctrl->edge_offset[i], - rn.end - ctrl->edge_offset[i]); + const int edge_offset = sweep_vref ? 0 : ctrl->edge_offset[i];
- lower[lane] = MAX(rn.start + ctrl->edge_offset[i], lower[lane]); - upper[lane] = MIN(rn.end - ctrl->edge_offset[i], upper[lane]); + if (!sweep_vref) { + printram("edges: %d, %d, %d: 0x%02x-0x%02x-0x%02x, " + "0x%02x-0x%02x\n", channel, slotrank, i, + rn.start, rn.middle, rn.end, + rn.start + edge_offset, rn.end - edge_offset); + } + + if (!sweep_vref || i == 0) { + lower[lane] = MAX(rn.start + edge_offset, lower[lane]); + upper[lane] = MIN(rn.end - edge_offset, upper[lane]); + }
edges[lane] = (lower[lane] + upper[lane]) / 2; - if (rn.all || (lower[lane] > upper[lane])) { + + if (!sweep_vref && (rn.all || lower[lane] > upper[lane])) { printk(BIOS_EMERG, "edge write discovery failed: " - "%d, %d, %d\n", channel, slotrank, lane); + "%d, %d, %d, %d, %d, %d\n", channel, slotrank, + lane, rn.all, lower[lane], upper[lane]);
return MAKE_ERR; } @@ -2518,6 +2629,9 @@ } }
+ if (sweep_vref) + printram("\n"); + /* Restore nominal Vref after training */ MCHBAR32(GDCRTRAININGMOD_ch(channel)) = 0; printram("CPA\n");