summaryrefslogtreecommitdiffstats
path: root/lmr
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lmr/lmr.h108
-rw-r--r--lmr/margin.c80
-rw-r--r--lmr/margin_args.c302
-rw-r--r--lmr/margin_hw.c77
-rw-r--r--lmr/margin_log.c25
-rw-r--r--lmr/margin_results.c322
6 files changed, 703 insertions, 211 deletions
diff --git a/lmr/lmr.h b/lmr/lmr.h
index 7375c33..98df17a 100644
--- a/lmr/lmr.h
+++ b/lmr/lmr.h
@@ -1,7 +1,7 @@
/*
* The PCI Utilities -- Margining utility main header
*
- * Copyright (c) 2023 KNS Group LLC (YADRO)
+ * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
*
* Can be freely distributed and used under the terms of the GNU GPL v2+.
*
@@ -15,19 +15,17 @@
#include "pciutils.h"
-#define MARGIN_STEP_MS 1000
-
-#define MARGIN_TIM_MIN 20
-#define MARGIN_TIM_RECOMMEND 30
-#define MARGIN_VOLT_MIN 50
-
enum margin_hw { MARGIN_HW_DEFAULT, MARGIN_ICE_LAKE_RC };
+// in ps
+static const double margin_ui[] = { 62.5, 31.25 };
+
/* PCI Device wrapper for margining functions */
struct margin_dev {
struct pci_dev *dev;
int lmr_cap_addr;
- u8 width;
+ u8 neg_width;
+ u8 max_width;
u8 retimers_n;
u8 link_speed;
@@ -39,11 +37,6 @@ struct margin_dev {
bool hawd; // Hardware Autonomous Width Disable
};
-struct margin_link {
- struct margin_dev down_port;
- struct margin_dev up_port;
-};
-
/* Specification Revision 5.0 Table 8-11 */
struct margin_params {
bool ind_error_sampler;
@@ -95,7 +88,7 @@ enum margin_test_status {
/* All lanes Receiver results */
struct margin_results {
- u8 recvn; // Receiver Number
+ u8 recvn; // Receiver Number; from 1 to 6
struct margin_params params;
bool lane_reversal;
u8 link_speed;
@@ -104,7 +97,7 @@ struct margin_results {
/* Used to convert steps to physical quantity.
Calculated from MaxOffset and NumSteps */
- double tim_coef;
+ double tim_coef; // from steps to % UI
double volt_coef;
bool tim_off_reported;
@@ -115,31 +108,56 @@ struct margin_results {
};
/* pcilmr arguments */
-struct margin_args {
+
+// Common args
+struct margin_com_args {
+ u8 error_limit; // [0; 63]
+ bool run_margin; // Or print params only
+ u8 verbosity; // 0 - basic;
+ // 1 - add info about remaining time and lanes in progress during margining
+ u64 steps_utility; // For ETA logging
+ bool save_csv;
+ char *dir_for_csv;
+ u8 dwell_time;
+};
+
+struct margin_recv_args {
+ // Grading options
+ struct {
+ bool valid;
+ double criteria; // in ps/mV
+ bool one_side_is_whole;
+ } t, v;
+};
+
+struct margin_link_args {
+ struct margin_com_args *common;
u8 steps_t; // 0 == use NumTimingSteps
u8 steps_v; // 0 == use NumVoltageSteps
u8 parallel_lanes; // [1; MaxLanes + 1]
- u8 error_limit; // [0; 63]
u8 recvs[6]; // Receivers Numbers
u8 recvs_n; // 0 == margin all available receivers
- u8 lanes[32]; // Lanes to Margin
- u8 lanes_n; // 0 == margin all available lanes
- bool run_margin; // Or print params only
- u8 verbosity; // 0 - basic;
- // 1 - add info about remaining time and lanes in progress during margining
+ struct margin_recv_args recv_args[6];
+ u8 lanes[32]; // Lanes to Margin
+ u8 lanes_n; // 0 == margin all available lanes
+};
- u64 *steps_utility; // For ETA logging
+struct margin_link {
+ struct margin_dev down_port;
+ struct margin_dev up_port;
+ struct margin_link_args args;
};
/* Receiver structure */
struct margin_recv {
struct margin_dev *dev;
- u8 recvn; // Receiver Number
+ u8 recvn; // Receiver Number; from 1 to 6
bool lane_reversal;
struct margin_params *params;
u8 parallel_lanes;
u8 error_limit;
+ u8 dwell_time;
};
struct margin_lanes_data {
@@ -159,8 +177,23 @@ struct margin_lanes_data {
u8 verbosity;
};
+/* margin_args */
+
+enum margin_mode { MARGIN, FULL, SCAN };
+
+extern const char *usage;
+
+struct margin_link *margin_parse_util_args(struct pci_access *pacc, int argc, char **argv,
+ enum margin_mode mode, u8 *links_n);
+
/* margin_hw */
+bool margin_port_is_down(struct pci_dev *dev);
+
+/* Results through down/up ports */
+bool margin_find_pair(struct pci_access *pacc, struct pci_dev *dev, struct pci_dev **down_port,
+ struct pci_dev **up_port);
+
/* Verify that devices form the link with 16 GT/s or 32 GT/s data rate */
bool margin_verify_link(struct pci_dev *down_port, struct pci_dev *up_port);
@@ -183,12 +216,11 @@ void margin_restore_link(struct margin_link *link);
bool margin_read_params(struct pci_access *pacc, struct pci_dev *dev, u8 recvn,
struct margin_params *params);
-enum margin_test_status margin_process_args(struct margin_dev *dev, struct margin_args *args);
+enum margin_test_status margin_process_args(struct margin_link *link);
-/* Awaits that args are prepared through process_args.
+/* Awaits that links are prepared through process_args.
Returns number of margined Receivers through recvs_n */
-struct margin_results *margin_test_link(struct margin_link *link, struct margin_args *args,
- u8 *recvs_n);
+struct margin_results *margin_test_link(struct margin_link *link, u8 *recvs_n);
void margin_free_results(struct margin_results *results, u8 results_n);
@@ -201,8 +233,9 @@ void margin_log(char *format, ...);
/* b:d.f -> b:d.f */
void margin_log_bdfs(struct pci_dev *down_port, struct pci_dev *up_port);
+void margin_gen_bdfs(struct pci_dev *down_port, struct pci_dev *up_port, char *dest, size_t maxlen);
-/* Print Link header (bdfs, width, speed) */
+/* Print Link header (bdfs, neg_width, speed) */
void margin_log_link(struct margin_link *link);
void margin_log_params(struct margin_params *params);
@@ -220,9 +253,20 @@ void margin_log_hw_quirks(struct margin_recv *recv);
/* margin_results */
-void margin_results_print_brief(struct margin_results *results, u8 recvs_n);
+// Min values are taken from PCIe Base Spec Rev. 5.0 Section 8.4.2.
+// Rec values are based on PCIe Arch PHY Test Spec Rev 5.0
+// (Transmitter Electrical Compliance)
+
+// values in ps
+static const double margin_ew_min[] = { 18.75, 9.375 };
+static const double margin_ew_rec[] = { 23.75, 10.1565 };
+
+static const double margin_eh_min[] = { 15, 15 };
+static const double margin_eh_rec[] = { 21, 19.75 };
+
+void margin_results_print_brief(struct margin_results *results, u8 recvs_n,
+ struct margin_link_args *args);
-void margin_results_save_csv(struct margin_results *results, u8 recvs_n, char *dir,
- struct pci_dev *up_port);
+void margin_results_save_csv(struct margin_results *results, u8 recvs_n, struct margin_link *link);
#endif
diff --git a/lmr/margin.c b/lmr/margin.c
index a8c6571..4d68031 100644
--- a/lmr/margin.c
+++ b/lmr/margin.c
@@ -1,7 +1,7 @@
/*
* The PCI Utilities -- Obtain the margin information of the Link
*
- * Copyright (c) 2023 KNS Group LLC (YADRO)
+ * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
*
* Can be freely distributed and used under the terms of the GNU GPL v2+.
*
@@ -143,13 +143,17 @@ margin_report_cmd(struct margin_dev *dev, u8 lane, margin_cmd cmd, margin_cmd *r
}
static void
-margin_apply_hw_quirks(struct margin_recv *recv)
+margin_apply_hw_quirks(struct margin_recv *recv, struct margin_link_args *args)
{
switch (recv->dev->hw)
{
case MARGIN_ICE_LAKE_RC:
if (recv->recvn == 1)
- recv->params->volt_offset = 12;
+ {
+ recv->params->volt_offset = 12;
+ args->recv_args[recv->recvn - 1].t.one_side_is_whole = true;
+ args->recv_args[recv->recvn - 1].t.valid = true;
+ }
break;
default:
break;
@@ -161,7 +165,7 @@ read_params_internal(struct margin_dev *dev, u8 recvn, bool lane_reversal,
struct margin_params *params)
{
margin_cmd resp;
- u8 lane = lane_reversal ? dev->width - 1 : 0;
+ u8 lane = lane_reversal ? dev->max_width - 1 : 0;
margin_set_cmd(dev, lane, NO_COMMAND);
bool status = margin_report_cmd(dev, lane, REPORT_CAPS(recvn), &resp);
if (status)
@@ -260,7 +264,7 @@ margin_test_lanes(struct margin_lanes_data arg)
pci_write_word(arg.recv->dev->dev, ctrl_addr, step_cmd);
}
}
- msleep(MARGIN_STEP_MS);
+ msleep(arg.recv->dwell_time * 1000);
for (int i = 0; i < arg.lanes_n; i++)
{
@@ -300,7 +304,7 @@ margin_test_lanes(struct margin_lanes_data arg)
/* Awaits that Receiver is prepared through prep_dev function */
static bool
-margin_test_receiver(struct margin_dev *dev, u8 recvn, struct margin_args *args,
+margin_test_receiver(struct margin_dev *dev, u8 recvn, struct margin_link_args *args,
struct margin_results *results)
{
u8 *lanes_to_margin = args->lanes;
@@ -312,7 +316,8 @@ margin_test_receiver(struct margin_dev *dev, u8 recvn, struct margin_args *args,
.lane_reversal = false,
.params = &params,
.parallel_lanes = args->parallel_lanes ? args->parallel_lanes : 1,
- .error_limit = args->error_limit };
+ .error_limit = args->common->error_limit,
+ .dwell_time = args->common->dwell_time };
results->recvn = recvn;
results->lanes_n = lanes_n;
@@ -340,7 +345,7 @@ margin_test_receiver(struct margin_dev *dev, u8 recvn, struct margin_args *args,
if (recv.parallel_lanes > params.max_lanes + 1)
recv.parallel_lanes = params.max_lanes + 1;
- margin_apply_hw_quirks(&recv);
+ margin_apply_hw_quirks(&recv, args);
margin_log_hw_quirks(&recv);
results->tim_off_reported = params.timing_offset != 0;
@@ -361,15 +366,16 @@ margin_test_receiver(struct margin_dev *dev, u8 recvn, struct margin_args *args,
for (int i = 0; i < lanes_n; i++)
{
results->lanes[i].lane
- = recv.lane_reversal ? dev->width - lanes_to_margin[i] - 1 : lanes_to_margin[i];
+ = recv.lane_reversal ? dev->max_width - lanes_to_margin[i] - 1 : lanes_to_margin[i];
}
- if (args->run_margin)
+ if (args->common->run_margin)
{
- if (args->verbosity > 0)
+ if (args->common->verbosity > 0)
margin_log("\n");
- struct margin_lanes_data lanes_data
- = { .recv = &recv, .verbosity = args->verbosity, .steps_utility = args->steps_utility };
+ struct margin_lanes_data lanes_data = { .recv = &recv,
+ .verbosity = args->common->verbosity,
+ .steps_utility = &args->common->steps_utility };
enum margin_dir dir[] = { TIM_LEFT, TIM_RIGHT, VOLT_UP, VOLT_DOWN };
@@ -399,15 +405,15 @@ margin_test_receiver(struct margin_dev *dev, u8 recvn, struct margin_args *args,
lanes_data.ind = timing ? params.ind_left_right_tim : params.ind_up_down_volt;
lanes_data.dir = dir[i];
lanes_data.steps_lane_total = timing ? steps_t : steps_v;
- if (*args->steps_utility >= lanes_data.steps_lane_total)
- *args->steps_utility -= lanes_data.steps_lane_total;
+ if (args->common->steps_utility >= lanes_data.steps_lane_total)
+ args->common->steps_utility -= lanes_data.steps_lane_total;
else
- *args->steps_utility = 0;
+ args->common->steps_utility = 0;
margin_test_lanes(lanes_data);
}
lanes_done += use_lanes;
}
- if (args->verbosity > 0)
+ if (args->common->verbosity > 0)
margin_log("\n");
if (recv.lane_reversal)
{
@@ -426,13 +432,8 @@ margin_read_params(struct pci_access *pacc, struct pci_dev *dev, u8 recvn,
struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
if (!cap)
return false;
- u8 dev_dir = GET_REG_MASK(pci_read_word(dev, cap->addr + PCI_EXP_FLAGS), PCI_EXP_FLAGS_TYPE);
- bool dev_down;
- if (dev_dir == PCI_EXP_TYPE_ROOT_PORT || dev_dir == PCI_EXP_TYPE_DOWNSTREAM)
- dev_down = true;
- else
- dev_down = false;
+ bool dev_down = margin_port_is_down(dev);
if (recvn == 0)
{
@@ -453,25 +454,7 @@ margin_read_params(struct pci_access *pacc, struct pci_dev *dev, u8 recvn,
struct pci_dev *up = NULL;
struct margin_link link;
- for (struct pci_dev *p = pacc->devices; p; p = p->next)
- {
- if (dev_down && pci_read_byte(dev, PCI_SECONDARY_BUS) == p->bus && dev->domain == p->domain
- && p->func == 0)
- {
- down = dev;
- up = p;
- break;
- }
- else if (!dev_down && pci_read_byte(p, PCI_SECONDARY_BUS) == dev->bus
- && dev->domain == p->domain)
- {
- down = p;
- up = dev;
- break;
- }
- }
-
- if (!down)
+ if (!margin_find_pair(pacc, dev, &down, &up))
return false;
if (!margin_fill_link(down, up, &link))
@@ -499,8 +482,11 @@ margin_read_params(struct pci_access *pacc, struct pci_dev *dev, u8 recvn,
}
enum margin_test_status
-margin_process_args(struct margin_dev *dev, struct margin_args *args)
+margin_process_args(struct margin_link *link)
{
+ struct margin_dev *dev = &link->down_port;
+ struct margin_link_args *args = &link->args;
+
u8 receivers_n = 2 + 2 * dev->retimers_n;
if (!args->recvs_n)
@@ -524,7 +510,7 @@ margin_process_args(struct margin_dev *dev, struct margin_args *args)
if (!args->lanes_n)
{
- args->lanes_n = dev->width;
+ args->lanes_n = dev->neg_width;
for (int i = 0; i < args->lanes_n; i++)
args->lanes[i] = i;
}
@@ -532,7 +518,7 @@ margin_process_args(struct margin_dev *dev, struct margin_args *args)
{
for (int i = 0; i < args->lanes_n; i++)
{
- if (args->lanes[i] >= dev->width)
+ if (args->lanes[i] >= dev->neg_width)
{
return MARGIN_TEST_ARGS_LANES;
}
@@ -543,8 +529,10 @@ margin_process_args(struct margin_dev *dev, struct margin_args *args)
}
struct margin_results *
-margin_test_link(struct margin_link *link, struct margin_args *args, u8 *recvs_n)
+margin_test_link(struct margin_link *link, u8 *recvs_n)
{
+ struct margin_link_args *args = &link->args;
+
bool status = margin_prep_link(link);
u8 receivers_n = status ? args->recvs_n : 1;
diff --git a/lmr/margin_args.c b/lmr/margin_args.c
new file mode 100644
index 0000000..57a1d0a
--- /dev/null
+++ b/lmr/margin_args.c
@@ -0,0 +1,302 @@
+/*
+ * The PCI Utilities -- Parse pcilmr utility arguments
+ *
+ * Copyright (c) 2024 KNS Group LLC (YADRO)
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lmr.h"
+
+const char *usage
+ = "! Utility requires preliminary preparation of the system. Refer to the pcilmr man page !\n\n"
+ "Brief usage (see man for all options):\n"
+ "pcilmr [--margin] [<common options>] <link port> [<link options>] [<link port> [<link "
+ "options>] ...]\n"
+ "pcilmr --full [<common options>]\n"
+ "pcilmr --scan\n\n"
+ "You can specify Downstream or Upstream Port of the Link.\nPort Specifier:\n"
+ "<device/component>:\t[<domain>:]<bus>:<dev>.<func>\n\n"
+ "Modes:\n"
+ "--margin\t\tMargin selected Links\n"
+ "--full\t\t\tMargin all ready for testing Links in the system (one by one)\n"
+ "--scan\t\t\tScan for Links available for margining\n\n"
+ "Margining options (see man for all options):\n\n"
+ "Common (for all specified links) options:\n"
+ "-c\t\t\tPrint Device Lane Margining Capabilities only. Do not run margining.\n\n"
+ "Link specific options:\n"
+ "-r <recvn>[,<recvn>...]\tSpecify Receivers to select margining targets.\n"
+ "\t\t\tDefault: all available Receivers (including Retimers).\n"
+ "-t <steps>\t\tSpecify maximum number of steps for Time Margining.\n"
+ "-v <steps>\t\tSpecify maximum number of steps for Voltage Margining.\n";
+
+static struct pci_dev *
+dev_for_filter(struct pci_access *pacc, char *filter)
+{
+ struct pci_filter pci_filter;
+ pci_filter_init(pacc, &pci_filter);
+ if (pci_filter_parse_slot(&pci_filter, filter))
+ die("Invalid device ID: %s\n", filter);
+
+ if (pci_filter.bus == -1 || pci_filter.slot == -1 || pci_filter.func == -1)
+ die("Invalid device ID: %s\n", filter);
+
+ if (pci_filter.domain == -1)
+ pci_filter.domain = 0;
+
+ for (struct pci_dev *p = pacc->devices; p; p = p->next)
+ {
+ if (pci_filter_match(&pci_filter, p))
+ return p;
+ }
+
+ die("No such PCI device: %s or you don't have enough privileges.\n", filter);
+}
+
+static u8
+parse_csv_arg(char *arg, u8 *vals)
+{
+ u8 cnt = 0;
+ char *token = strtok(arg, ",");
+ while (token)
+ {
+ vals[cnt] = atoi(token);
+ cnt++;
+ token = strtok(NULL, ",");
+ }
+ return cnt;
+}
+
+static u8
+find_ready_links(struct pci_access *pacc, struct margin_link *links, bool cnt_only)
+{
+ u8 cnt = 0;
+ for (struct pci_dev *p = pacc->devices; p; p = p->next)
+ {
+ if (pci_find_cap(p, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED) && margin_port_is_down(p))
+ {
+ struct pci_dev *down = NULL;
+ struct pci_dev *up = NULL;
+ margin_find_pair(pacc, p, &down, &up);
+
+ if (down && margin_verify_link(down, up)
+ && (margin_check_ready_bit(down) || margin_check_ready_bit(up)))
+ {
+ if (!cnt_only)
+ margin_fill_link(down, up, &(links[cnt]));
+ cnt++;
+ }
+ }
+ }
+ return cnt;
+}
+
+static void
+init_link_args(struct margin_link_args *link_args, struct margin_com_args *com_args)
+{
+ memset(link_args, 0, sizeof(*link_args));
+ link_args->common = com_args;
+ link_args->parallel_lanes = 1;
+}
+
+static void
+parse_dev_args(int argc, char **argv, struct margin_link_args *args, u8 link_speed)
+{
+ if (argc == optind)
+ return;
+ int c;
+ while ((c = getopt(argc, argv, "+r:l:p:t:v:VTg:")) != -1)
+ {
+ switch (c)
+ {
+ case 't':
+ args->steps_t = atoi(optarg);
+ break;
+ case 'T':
+ args->steps_t = 63;
+ break;
+ case 'v':
+ args->steps_v = atoi(optarg);
+ break;
+ case 'V':
+ args->steps_v = 127;
+ break;
+ case 'p':
+ args->parallel_lanes = atoi(optarg);
+ break;
+ case 'l':
+ args->lanes_n = parse_csv_arg(optarg, args->lanes);
+ break;
+ case 'r':
+ args->recvs_n = parse_csv_arg(optarg, args->recvs);
+ break;
+ case 'g': {
+ char recv[2] = { 0 };
+ char dir[2] = { 0 };
+ char unit[4] = { 0 };
+ float criteria = 0.0;
+ char eye[2] = { 0 };
+ int cons[3] = { 0 };
+
+ int ret = sscanf(optarg, "%1[1-6]%1[tv]=%f%n%3[%,ps]%n%1[f]%n", recv, dir, &criteria,
+ &cons[0], unit, &cons[1], eye, &cons[2]);
+ if (ret < 3)
+ {
+ ret = sscanf(optarg, "%1[1-6]%1[tv]=%1[f]%n,%f%n%2[ps%]%n", recv, dir, eye,
+ &cons[0], &criteria, &cons[1], unit, &cons[2]);
+ if (ret < 3)
+ die("Invalid arguments\n\n%s", usage);
+ }
+
+ int consumed = 0;
+ for (int i = 0; i < 3; i++)
+ if (cons[i] > consumed)
+ consumed = cons[i];
+ if ((size_t)consumed != strlen(optarg))
+ die("Invalid arguments\n\n%s", usage);
+ if (criteria < 0)
+ die("Invalid arguments\n\n%s", usage);
+ if (strstr(unit, ",") && eye[0] == 0)
+ die("Invalid arguments\n\n%s", usage);
+
+ u8 recv_n = recv[0] - '0' - 1;
+ if (dir[0] == 'v')
+ {
+ if (unit[0] != ',' && unit[0] != 0)
+ die("Invalid arguments\n\n%s", usage);
+ args->recv_args[recv_n].v.valid = true;
+ args->recv_args[recv_n].v.criteria = criteria;
+ if (eye[0] != 0)
+ args->recv_args[recv_n].v.one_side_is_whole = true;
+ }
+ else
+ {
+ if (unit[0] == '%')
+ criteria = criteria / 100.0 * margin_ui[link_speed];
+ else if (unit[0] != 0 && (unit[0] != 'p' || unit[1] != 's'))
+ die("Invalid arguments\n\n%s", usage);
+ else if (unit[0] == 0 && criteria != 0)
+ die("Invalid arguments\n\n%s", usage);
+ args->recv_args[recv_n].t.valid = true;
+ args->recv_args[recv_n].t.criteria = criteria;
+ if (eye[0] != 0)
+ args->recv_args[recv_n].t.one_side_is_whole = true;
+ }
+ break;
+ }
+ case '?':
+ die("Invalid arguments\n\n%s", usage);
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+struct margin_link *
+margin_parse_util_args(struct pci_access *pacc, int argc, char **argv, enum margin_mode mode,
+ u8 *links_n)
+{
+ struct margin_com_args *com_args = xmalloc(sizeof(*com_args));
+ com_args->error_limit = 4;
+ com_args->run_margin = true;
+ com_args->verbosity = 1;
+ com_args->steps_utility = 0;
+ com_args->dir_for_csv = NULL;
+ com_args->save_csv = false;
+ com_args->dwell_time = 1;
+
+ int c;
+ while ((c = getopt(argc, argv, "+e:co:d:")) != -1)
+ {
+ switch (c)
+ {
+ case 'c':
+ com_args->run_margin = false;
+ break;
+ case 'e':
+ com_args->error_limit = atoi(optarg);
+ break;
+ case 'o':
+ com_args->dir_for_csv = optarg;
+ com_args->save_csv = true;
+ break;
+ case 'd':
+ com_args->dwell_time = atoi(optarg);
+ break;
+ default:
+ die("Invalid arguments\n\n%s", usage);
+ }
+ }
+
+ bool status = true;
+ if (mode == FULL && optind != argc)
+ status = false;
+ if (mode == MARGIN && optind == argc)
+ status = false;
+ if (!status && argc > 1)
+ die("Invalid arguments\n\n%s", usage);
+ if (!status)
+ {
+ printf("%s", usage);
+ exit(0);
+ }
+
+ u8 ports_n = 0;
+ struct margin_link *links = NULL;
+ char err[128];
+
+ if (mode == FULL)
+ {
+ ports_n = find_ready_links(pacc, NULL, true);
+ if (ports_n == 0)
+ die("Links not found or you don't have enough privileges.\n");
+ else
+ {
+ links = xmalloc(ports_n * sizeof(*links));
+ find_ready_links(pacc, links, false);
+ for (int i = 0; i < ports_n; i++)
+ init_link_args(&(links[i].args), com_args);
+ }
+ }
+ else if (mode == MARGIN)
+ {
+ while (optind != argc)
+ {
+ struct pci_dev *dev = dev_for_filter(pacc, argv[optind]);
+ optind++;
+ links = xrealloc(links, (ports_n + 1) * sizeof(*links));
+ struct pci_dev *down;
+ struct pci_dev *up;
+ if (!margin_find_pair(pacc, dev, &down, &up))
+ die("Cannot find pair for the specified device: %s\n", argv[optind - 1]);
+ struct pci_cap *cap = pci_find_cap(down, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
+ if (!cap)
+ die("Looks like you don't have enough privileges to access "
+ "Device Configuration Space.\nTry to run utility as root.\n");
+ if (!margin_fill_link(down, up, &(links[ports_n])))
+ {
+ margin_gen_bdfs(down, up, err, sizeof(err));
+ die("Link %s is not ready for margining.\n"
+ "Link data rate must be 16 GT/s or 32 GT/s.\n"
+ "Downstream Component must be at D0 PM state.\n",
+ err);
+ }
+ init_link_args(&(links[ports_n].args), com_args);
+ parse_dev_args(argc, argv, &(links[ports_n].args),
+ links[ports_n].down_port.link_speed - 4);
+ ports_n++;
+ }
+ }
+ else
+ die("Bug in the args parsing!\n");
+
+ *links_n = ports_n;
+ return links;
+}
diff --git a/lmr/margin_hw.c b/lmr/margin_hw.c
index fc427c8..c376549 100644
--- a/lmr/margin_hw.c
+++ b/lmr/margin_hw.c
@@ -1,13 +1,15 @@
/*
* The PCI Utilities -- Verify and prepare devices before margining
*
- * Copyright (c) 2023 KNS Group LLC (YADRO)
+ * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
*
* Can be freely distributed and used under the terms of the GNU GPL v2+.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
+#include <memory.h>
+
#include "lmr.h"
static u16 special_hw[][4] =
@@ -32,6 +34,51 @@ detect_unique_hw(struct pci_dev *dev)
}
bool
+margin_port_is_down(struct pci_dev *dev)
+{
+ struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
+ if (!cap)
+ return false;
+ u8 type = pci_read_byte(dev, PCI_HEADER_TYPE) & 0x7F;
+ u8 dir = GET_REG_MASK(pci_read_word(dev, cap->addr + PCI_EXP_FLAGS), PCI_EXP_FLAGS_TYPE);
+
+ if (type == PCI_HEADER_TYPE_BRIDGE
+ && (dir == PCI_EXP_TYPE_ROOT_PORT || dir == PCI_EXP_TYPE_DOWNSTREAM))
+ return true;
+ else
+ return false;
+}
+
+bool
+margin_find_pair(struct pci_access *pacc, struct pci_dev *dev, struct pci_dev **down_port,
+ struct pci_dev **up_port)
+{
+ struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
+ if (!cap)
+ return false;
+ bool given_down = margin_port_is_down(dev);
+
+ for (struct pci_dev *p = pacc->devices; p; p = p->next)
+ {
+ if (given_down && pci_read_byte(dev, PCI_SECONDARY_BUS) == p->bus && dev->domain == p->domain
+ && p->func == 0)
+ {
+ *down_port = dev;
+ *up_port = p;
+ return true;
+ }
+ else if (!given_down && pci_read_byte(p, PCI_SECONDARY_BUS) == dev->bus
+ && dev->domain == p->domain)
+ {
+ *down_port = p;
+ *up_port = dev;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
margin_verify_link(struct pci_dev *down_port, struct pci_dev *up_port)
{
struct pci_cap *cap = pci_find_cap(down_port, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
@@ -42,16 +89,11 @@ margin_verify_link(struct pci_dev *down_port, struct pci_dev *up_port)
if ((pci_read_word(down_port, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED) > 5)
return false;
- u8 down_type = pci_read_byte(down_port, PCI_HEADER_TYPE) & 0x7F;
u8 down_sec = pci_read_byte(down_port, PCI_SECONDARY_BUS);
- u8 down_dir
- = GET_REG_MASK(pci_read_word(down_port, cap->addr + PCI_EXP_FLAGS), PCI_EXP_FLAGS_TYPE);
// Verify that devices are linked, down_port is Root Port or Downstream Port of Switch,
// up_port is Function 0 of a Device
- if (!(down_sec == up_port->bus && down_type == PCI_HEADER_TYPE_BRIDGE
- && (down_dir == PCI_EXP_TYPE_ROOT_PORT || down_dir == PCI_EXP_TYPE_DOWNSTREAM)
- && up_port->func == 0))
+ if (!(down_sec == up_port->bus && margin_port_is_down(down_port) && up_port->func == 0))
return false;
struct pci_cap *pm = pci_find_cap(up_port, PCI_CAP_ID_PM, PCI_CAP_NORMAL);
@@ -70,21 +112,24 @@ static struct margin_dev
fill_dev_wrapper(struct pci_dev *dev)
{
struct pci_cap *cap = pci_find_cap(dev, PCI_CAP_ID_EXP, PCI_CAP_NORMAL);
- struct margin_dev res
- = { .dev = dev,
- .lmr_cap_addr = pci_find_cap(dev, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED)->addr,
- .width = GET_REG_MASK(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA), PCI_EXP_LNKSTA_WIDTH),
- .retimers_n
- = (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_RETIMER))
- + (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_2RETIMERS)),
- .link_speed = (pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED),
- .hw = detect_unique_hw(dev) };
+ struct margin_dev res = {
+ .dev = dev,
+ .lmr_cap_addr = pci_find_cap(dev, PCI_EXT_CAP_ID_LMR, PCI_CAP_EXTENDED)->addr,
+ .neg_width = GET_REG_MASK(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA), PCI_EXP_LNKSTA_WIDTH),
+ .max_width = GET_REG_MASK(pci_read_long(dev, cap->addr + PCI_EXP_LNKCAP), PCI_EXP_LNKCAP_WIDTH),
+ .retimers_n
+ = (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_RETIMER))
+ + (!!(pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA2) & PCI_EXP_LINKSTA2_2RETIMERS)),
+ .link_speed = (pci_read_word(dev, cap->addr + PCI_EXP_LNKSTA) & PCI_EXP_LNKSTA_SPEED),
+ .hw = detect_unique_hw(dev)
+ };
return res;
}
bool
margin_fill_link(struct pci_dev *down_port, struct pci_dev *up_port, struct margin_link *wrappers)
{
+ memset(wrappers, 0, sizeof(*wrappers));
if (!margin_verify_link(down_port, up_port))
return false;
wrappers->down_port = fill_dev_wrapper(down_port);
diff --git a/lmr/margin_log.c b/lmr/margin_log.c
index b3c4bd5..2cb01c8 100644
--- a/lmr/margin_log.c
+++ b/lmr/margin_log.c
@@ -1,7 +1,7 @@
/*
* The PCI Utilities -- Log margining process
*
- * Copyright (c) 2023 KNS Group LLC (YADRO)
+ * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
*
* Can be freely distributed and used under the terms of the GNU GPL v2+.
*
@@ -38,11 +38,22 @@ margin_log_bdfs(struct pci_dev *down, struct pci_dev *up)
}
void
+margin_gen_bdfs(struct pci_dev *down, struct pci_dev *up, char *dest, size_t maxlen)
+{
+ if (margin_print_domain)
+ snprintf(dest, maxlen, "%x:%x:%x.%x -> %x:%x:%x.%x", down->domain, down->bus, down->dev,
+ down->func, up->domain, up->bus, up->dev, up->func);
+ else
+ snprintf(dest, maxlen, "%x:%x.%x -> %x:%x.%x", down->bus, down->dev, down->func, up->bus,
+ up->dev, up->func);
+}
+
+void
margin_log_link(struct margin_link *link)
{
margin_log("Link ");
margin_log_bdfs(link->down_port.dev, link->up_port.dev);
- margin_log("\nNegotiated Link Width: %d\n", link->down_port.width);
+ margin_log("\nNegotiated Link Width: %d\n", link->down_port.neg_width);
margin_log("Link Speed: %d.0 GT/s = Gen %d\n", (link->down_port.link_speed - 3) * 16,
link->down_port.link_speed);
margin_log("Available receivers: ");
@@ -77,7 +88,8 @@ void
margin_log_receiver(struct margin_recv *recv)
{
margin_log("\nError Count Limit = %d\n", recv->error_limit);
- margin_log("Parallel Lanes: %d\n\n", recv->parallel_lanes);
+ margin_log("Parallel Lanes: %d\n", recv->parallel_lanes);
+ margin_log("Margining dwell time: %d s\n\n", recv->dwell_time);
margin_log_params(recv->params);
@@ -132,8 +144,8 @@ margin_log_margining(struct margin_lanes_data arg)
}
margin_log("]");
- u64 lane_eta_s = (arg.steps_lane_total - arg.steps_lane_done) * MARGIN_STEP_MS / 1000;
- u64 total_eta_s = *arg.steps_utility * MARGIN_STEP_MS / 1000 + lane_eta_s;
+ u64 lane_eta_s = (arg.steps_lane_total - arg.steps_lane_done) * arg.recv->dwell_time;
+ u64 total_eta_s = *arg.steps_utility * arg.recv->dwell_time + lane_eta_s;
margin_log(" - ETA: %3ds Steps: %3d Total ETA: %3dm %2ds", lane_eta_s, arg.steps_lane_done,
total_eta_s / 60, total_eta_s % 60);
@@ -150,7 +162,8 @@ margin_log_hw_quirks(struct margin_recv *recv)
if (recv->recvn == 1)
margin_log("\nRx(A) is Intel Ice Lake RC port.\n"
"Applying next quirks for margining process:\n"
- " - Set MaxVoltageOffset to 12 (120 mV).\n");
+ " - Set MaxVoltageOffset to 12 (120 mV);\n"
+ " - Force the use of 'one side is the whole' grading mode.\n");
break;
default:
break;
diff --git a/lmr/margin_results.c b/lmr/margin_results.c
index 4d28f04..b0c5c26 100644
--- a/lmr/margin_results.c
+++ b/lmr/margin_results.c
@@ -1,7 +1,7 @@
/*
* The PCI Utilities -- Display/save margining results
*
- * Copyright (c) 2023 KNS Group LLC (YADRO)
+ * Copyright (c) 2023-2024 KNS Group LLC (YADRO)
*
* Can be freely distributed and used under the terms of the GNU GPL v2+.
*
@@ -16,25 +16,23 @@
#include "lmr.h"
enum lane_rating {
- BAD = 0,
- OKAY,
+ FAIL = 0,
+ PASS,
PERFECT,
- WEIRD,
INIT,
};
-static char *const grades[] = { "Bad", "Okay", "Perfect", "Weird" };
+static char *const grades[] = { "Fail", "Pass", "Perfect" };
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;
+ res = PASS;
if (value < min)
- res = BAD;
+ res = FAIL;
if (cur_rate == INIT)
return res;
if (res < cur_rate)
@@ -43,34 +41,9 @@ rate_lane(double value, double min, double recommended, enum lane_rating cur_rat
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 (int 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->params.ind_left_right_tim
- && lane->steps[TIM_RIGHT] * results->tim_coef != tim_min)
- result = false;
- if (results->params.volt_support)
- {
- if (lane->steps[VOLT_UP] * results->volt_coef != volt_min)
- result = false;
- if (results->params.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, u8 recvs_n)
+margin_results_print_brief(struct margin_results *results, u8 recvs_n,
+ struct margin_link_args *args)
{
struct margin_res_lane *lane;
struct margin_results *res;
@@ -80,6 +53,14 @@ margin_results_print_brief(struct margin_results *results, u8 recvs_n)
u8 link_speed;
+ struct margin_recv_args grade_args;
+ bool spec_ref_only;
+
+ double ew_min;
+ double ew_rec;
+ double eh_min;
+ double eh_rec;
+
char *no_test_msgs[] = { "",
"Margining Ready bit is Clear",
"Error during caps reading",
@@ -102,6 +83,67 @@ margin_results_print_brief(struct margin_results *results, u8 recvs_n)
continue;
}
+ spec_ref_only = true;
+ grade_args = args->recv_args[res->recvn - 1];
+ if (grade_args.t.criteria != 0)
+ {
+ spec_ref_only = false;
+ ew_min = grade_args.t.criteria;
+ ew_rec = grade_args.t.criteria;
+ }
+ else
+ {
+ ew_min = margin_ew_min[link_speed];
+ ew_rec = margin_ew_rec[link_speed];
+ }
+
+ if (grade_args.v.criteria != 0)
+ {
+ spec_ref_only = false;
+ eh_min = grade_args.v.criteria;
+ eh_rec = grade_args.v.criteria;
+ }
+ else
+ {
+ eh_min = margin_eh_min[link_speed];
+ eh_rec = margin_eh_rec[link_speed];
+ }
+
+ printf("Rx(%X) - Grading criteria:\n", 10 + res->recvn - 1);
+ if (spec_ref_only)
+ {
+ printf("\tUsing spec only:\n");
+ printf("\tEW: minimum - %.2f ps; recommended - %.2f ps\n", ew_min, ew_rec);
+ printf("\tEH: minimum - %.2f mV; recommended - %.2f mV\n\n", eh_min, eh_rec);
+ }
+ else
+ {
+ printf("\tEW: pass - %.2f ps\n", ew_min);
+ printf("\tEH: pass - %.2f mV\n\n", eh_min);
+ }
+
+ if (!params.ind_left_right_tim)
+ {
+ printf("Rx(%X) - EW: independent left/right timing margin is not supported:\n",
+ 10 + res->recvn - 1);
+ if (grade_args.t.one_side_is_whole)
+ printf("\tmanual setting - the entire margin across the eye "
+ "is what is reported by one side margining\n\n");
+ else
+ printf("\tdefault - calculating EW as double one side result\n\n");
+ }
+
+ if (params.volt_support && !params.ind_up_down_volt)
+ {
+ printf("Rx(%X) - EH: independent up and down voltage margining is not supported:\n",
+ 10 + res->recvn - 1);
+ if (grade_args.v.one_side_is_whole)
+ printf("\tmanual setting - the entire margin across the eye "
+ "is what is reported by one side margining\n\n");
+ else
+ printf("\tdefault - calculating EH as double one side result\n\n");
+ }
+
if (res->lane_reversal)
printf("Rx(%X) - Lane Reversal\n", 10 + res->recvn - 1);
@@ -118,51 +160,60 @@ margin_results_print_brief(struct margin_results *results, u8 recvs_n)
"reliable.\n\n",
10 + res->recvn - 1);
- if (check_recv_weird(res, MARGIN_TIM_MIN, MARGIN_VOLT_MIN))
- lane_rating = WEIRD;
- else
- lane_rating = INIT;
-
- for (u8 j = 0; j < res->lanes_n; j++)
+ for (int j = 0; j < res->lanes_n; j++)
{
+ if (spec_ref_only)
+ lane_rating = INIT;
+ else
+ lane_rating = PASS;
+
lane = &(res->lanes[j]);
- double left_ui = lane->steps[TIM_LEFT] * res->tim_coef;
- double right_ui = lane->steps[TIM_RIGHT] * res->tim_coef;
+ double left_ps = lane->steps[TIM_LEFT] * res->tim_coef / 100.0 * margin_ui[link_speed];
+ double right_ps = lane->steps[TIM_RIGHT] * res->tim_coef / 100.0 * margin_ui[link_speed];
double up_volt = lane->steps[VOLT_UP] * res->volt_coef;
double down_volt = lane->steps[VOLT_DOWN] * res->volt_coef;
- if (lane_rating != WEIRD)
+ double ew = left_ps;
+ if (params.ind_left_right_tim)
+ ew += right_ps;
+ else if (!grade_args.t.one_side_is_whole)
+ ew *= 2.0;
+
+ double eh = 0.0;
+ if (params.volt_support)
{
- lane_rating = rate_lane(left_ui, MARGIN_TIM_MIN, MARGIN_TIM_RECOMMEND, INIT);
- if (params.ind_left_right_tim)
- lane_rating
- = rate_lane(right_ui, MARGIN_TIM_MIN, MARGIN_TIM_RECOMMEND, lane_rating);
- if (params.volt_support)
- {
- lane_rating = rate_lane(up_volt, MARGIN_VOLT_MIN, MARGIN_VOLT_MIN, lane_rating);
- if (params.ind_up_down_volt)
- lane_rating
- = rate_lane(down_volt, MARGIN_VOLT_MIN, MARGIN_VOLT_MIN, lane_rating);
- }
+ eh += up_volt;
+ if (params.ind_up_down_volt)
+ eh += down_volt;
+ else if (!grade_args.v.one_side_is_whole)
+ eh *= 2.0;
}
- printf("Rx(%X) Lane %2d - %s\t", 10 + res->recvn - 1, lane->lane, grades[lane_rating]);
+ lane_rating = rate_lane(ew, ew_min, ew_rec, lane_rating);
+ if (params.volt_support)
+ lane_rating = rate_lane(eh, eh_min, eh_rec, lane_rating);
+
+ printf("Rx(%X) Lane %2d: %s\t (W %4.1f%% UI - %5.2fps", 10 + res->recvn - 1, lane->lane,
+ grades[lane_rating], ew / margin_ui[link_speed] * 100.0, ew);
+ if (params.volt_support)
+ printf(", H %5.1f mV", eh);
if (params.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]]);
+ printf(") (L %4.1f%% UI - %5.2fps - %2dst %s) (R %4.1f%% UI - %5.2fps - %2dst %s)",
+ left_ps / margin_ui[link_speed] * 100.0, left_ps, lane->steps[TIM_LEFT],
+ sts_strings[lane->statuses[TIM_LEFT]], right_ps / margin_ui[link_speed] * 100.0,
+ right_ps, 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]]);
+ printf(") (T %4.1f%% UI - %5.2fps - %2dst %s)",
+ left_ps / margin_ui[link_speed] * 100.0, left_ps, lane->steps[TIM_LEFT],
+ sts_strings[lane->statuses[TIM_LEFT]]);
if (params.volt_support)
{
if (params.ind_up_down_volt)
- printf(", U %5.1f mV - %3dst %s, D %5.1f mV - %3dst %s", up_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],
+ printf(" (V %5.1f mV - %3dst %s)", up_volt, lane->steps[VOLT_UP],
sts_strings[lane->statuses[VOLT_UP]]);
}
printf("\n");
@@ -172,13 +223,13 @@ margin_results_print_brief(struct margin_results *results, u8 recvs_n)
}
void
-margin_results_save_csv(struct margin_results *results, u8 recvs_n, char *dir,
- struct pci_dev *up_port)
+margin_results_save_csv(struct margin_results *results, u8 recvs_n, struct margin_link *link)
{
char timestamp[64];
time_t tim = time(NULL);
strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S", gmtime(&tim));
+ char *dir = link->args.common->dir_for_csv;
size_t pathlen = strlen(dir) + 128;
char *path = xmalloc(pathlen);
FILE *csv;
@@ -190,6 +241,16 @@ margin_results_save_csv(struct margin_results *results, u8 recvs_n, char *dir,
enum lane_rating lane_rating;
u8 link_speed;
+ struct margin_recv_args grade_args;
+ bool spec_ref_only;
+
+ double ew_min;
+ double ew_rec;
+ double eh_min;
+ double eh_rec;
+
+ struct pci_dev *port;
+
for (int i = 0; i < recvs_n; i++)
{
res = &(results[i]);
@@ -198,81 +259,120 @@ margin_results_save_csv(struct margin_results *results, u8 recvs_n, char *dir,
if (res->test_status != MARGIN_TEST_OK)
continue;
+
+ port = res->recvn == 6 ? link->up_port.dev : link->down_port.dev;
snprintf(path, pathlen, "%s/lmr_%0*x.%02x.%02x.%x_Rx%X_%s.csv", dir,
- up_port->domain_16 == 0xffff ? 8 : 4, up_port->domain, up_port->bus, up_port->dev,
- up_port->func, 10 + res->recvn - 1, timestamp);
+ port->domain_16 == 0xffff ? 8 : 4, port->domain, port->bus, port->dev, port->func,
+ 10 + res->recvn - 1, timestamp);
csv = fopen(path, "w");
if (!csv)
die("Error while saving %s\n", path);
- fprintf(csv, "Lane,Lane Status,Left %% UI,Left ps,Left Steps,Left Status,"
- "Right %% UI,Right ps,Right Steps,Right Status,"
- "Time %% UI,Time ps,Time Steps,Time Status,"
- "Up mV,Up Steps,Up Status,Down mV,Down Steps,Down Status,"
- "Voltage mV,Voltage Steps,Voltage Status\n");
+ fprintf(csv, "Lane,EW Min,EW Rec,EW,EH Min,EH Rec,EH,Lane Status,Left %% UI,Left "
+ "ps,Left Steps,Left Status,Right %% UI,Right ps,Right Steps,Right Status,Up "
+ "mV,Up Steps,Up Status,Down mV,Down Steps,Down Status\n");
- if (check_recv_weird(res, MARGIN_TIM_MIN, MARGIN_VOLT_MIN))
- lane_rating = WEIRD;
+ spec_ref_only = true;
+ grade_args = link->args.recv_args[res->recvn - 1];
+ if (grade_args.t.criteria != 0)
+ {
+ spec_ref_only = false;
+ ew_min = grade_args.t.criteria;
+ ew_rec = grade_args.t.criteria;
+ }
+ else
+ {
+ ew_min = margin_ew_min[link_speed];
+ ew_rec = margin_ew_rec[link_speed];
+ }
+ if (grade_args.v.criteria != 0)
+ {
+ spec_ref_only = false;
+ eh_min = grade_args.v.criteria;
+ eh_rec = grade_args.v.criteria;
+ }
else
- lane_rating = INIT;
+ {
+ eh_min = margin_eh_min[link_speed];
+ eh_rec = margin_eh_rec[link_speed];
+ }
for (int j = 0; j < res->lanes_n; j++)
{
+ if (spec_ref_only)
+ lane_rating = INIT;
+ else
+ lane_rating = PASS;
+
lane = &(res->lanes[j]);
- double left_ui = lane->steps[TIM_LEFT] * res->tim_coef;
- double right_ui = lane->steps[TIM_RIGHT] * res->tim_coef;
+ double left_ps = lane->steps[TIM_LEFT] * res->tim_coef / 100.0 * margin_ui[link_speed];
+ double right_ps = lane->steps[TIM_RIGHT] * res->tim_coef / 100.0 * margin_ui[link_speed];
double up_volt = lane->steps[VOLT_UP] * res->volt_coef;
double down_volt = lane->steps[VOLT_DOWN] * res->volt_coef;
- if (lane_rating != WEIRD)
+ double ew = left_ps;
+ if (params.ind_left_right_tim)
+ ew += right_ps;
+ else if (!grade_args.t.one_side_is_whole)
+ ew *= 2.0;
+
+ double eh = 0.0;
+ if (params.volt_support)
{
- lane_rating = rate_lane(left_ui, MARGIN_TIM_MIN, MARGIN_TIM_RECOMMEND, INIT);
- if (params.ind_left_right_tim)
- lane_rating
- = rate_lane(right_ui, MARGIN_TIM_MIN, MARGIN_TIM_RECOMMEND, lane_rating);
- if (params.volt_support)
- {
- lane_rating = rate_lane(up_volt, MARGIN_VOLT_MIN, MARGIN_VOLT_MIN, lane_rating);
- if (params.ind_up_down_volt)
- lane_rating
- = rate_lane(down_volt, MARGIN_VOLT_MIN, MARGIN_VOLT_MIN, lane_rating);
- }
+ eh += up_volt;
+ if (params.ind_up_down_volt)
+ eh += down_volt;
+ else if (!grade_args.v.one_side_is_whole)
+ eh *= 2.0;
}
- fprintf(csv, "%d,%s,", lane->lane, grades[lane_rating]);
- if (params.ind_left_right_tim)
+ lane_rating = rate_lane(ew, ew_min, ew_rec, lane_rating);
+ if (params.volt_support)
+ lane_rating = rate_lane(eh, eh_min, eh_rec, lane_rating);
+
+ fprintf(csv, "%d,%f,", lane->lane, ew_min);
+ if (spec_ref_only)
+ fprintf(csv, "%f,", ew_rec);
+ else
+ fprintf(csv, "NA,");
+ fprintf(csv, "%f,", ew);
+ if (params.volt_support)
{
- fprintf(csv, "%f,%f,%d,%s,%f,%f,%d,%s,NA,NA,NA,NA,", 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]]);
+ fprintf(csv, "%f,", eh_min);
+ if (spec_ref_only)
+ fprintf(csv, "%f,", eh_rec);
+ else
+ fprintf(csv, "NA,");
+ fprintf(csv, "%f,", eh);
}
else
+ fprintf(csv, "NA,NA,NA,");
+ fprintf(csv, "%s,", grades[lane_rating]);
+
+ fprintf(csv, "%f,%f,%d,%s,", left_ps * 100.0 / margin_ui[link_speed], left_ps,
+ lane->steps[TIM_LEFT], sts_strings[lane->statuses[TIM_LEFT]]);
+
+ if (params.ind_left_right_tim)
+ fprintf(csv, "%f,%f,%d,%s,", right_ps * 100.0 / margin_ui[link_speed], right_ps,
+ lane->steps[TIM_RIGHT], sts_strings[lane->statuses[TIM_RIGHT]]);
+ else
{
- for (int k = 0; k < 8; k++)
+ for (int k = 0; k < 4; k++)
fprintf(csv, "NA,");
- fprintf(csv, "%f,%f,%d,%s,", left_ui, left_ui * ui[link_speed], lane->steps[TIM_LEFT],
- sts_strings[lane->statuses[TIM_LEFT]]);
}
if (params.volt_support)
{
+ fprintf(csv, "%f,%d,%s,", up_volt, lane->steps[VOLT_UP],
+ sts_strings[lane->statuses[VOLT_UP]]);
if (params.ind_up_down_volt)
- {
- fprintf(csv, "%f,%d,%s,%f,%d,%s,NA,NA,NA\n", up_volt, lane->steps[VOLT_UP],
- sts_strings[lane->statuses[VOLT_UP]], down_volt, lane->steps[VOLT_DOWN],
- sts_strings[lane->statuses[VOLT_DOWN]]);
- }
+ fprintf(csv, "%f,%d,%s\n", down_volt, lane->steps[VOLT_DOWN],
+ sts_strings[lane->statuses[VOLT_DOWN]]);
else
- {
- for (int k = 0; k < 6; k++)
- fprintf(csv, "NA,");
- fprintf(csv, "%f,%d,%s\n", up_volt, lane->steps[VOLT_UP],
- sts_strings[lane->statuses[VOLT_UP]]);
- }
+ fprintf(csv, "NA,NA,NA\n");
}
else
{
- for (int k = 0; k < 8; k++)
+ for (int k = 0; k < 5; k++)
fprintf(csv, "NA,");
fprintf(csv, "NA\n");
}