2 * The PCI Utilities -- Display/save margining results
4 * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
6 * Can be freely distributed and used under the terms of the GNU GPL v2+.
8 * SPDX-License-Identifier: GPL-2.0-or-later
25 static char *const grades[] = { "Fail", "Pass", "Perfect" };
26 static char *const sts_strings[] = { "NAK", "LIM", "THR" };
28 static enum lane_rating
29 rate_lane(double value, double min, double recommended, enum lane_rating cur_rate)
31 enum lane_rating res = PERFECT;
32 if (value < recommended)
45 margin_results_print_brief(struct margin_results *results, u8 recvs_n,
46 struct margin_link_args *args)
48 struct margin_res_lane *lane;
49 struct margin_results *res;
50 struct margin_params params;
52 enum lane_rating lane_rating;
56 struct margin_recv_args grade_args;
64 char *no_test_msgs[] = { "",
65 "Margining Ready bit is Clear",
66 "Error during caps reading",
67 "Margining prerequisites are not satisfied (16/32 GT/s, D0)",
68 "Invalid lanes specified with arguments",
69 "Invalid receivers specified with arguments",
70 "Couldn't disable ASPM" };
72 for (int i = 0; i < recvs_n; i++)
76 link_speed = res->link_speed - 4;
78 if (res->test_status != MARGIN_TEST_OK)
80 if (res->test_status < MARGIN_TEST_PREREQS)
81 printf("Rx(%X) -", 10 + res->recvn - 1);
82 printf(" Couldn't run test (%s)\n\n", no_test_msgs[res->test_status]);
87 grade_args = args->recv_args[res->recvn - 1];
88 if (grade_args.t.criteria != 0)
90 spec_ref_only = false;
91 ew_min = grade_args.t.criteria;
92 ew_rec = grade_args.t.criteria;
96 ew_min = margin_ew_min[link_speed];
97 ew_rec = margin_ew_rec[link_speed];
100 if (grade_args.v.criteria != 0)
102 spec_ref_only = false;
103 eh_min = grade_args.v.criteria;
104 eh_rec = grade_args.v.criteria;
108 eh_min = margin_eh_min[link_speed];
109 eh_rec = margin_eh_rec[link_speed];
112 printf("Rx(%X) - Grading criteria:\n", 10 + res->recvn - 1);
115 printf("\tUsing spec only:\n");
116 printf("\tEW: minimum - %.2f ps; recommended - %.2f ps\n", ew_min, ew_rec);
117 printf("\tEH: minimum - %.2f mV; recommended - %.2f mV\n\n", eh_min, eh_rec);
121 printf("\tEW: pass - %.2f ps\n", ew_min);
122 printf("\tEH: pass - %.2f mV\n\n", eh_min);
125 if (!params.ind_left_right_tim)
127 printf("Rx(%X) - EW: independent left/right timing margin is not supported:\n",
128 10 + res->recvn - 1);
129 if (grade_args.t.one_side_is_whole)
130 printf("\tmanual setting - the entire margin across the eye "
131 "is what is reported by one side margining\n\n");
133 printf("\tdefault - calculating EW as double one side result\n\n");
136 if (params.volt_support && !params.ind_up_down_volt)
138 printf("Rx(%X) - EH: independent up and down voltage margining is not supported:\n",
139 10 + res->recvn - 1);
140 if (grade_args.v.one_side_is_whole)
141 printf("\tmanual setting - the entire margin across the eye "
142 "is what is reported by one side margining\n\n");
144 printf("\tdefault - calculating EH as double one side result\n\n");
147 if (res->lane_reversal)
148 printf("Rx(%X) - Lane Reversal\n", 10 + res->recvn - 1);
150 if (!res->tim_off_reported)
151 printf("Rx(%X) - Attention: Vendor chose not to report the Max Timing Offset.\n"
152 "Utility used its max possible value (50%% UI) for calculations of %% UI and ps.\n"
153 "Keep in mind that for timing results of this receiver only steps values are "
155 10 + res->recvn - 1);
156 if (params.volt_support && !res->volt_off_reported)
157 printf("Rx(%X) - Attention: Vendor chose not to report the Max Voltage Offset.\n"
158 "Utility used its max possible value (500 mV) for calculations of mV.\n"
159 "Keep in mind that for voltage results of this receiver only steps values are "
161 10 + res->recvn - 1);
163 for (int j = 0; j < res->lanes_n; j++)
170 lane = &(res->lanes[j]);
171 double left_ps = lane->steps[TIM_LEFT] * res->tim_coef / 100.0 * margin_ui[link_speed];
172 double right_ps = lane->steps[TIM_RIGHT] * res->tim_coef / 100.0 * margin_ui[link_speed];
173 double up_volt = lane->steps[VOLT_UP] * res->volt_coef;
174 double down_volt = lane->steps[VOLT_DOWN] * res->volt_coef;
177 if (params.ind_left_right_tim)
179 else if (!grade_args.t.one_side_is_whole)
183 if (params.volt_support)
186 if (params.ind_up_down_volt)
188 else if (!grade_args.v.one_side_is_whole)
192 lane_rating = rate_lane(ew, ew_min, ew_rec, lane_rating);
193 if (params.volt_support)
194 lane_rating = rate_lane(eh, eh_min, eh_rec, lane_rating);
196 printf("Rx(%X) Lane %2d: %s\t (W %4.1f%% UI - %5.2fps", 10 + res->recvn - 1, lane->lane,
197 grades[lane_rating], ew / margin_ui[link_speed] * 100.0, ew);
198 if (params.volt_support)
199 printf(", H %5.1f mV", eh);
200 if (params.ind_left_right_tim)
201 printf(") (L %4.1f%% UI - %5.2fps - %2dst %s) (R %4.1f%% UI - %5.2fps - %2dst %s)",
202 left_ps / margin_ui[link_speed] * 100.0, left_ps, lane->steps[TIM_LEFT],
203 sts_strings[lane->statuses[TIM_LEFT]], right_ps / margin_ui[link_speed] * 100.0,
204 right_ps, lane->steps[TIM_RIGHT], sts_strings[lane->statuses[TIM_RIGHT]]);
206 printf(") (T %4.1f%% UI - %5.2fps - %2dst %s)",
207 left_ps / margin_ui[link_speed] * 100.0, left_ps, lane->steps[TIM_LEFT],
208 sts_strings[lane->statuses[TIM_LEFT]]);
209 if (params.volt_support)
211 if (params.ind_up_down_volt)
212 printf(" (U %5.1f mV - %3dst %s) (D %5.1f mV - %3dst %s)", up_volt,
213 lane->steps[VOLT_UP], sts_strings[lane->statuses[VOLT_UP]], down_volt,
214 lane->steps[VOLT_DOWN], sts_strings[lane->statuses[VOLT_DOWN]]);
216 printf(" (V %5.1f mV - %3dst %s)", up_volt, lane->steps[VOLT_UP],
217 sts_strings[lane->statuses[VOLT_UP]]);
226 margin_results_save_csv(struct margin_results *results, u8 recvs_n, struct margin_link *link)
229 time_t tim = time(NULL);
230 strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S", gmtime(&tim));
232 char *dir = link->args.common->dir_for_csv;
233 size_t pathlen = strlen(dir) + 128;
234 char *path = xmalloc(pathlen);
237 struct margin_res_lane *lane;
238 struct margin_results *res;
239 struct margin_params params;
241 enum lane_rating lane_rating;
244 struct margin_recv_args grade_args;
252 struct pci_dev *port;
254 for (int i = 0; i < recvs_n; i++)
257 params = res->params;
258 link_speed = res->link_speed - 4;
260 if (res->test_status != MARGIN_TEST_OK)
263 port = res->recvn == 6 ? link->up_port.dev : link->down_port.dev;
264 snprintf(path, pathlen, "%s/lmr_%0*x.%02x.%02x.%x_Rx%X_%s.csv", dir,
265 port->domain_16 == 0xffff ? 8 : 4, port->domain, port->bus, port->dev, port->func,
266 10 + res->recvn - 1, timestamp);
267 csv = fopen(path, "w");
269 die("Error while saving %s\n", path);
271 fprintf(csv, "Lane,EW Min,EW Rec,EW,EH Min,EH Rec,EH,Lane Status,Left %% UI,Left "
272 "ps,Left Steps,Left Status,Right %% UI,Right ps,Right Steps,Right Status,Up "
273 "mV,Up Steps,Up Status,Down mV,Down Steps,Down Status\n");
275 spec_ref_only = true;
276 grade_args = link->args.recv_args[res->recvn - 1];
277 if (grade_args.t.criteria != 0)
279 spec_ref_only = false;
280 ew_min = grade_args.t.criteria;
281 ew_rec = grade_args.t.criteria;
285 ew_min = margin_ew_min[link_speed];
286 ew_rec = margin_ew_rec[link_speed];
288 if (grade_args.v.criteria != 0)
290 spec_ref_only = false;
291 eh_min = grade_args.v.criteria;
292 eh_rec = grade_args.v.criteria;
296 eh_min = margin_eh_min[link_speed];
297 eh_rec = margin_eh_rec[link_speed];
300 for (int j = 0; j < res->lanes_n; j++)
307 lane = &(res->lanes[j]);
308 double left_ps = lane->steps[TIM_LEFT] * res->tim_coef / 100.0 * margin_ui[link_speed];
309 double right_ps = lane->steps[TIM_RIGHT] * res->tim_coef / 100.0 * margin_ui[link_speed];
310 double up_volt = lane->steps[VOLT_UP] * res->volt_coef;
311 double down_volt = lane->steps[VOLT_DOWN] * res->volt_coef;
314 if (params.ind_left_right_tim)
316 else if (!grade_args.t.one_side_is_whole)
320 if (params.volt_support)
323 if (params.ind_up_down_volt)
325 else if (!grade_args.v.one_side_is_whole)
329 lane_rating = rate_lane(ew, ew_min, ew_rec, lane_rating);
330 if (params.volt_support)
331 lane_rating = rate_lane(eh, eh_min, eh_rec, lane_rating);
333 fprintf(csv, "%d,%f,", lane->lane, ew_min);
335 fprintf(csv, "%f,", ew_rec);
338 fprintf(csv, "%f,", ew);
339 if (params.volt_support)
341 fprintf(csv, "%f,", eh_min);
343 fprintf(csv, "%f,", eh_rec);
346 fprintf(csv, "%f,", eh);
349 fprintf(csv, "NA,NA,NA,");
350 fprintf(csv, "%s,", grades[lane_rating]);
352 fprintf(csv, "%f,%f,%d,%s,", left_ps * 100.0 / margin_ui[link_speed], left_ps,
353 lane->steps[TIM_LEFT], sts_strings[lane->statuses[TIM_LEFT]]);
355 if (params.ind_left_right_tim)
356 fprintf(csv, "%f,%f,%d,%s,", right_ps * 100.0 / margin_ui[link_speed], right_ps,
357 lane->steps[TIM_RIGHT], sts_strings[lane->statuses[TIM_RIGHT]]);
360 for (int k = 0; k < 4; k++)
363 if (params.volt_support)
365 fprintf(csv, "%f,%d,%s,", up_volt, lane->steps[VOLT_UP],
366 sts_strings[lane->statuses[VOLT_UP]]);
367 if (params.ind_up_down_volt)
368 fprintf(csv, "%f,%d,%s\n", down_volt, lane->steps[VOLT_DOWN],
369 sts_strings[lane->statuses[VOLT_DOWN]]);
371 fprintf(csv, "NA,NA,NA\n");
375 for (int k = 0; k < 5; k++)
377 fprintf(csv, "NA\n");