1 /*
2 * The PCI Utilities -- Display/save margining results
3 *
4 * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL v2+.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <time.h>
15
16 #include "lmr.h"
17
18 enum lane_rating {
19 FAIL = 0,
20 PASS,
21 PERFECT,
22 INIT,
23 };
24
25 static char *const grades[] = { "Fail", "Pass", "Perfect" };
26 static char *const sts_strings[] = { "NAK", "LIM", "THR" };
27
28 static enum lane_rating
rate_lane(double value,double min,double recommended,enum lane_rating cur_rate)29 rate_lane(double value, double min, double recommended, enum lane_rating cur_rate)
30 {
31 enum lane_rating res = PERFECT;
32 if (value < recommended)
33 res = PASS;
34 if (value < min)
35 res = FAIL;
36 if (cur_rate == INIT)
37 return res;
38 if (res < cur_rate)
39 return res;
40 else
41 return cur_rate;
42 }
43
44 void
margin_results_print_brief(struct margin_results * results,u8 recvs_n,struct margin_link_args * args)45 margin_results_print_brief(struct margin_results *results, u8 recvs_n,
46 struct margin_link_args *args)
47 {
48 struct margin_res_lane *lane;
49 struct margin_results *res;
50 struct margin_params params;
51
52 enum lane_rating lane_rating;
53
54 u8 link_speed;
55
56 struct margin_recv_args grade_args;
57 bool spec_ref_only;
58
59 double ew_min;
60 double ew_rec;
61 double eh_min;
62 double eh_rec;
63
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" };
71
72 for (int i = 0; i < recvs_n; i++)
73 {
74 res = &(results[i]);
75 params = res->params;
76 link_speed = res->link_speed - 4;
77
78 if (res->test_status != MARGIN_TEST_OK)
79 {
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]);
83 continue;
84 }
85
86 spec_ref_only = true;
87 grade_args = args->recv_args[res->recvn - 1];
88 if (grade_args.t.criteria != 0)
89 {
90 spec_ref_only = false;
91 ew_min = grade_args.t.criteria;
92 ew_rec = grade_args.t.criteria;
93 }
94 else
95 {
96 ew_min = margin_ew_min[link_speed];
97 ew_rec = margin_ew_rec[link_speed];
98 }
99
100 if (grade_args.v.criteria != 0)
101 {
102 spec_ref_only = false;
103 eh_min = grade_args.v.criteria;
104 eh_rec = grade_args.v.criteria;
105 }
106 else
107 {
108 eh_min = margin_eh_min[link_speed];
109 eh_rec = margin_eh_rec[link_speed];
110 }
111
112 printf("Rx(%X) - Grading criteria:\n", 10 + res->recvn - 1);
113 if (spec_ref_only)
114 {
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);
118 }
119 else
120 {
121 printf("\tEW: pass - %.2f ps\n", ew_min);
122 printf("\tEH: pass - %.2f mV\n\n", eh_min);
123 }
124
125 if (!params.ind_left_right_tim)
126 {
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");
132 else
133 printf("\tdefault - calculating EW as double one side result\n\n");
134 }
135
136 if (params.volt_support && !params.ind_up_down_volt)
137 {
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");
143 else
144 printf("\tdefault - calculating EH as double one side result\n\n");
145 }
146
147 if (res->lane_reversal)
148 printf("Rx(%X) - Lane Reversal\n", 10 + res->recvn - 1);
149
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 "
154 "reliable.\n\n",
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 "
160 "reliable.\n\n",
161 10 + res->recvn - 1);
162
163 for (int j = 0; j < res->lanes_n; j++)
164 {
165 if (spec_ref_only)
166 lane_rating = INIT;
167 else
168 lane_rating = PASS;
169
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;
175
176 double ew = left_ps;
177 if (params.ind_left_right_tim)
178 ew += right_ps;
179 else if (!grade_args.t.one_side_is_whole)
180 ew *= 2.0;
181
182 double eh = 0.0;
183 if (params.volt_support)
184 {
185 eh += up_volt;
186 if (params.ind_up_down_volt)
187 eh += down_volt;
188 else if (!grade_args.v.one_side_is_whole)
189 eh *= 2.0;
190 }
191
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);
195
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]]);
205 else
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)
210 {
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]]);
215 else
216 printf(" (V %5.1f mV - %3dst %s)", up_volt, lane->steps[VOLT_UP],
217 sts_strings[lane->statuses[VOLT_UP]]);
218 }
219 printf("\n");
220 }
221 printf("\n");
222 }
223 }
224
225 void
margin_results_save_csv(struct margin_results * results,u8 recvs_n,struct margin_link * link)226 margin_results_save_csv(struct margin_results *results, u8 recvs_n, struct margin_link *link)
227 {
228 char timestamp[64];
229 time_t tim = time(NULL);
230 strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S", gmtime(&tim));
231
232 char *dir = link->args.common->dir_for_csv;
233 size_t pathlen = strlen(dir) + 128;
234 char *path = xmalloc(pathlen);
235 FILE *csv;
236
237 struct margin_res_lane *lane;
238 struct margin_results *res;
239 struct margin_params params;
240
241 enum lane_rating lane_rating;
242 u8 link_speed;
243
244 struct margin_recv_args grade_args;
245 bool spec_ref_only;
246
247 double ew_min;
248 double ew_rec;
249 double eh_min;
250 double eh_rec;
251
252 struct pci_dev *port;
253
254 for (int i = 0; i < recvs_n; i++)
255 {
256 res = &(results[i]);
257 params = res->params;
258 link_speed = res->link_speed - 4;
259
260 if (res->test_status != MARGIN_TEST_OK)
261 continue;
262
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");
268 if (!csv)
269 die("Error while saving %s\n", path);
270
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");
274
275 spec_ref_only = true;
276 grade_args = link->args.recv_args[res->recvn - 1];
277 if (grade_args.t.criteria != 0)
278 {
279 spec_ref_only = false;
280 ew_min = grade_args.t.criteria;
281 ew_rec = grade_args.t.criteria;
282 }
283 else
284 {
285 ew_min = margin_ew_min[link_speed];
286 ew_rec = margin_ew_rec[link_speed];
287 }
288 if (grade_args.v.criteria != 0)
289 {
290 spec_ref_only = false;
291 eh_min = grade_args.v.criteria;
292 eh_rec = grade_args.v.criteria;
293 }
294 else
295 {
296 eh_min = margin_eh_min[link_speed];
297 eh_rec = margin_eh_rec[link_speed];
298 }
299
300 for (int j = 0; j < res->lanes_n; j++)
301 {
302 if (spec_ref_only)
303 lane_rating = INIT;
304 else
305 lane_rating = PASS;
306
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;
312
313 double ew = left_ps;
314 if (params.ind_left_right_tim)
315 ew += right_ps;
316 else if (!grade_args.t.one_side_is_whole)
317 ew *= 2.0;
318
319 double eh = 0.0;
320 if (params.volt_support)
321 {
322 eh += up_volt;
323 if (params.ind_up_down_volt)
324 eh += down_volt;
325 else if (!grade_args.v.one_side_is_whole)
326 eh *= 2.0;
327 }
328
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);
332
333 fprintf(csv, "%d,%f,", lane->lane, ew_min);
334 if (spec_ref_only)
335 fprintf(csv, "%f,", ew_rec);
336 else
337 fprintf(csv, "NA,");
338 fprintf(csv, "%f,", ew);
339 if (params.volt_support)
340 {
341 fprintf(csv, "%f,", eh_min);
342 if (spec_ref_only)
343 fprintf(csv, "%f,", eh_rec);
344 else
345 fprintf(csv, "NA,");
346 fprintf(csv, "%f,", eh);
347 }
348 else
349 fprintf(csv, "NA,NA,NA,");
350 fprintf(csv, "%s,", grades[lane_rating]);
351
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]]);
354
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]]);
358 else
359 {
360 for (int k = 0; k < 4; k++)
361 fprintf(csv, "NA,");
362 }
363 if (params.volt_support)
364 {
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]]);
370 else
371 fprintf(csv, "NA,NA,NA\n");
372 }
373 else
374 {
375 for (int k = 0; k < 5; k++)
376 fprintf(csv, "NA,");
377 fprintf(csv, "NA\n");
378 }
379 }
380 fclose(csv);
381 }
382 free(path);
383 }
384