Lanes are rated according to the minimum/recommended values. The minimum values are taken from the specification (Lane Margining at the Receiver - Electrical Requirements). 30% UI recommended value for timing is taken from NVIDIA presentation "PCIe 4.0 Mass Electrical Margins Data Collection". Receiver lanes are called 'Weird' if all results of all receiver lanes are equal to the spec minimum value. Reviewed-by: Sergei Miroshnichenko <s.miroshnichenko@xxxxxxxxx> Signed-off-by: Nikita Proshkin <n.proshkin@xxxxxxxxx> --- lmr_lib/Makefile | 2 +- lmr_lib/margin_results.c | 135 +++++++++++++++++++++++++++++++++++++++ lmr_lib/margin_results.h | 13 ++++ 3 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 lmr_lib/margin_results.c create mode 100644 lmr_lib/margin_results.h diff --git a/lmr_lib/Makefile b/lmr_lib/Makefile index a0eefd6..193e164 100644 --- a/lmr_lib/Makefile +++ b/lmr_lib/Makefile @@ -1,4 +1,4 @@ -OBJS=margin_hw margin margin_log +OBJS=margin_hw margin margin_log margin_results INCL=$(addsuffix .h,$(OBJS)) $(addprefix ../,$(PCIINC)) $(addsuffix .o, $(OBJS)): %.o: %.c $(INCL) diff --git a/lmr_lib/margin_results.c b/lmr_lib/margin_results.c new file mode 100644 index 0000000..61f483a --- /dev/null +++ b/lmr_lib/margin_results.c @@ -0,0 +1,135 @@ +#include <stdlib.h> +#include <stdio.h> + +#include "margin_results.h" + +enum lane_rating { + BAD = 0, + OKAY, + PERFECT, + WEIRD, + INIT, +}; + +static char *const grades[] = {"Bad", "Okay", "Perfect", "Weird"}; +static char *const sts_strings[] = {"NAK", "LIM", "THR"}; +static const double ui[] = {62.5 / 100, 31.25 / 100}; + +static enum lane_rating rate_lane(double value, double min, double recommended, enum lane_rating cur_rate) +{ + enum lane_rating res = PERFECT; + if (value < recommended) + res = OKAY; + if (value < min) + res = BAD; + if (cur_rate == INIT) + return res; + if (res < cur_rate) + return res; + else + return cur_rate; +} + +static bool check_recv_weird(struct margin_results *results, double tim_min, double volt_min) +{ + bool result = true; + + struct margin_res_lane *lane; + for (uint8_t i = 0; i < results->lanes_n && result; i++) + { + lane = &(results->lanes[i]); + if (lane->steps[TIM_LEFT] * results->tim_coef != tim_min) + result = false; + if (results->ind_left_right_tim && lane->steps[TIM_RIGHT] * results->tim_coef != tim_min) + result = false; + if (results->volt_support) + { + if (lane->steps[VOLT_UP] * results->volt_coef != volt_min) + result = false; + if (results->ind_up_down_volt && lane->steps[VOLT_DOWN] * results->volt_coef != volt_min) + result = false; + } + } + return result; +} + +void margin_results_print_brief(struct margin_results *results, uint8_t recvs_n) +{ + struct margin_res_lane *lane; + struct margin_results *recv; + + enum lane_rating lane_rating; + + uint8_t link_speed; + + char *no_test_msgs[] = { + "", "Margining Ready bit is Clear", + "Error during caps reading", + "Margining prerequisites are not satisfied (Gen 4/5, D0)", + "Invalid lanes specified with arguments", + "Invalid receivers specified with arguments", + "Couldn't disable ASPM"}; + + for (uint8_t i = 0; i < recvs_n; i++) + { + recv = &(results[i]); + link_speed = recv->link_speed - 4; + + if (recv->test_status != MARGIN_TEST_OK) + { + if (recv->test_status < MARGIN_TEST_PREREQS) + printf("Rx(%X) -", 10 + recv->recvn - 1); + printf(" Couldn't run test (%s)\n\n", no_test_msgs[recv->test_status]); + continue; + } + + if (recv->lane_reversal) + printf("Rx(%X) - Lane Reversal\n", 10 + recv->recvn - 1); + if (check_recv_weird(recv, MARGIN_TIM_MIN, MARGIN_VOLT_MIN)) + lane_rating = WEIRD; + else + lane_rating = INIT; + + for (uint8_t j = 0; j < recv->lanes_n; j++) + { + lane = &(recv->lanes[j]); + double left_ui = lane->steps[TIM_LEFT] * recv->tim_coef; + double right_ui = lane->steps[TIM_RIGHT] * recv->tim_coef; + double up_volt = lane->steps[VOLT_UP] * recv->volt_coef; + double down_volt = lane->steps[VOLT_DOWN] * recv->volt_coef; + + if (lane_rating != WEIRD) + { + lane_rating = rate_lane(left_ui, MARGIN_TIM_MIN, MARGIN_TIM_RECOMMEND, INIT); + if (recv->ind_left_right_tim) + lane_rating = rate_lane(right_ui, MARGIN_TIM_MIN, MARGIN_TIM_RECOMMEND, lane_rating); + if (recv->volt_support) + { + lane_rating = rate_lane(up_volt, MARGIN_VOLT_MIN, MARGIN_VOLT_MIN, lane_rating); + if (recv->ind_up_down_volt) + lane_rating = rate_lane(down_volt, MARGIN_VOLT_MIN, MARGIN_VOLT_MIN, lane_rating); + } + } + + printf("Rx(%X) Lane %2d - %s\t", 10 + recv->recvn - 1, lane->lane, grades[lane_rating]); + if (recv->ind_left_right_tim) + printf("L %4.1f%% UI - %5.2fps - %2dst %s, R %4.1f%% UI - %5.2fps - %2dst %s", left_ui, + left_ui * ui[link_speed], lane->steps[TIM_LEFT], sts_strings[lane->statuses[TIM_LEFT]], right_ui, + right_ui * ui[link_speed], lane->steps[TIM_RIGHT], sts_strings[lane->statuses[TIM_RIGHT]]); + else + printf("T %4.1f%% UI - %5.2fps - %2dst %s", left_ui, left_ui * ui[link_speed], + lane->steps[TIM_LEFT], sts_strings[lane->statuses[TIM_LEFT]]); + if (recv->volt_support) + { + if (recv->ind_up_down_volt) + printf(", U %5.1f mV - %3dst %s, D %5.1f mV - %3dst %s", up_volt, + lane->steps[VOLT_UP], sts_strings[lane->statuses[VOLT_UP]], + down_volt, lane->steps[VOLT_DOWN], sts_strings[lane->statuses[VOLT_DOWN]]); + else + printf(", V %5.1f mV - %3dst %s", up_volt, lane->steps[VOLT_UP], sts_strings[lane->statuses[VOLT_UP]]); + } + printf("\n"); + } + printf("\n"); + } +} diff --git a/lmr_lib/margin_results.h b/lmr_lib/margin_results.h new file mode 100644 index 0000000..ab6ddb0 --- /dev/null +++ b/lmr_lib/margin_results.h @@ -0,0 +1,13 @@ +#ifndef _MARGIN_RESULTS_H +#define _MARGIN_RESULTS_H + +#include "margin.h" + +#define MARGIN_TIM_MIN 20 +#define MARGIN_TIM_RECOMMEND 30 + +#define MARGIN_VOLT_MIN 50 + +void margin_results_print_brief(struct margin_results *results, uint8_t recvs_n); + +#endif -- 2.34.1