From 5b228074a765473d25ee7f7e03168f2c146a826c Mon Sep 17 00:00:00 2001 From: Peter Colberg Date: Fri, 21 Jun 2024 13:17:59 -0400 Subject: [PATCH 1/6] changelog: update for 2.13.0-3 Signed-off-by: Peter Colberg --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a056749ca41d..cadf0f7fa3cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [2.13.0-3] + +### Fixed + +- Increate hssi poll timeout from 500 us to 1 s ([#3132]). + +[2.13.0-3]: https://github.com/OFS/opae-sdk/compare/2.13.0-2...2.13.0-3 +[#3132]: https://github.com/OFS/opae-sdk/pull/3132 + ## [2.13.0-2] ### Added From 7de7b1d03515c9cb98fa40e08b7bf8e83e45f9f4 Mon Sep 17 00:00:00 2001 From: Roger Christensen Date: Mon, 15 Apr 2024 10:13:05 +0200 Subject: [PATCH 2/6] qsfpinfo: New tool to print QSFP EEPROM information for cards with qsfp feature (id 0x13). Signed-off-by: Roger Christensen --- CMakeLists.txt | 2 + binaries/CMakeLists.txt | 6 + binaries/qsfpinfo/CMakeLists.txt | 73 +++++ binaries/qsfpinfo/cmis_wrap.c | 28 ++ binaries/qsfpinfo/main.c | 412 ++++++++++++++++++++++++ binaries/qsfpinfo/sff-common_wrap.c | 28 ++ doc/sphinx/conf.py.in | 1 + doc/sphinx/index.rst.in | 1 + doc/src/fpga_tools/qsfpinfo/qsfpinfo.md | 96 ++++++ opae.spec.fedora | 5 +- opae.spec.in | 1 + opae.spec.rhel | 3 +- packaging/opae/deb/opae-devel.install | 1 + scripts/opae-clean.py | 1 + 14 files changed, 655 insertions(+), 3 deletions(-) create mode 100644 binaries/qsfpinfo/CMakeLists.txt create mode 100644 binaries/qsfpinfo/cmis_wrap.c create mode 100644 binaries/qsfpinfo/main.c create mode 100644 binaries/qsfpinfo/sff-common_wrap.c create mode 100644 doc/src/fpga_tools/qsfpinfo/qsfpinfo.md diff --git a/CMakeLists.txt b/CMakeLists.txt index 41838a4846fc..ad4d16bea76e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -982,6 +982,7 @@ set(CPACK_COMPONENTS_ALL toolfpgadiagapps toolpackager tooluserclk + toolqsfpinfo toolras toolcoreidle toolmmlink @@ -1049,6 +1050,7 @@ define_pkg(tools define_pkg(tools-extra COMPONENTS tooluserclk + toolqsfpinfo toolras toolmmlink toolcoreidle diff --git a/binaries/CMakeLists.txt b/binaries/CMakeLists.txt index e9158f78e885..a7d0e107842e 100644 --- a/binaries/CMakeLists.txt +++ b/binaries/CMakeLists.txt @@ -31,6 +31,9 @@ opae_add_subdirectory(fpgametrics) option(OPAE_BUILD_USERCLK "Enable building extra tool userclk" ON) mark_as_advanced(OPAE_BUILD_USERCLK) +option(OPAE_BUILD_QSFPINFO "Enable building extra tool qsfpinfo" ON) +mark_as_advanced(OPAE_BUILD_QSFPINFO) + option(OPAE_BUILD_FPGADIAG "Enable building extra tool fpgadiag" ON) mark_as_advanced(OPAE_BUILD_FPGADIAG) @@ -52,6 +55,9 @@ if(OPAE_BUILD_EXTRA_TOOLS) if(OPAE_BUILD_USERCLK) opae_add_subdirectory(userclk) endif() + if(OPAE_BUILD_QSFPINFO) + opae_add_subdirectory(qsfpinfo) + endif() if(OPAE_BUILD_FPGADIAG) set(OPAE_BUILD_CXXUTILS ON) opae_add_subdirectory(fpgadiag) diff --git a/binaries/qsfpinfo/CMakeLists.txt b/binaries/qsfpinfo/CMakeLists.txt new file mode 100644 index 000000000000..32a70bb3e3cc --- /dev/null +++ b/binaries/qsfpinfo/CMakeLists.txt @@ -0,0 +1,73 @@ +## Copyright(c) 2024, Silciom Denmark A/S +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are met: +## +## * Redistributions of source code must retain the above copyright notice, +## this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright notice, +## this list of conditions and the following disclaimer in the documentation +## and/or other materials provided with the distribution. +## * Neither the name of Intel Corporation nor the names of its contributors +## may be used to endorse or promote products derived from this software +## without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +## AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +## ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +## LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +## POSSIBILITY OF SUCH DAMAGE. + +opae_add_executable(TARGET qsfpinfo + SOURCE + main.c + cmis_wrap.c + sff-common_wrap.c + LIBS + argsfilter + opae-c + m + COMPONENT toolqsfpinfo +) + +target_include_directories(qsfpinfo + PRIVATE + ${OPAE_LIB_SOURCE}/argsfilter +) + +include(ExternalProject) + +ExternalProject_Add( + ethtool_project + GIT_REPOSITORY "https://git.kernel.org/pub/scm/network/ethtool/ethtool.git" + GIT_TAG "v6.7" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + UPDATE_COMMAND "" +) + +ExternalProject_Get_Property(ethtool_project SOURCE_DIR) + +add_custom_target(get-ethtool-files + DEPENDS ethtool_project + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${SOURCE_DIR}/cmis.c" + "${SOURCE_DIR}/cmis.h" + "${SOURCE_DIR}/qsfp.c" + "${SOURCE_DIR}/qsfp.h" + "${SOURCE_DIR}/sff-common.c" + "${SOURCE_DIR}/sff-common.h" + "${SOURCE_DIR}/internal.h" + "${SOURCE_DIR}/json_print.h" + "${SOURCE_DIR}/json_writer.h" "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${SOURCE_DIR}/netlink/extapi.h" "${CMAKE_CURRENT_SOURCE_DIR}/netlink/extapi.h" + ) + +add_dependencies(qsfpinfo get-ethtool-files) diff --git a/binaries/qsfpinfo/cmis_wrap.c b/binaries/qsfpinfo/cmis_wrap.c new file mode 100644 index 000000000000..0ac4217c0d9d --- /dev/null +++ b/binaries/qsfpinfo/cmis_wrap.c @@ -0,0 +1,28 @@ +// Copyright(c) 2024, Silciom Denmark A/S +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#undef HAVE_CONFIG_H +#include "cmis.c" diff --git a/binaries/qsfpinfo/main.c b/binaries/qsfpinfo/main.c new file mode 100644 index 000000000000..15a8c801dbec --- /dev/null +++ b/binaries/qsfpinfo/main.c @@ -0,0 +1,412 @@ +// Copyright(c) 2024, Silciom Denmark A/S +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "argsfilter.h" + +#include +#include +#include + +// From ethtool +#undef HAVE_CONFIG_H +#include "qsfp.c" + +// Read QSFP SFF-8636 registers into memory and setup pointers so +// sff8636_show_all_common() from ethertool can print result +static int sff8636_show(const char * regmap_path) +{ + + struct sff8636_memory_map map = {}; + __u8 buffer[1024]; + int i = 0; // counting lines read + int bufp = 0; // counting bytes written to buffer + uint32_t value; + + char *line = NULL; + size_t len = 0; + ssize_t read; + char delim[] = ":"; + + FILE *fp = fopen (regmap_path, "r"); + if (!fp) { + OPAE_ERR("Could not open regmap: %s ", regmap_path); + return ENOENT; + } + + while ((read = getline(&line, &len, fp)) != -1) + { + //The first 4 lines are not used + if (i >= 4) + { + // Line has the form : <32 bit little endian value> + char *ptr = strtok(line, delim); + ptr = strtok(NULL, delim); + value = strtol(ptr, NULL, 16); + //printf("value: 0x%x\n", value); + uint32_t *buftemp = (uint32_t *) &buffer[bufp]; + *buftemp = value; + bufp += 4; + } + i++; + } + + map.lower_memory = buffer; // address 0x100 in regmap + map.page_00h = buffer + (0x200 - 0x80); //starts from beginning of lower page 128 bytes before + map.page_03h = buffer + 0x200; //starts 128 bytes before + + // Print info + sff8636_show_all_common(&map); + fclose (fp); + return 0; +} + +// Define the function to be called when ctrl-c (SIGINT) signal is sent to +// process +static volatile bool stop = false; +void signal_callback_handler(int signum) +{ + switch (signum) { + case SIGINT: + stop = true; + break; + default: + break; + } +} + +#define MAX_PORT 2 +#define NO_SPECIFIC_PORT (MAX_PORT+1) +#define MAX_CARDS 4 + +struct card { + fpga_properties filter; + fpga_token token[MAX_CARDS]; + int32_t port; + uint32_t cards_found; +}; + +static fpga_result fpga_open(struct card *card) +{ + fpga_result res; + res = fpgaPropertiesSetObjectType(card->filter, FPGA_DEVICE); + if (res != FPGA_OK) { + OPAE_ERR("failed to set object type: %s ", fpgaErrStr(res)); + goto error; + } + + res = fpgaEnumerate(&card->filter, 1, card->token, MAX_CARDS, &card->cards_found); + if (res != FPGA_OK) { + OPAE_ERR("failed to enumerate fpga: %s ", fpgaErrStr(res)); + goto error; + } + + if (card->cards_found == 0) { + res = FPGA_NOT_FOUND; + OPAE_ERR("failed to find fpga: %s ", fpgaErrStr(res)); + goto error; + } + +error: + return res; +} + +static void fpga_close(struct card *card) +{ + fpga_result res; + + for(uint32_t i = 0; i < card->cards_found; i++) { + res = fpgaDestroyToken(&card->token[i]); + if (res != FPGA_OK) + OPAE_ERR("failed to destroy token: %s ", fpgaErrStr(res)); + } +} + +// QSFP cable status +#define LPATH_MAX 100 +#define DFL_SYSFS_QSFP "*dfl*dev.%ld/qsfp_connected" +#define REGMAP_PATH "/sys/kernel/debug/regmap/dfl_dev.%ld/registers" +#define MAX_DEV_FEATURE_COUNT 256 + +// from opae board_common.c extended with sff8636_show() +fpga_result qsfp_cable_status(const fpga_token token, const size_t port) +{ + fpga_object fpga_object; + fpga_result res = FPGA_OK; + uint64_t value = 0; + size_t i = 0; + char qsfp_path[LPATH_MAX] = { 0 }; + char regmap_path[LPATH_MAX] = { 0 }; + int retval = 0; + size_t qsfp_count = 0; + + for (i = 0; i < MAX_DEV_FEATURE_COUNT; i++) { + + memset(qsfp_path, 0, sizeof(qsfp_path)); + + retval = snprintf(qsfp_path, sizeof(qsfp_path), + DFL_SYSFS_QSFP, i); + if (retval < 0) { + OPAE_MSG("error in formatting qsfp cable status"); + return FPGA_EXCEPTION; + } + + res = fpgaTokenGetObject(token, qsfp_path, + &fpga_object, FPGA_OBJECT_GLOB); + if (res != FPGA_OK) { + OPAE_MSG("Failed to get token Object"); + continue; + } + + res = fpgaObjectRead64(fpga_object, &value, 0); + if (res == FPGA_OK) { + if (port == NO_SPECIFIC_PORT || port == qsfp_count) { + OPAE_MSG("Failed to Read object "); + + switch (value) { + case 0: + printf("QSFP%-45ld : %s \n", qsfp_count, "Not Connected"); + break; + case 1: + printf("QSFP%-45ld : %s \n", qsfp_count, "Connected"); + retval = snprintf(regmap_path, sizeof(regmap_path), + REGMAP_PATH, i); + if (retval < 0) { + OPAE_MSG("error in formatting qsfp regmap"); + return FPGA_EXCEPTION; + } + sff8636_show(regmap_path); + break; + default: + printf("QSFP%-28ld : %s \n", qsfp_count, "N/A"); + } + } + qsfp_count++; + + } else { + OPAE_MSG("Failed to Read object "); + } + + res = fpgaDestroyObject(&fpga_object); + if (res != FPGA_OK) { + OPAE_MSG("Failed to Destroy Object"); + } + + } + + return res; +} + +// from opae board_common.c +fpga_result get_fpga_sbdf(fpga_token token, + uint16_t *segment, + uint8_t *bus, + uint8_t *device, + uint8_t *function) + +{ + fpga_result res = FPGA_OK; + fpga_properties props = NULL; + fpga_result resval = FPGA_OK; + + if (!segment || !bus || + !device || !function) { + OPAE_ERR("Invalid input parameters"); + return FPGA_INVALID_PARAM; + } + res = fpgaGetProperties(token, &props); + if (res != FPGA_OK) { + OPAE_ERR("Failed to get properties "); + return res; + } + + res = fpgaPropertiesGetBus(props, bus); + if (res != FPGA_OK) { + OPAE_ERR("Failed to get bus "); + resval = res; + goto out_destroy; + } + + res = fpgaPropertiesGetSegment(props, segment); + if (res != FPGA_OK) { + OPAE_ERR("Failed to get Segment "); + resval = res; + goto out_destroy; + } + res = fpgaPropertiesGetDevice(props, device); + if (res != FPGA_OK) { + OPAE_ERR("Failed to get Device "); + resval = res; + goto out_destroy; + } + + res = fpgaPropertiesGetFunction(props, function); + if (res != FPGA_OK) { + OPAE_ERR("Failed to get Function "); + resval = res; + goto out_destroy; + } + +out_destroy: + res = fpgaDestroyProperties(&props); + if (res != FPGA_OK) { + OPAE_ERR("Failed to destroy properties"); + } + + return resval; +} + + +static void print_usage(FILE *f) +{ + fprintf(f, + "\n" + "qsfpinfo\n" + "Print QSFP EEPROM information\n" + "\n"); + fprintf(f, "Usage:\n"); + fprintf(f, " qsfpinfo [-h] [-p ] [PCI_ADDR]\n"); + fprintf(f,"\n"); + fprintf(f, + " -p,--port Print only information for this QSFP [0..1]\n" + " -S,--segment Set target segment number\n" + " -B,--bus Set target bus number\n" + " -D,--device Set target device number\n" + " -F,--function Set target function number\n" + "\n"); +} + + +static bool parse_port(struct card *card, const char *port_str) +{ + char *endptr; + unsigned long int port = strtoul(port_str, &endptr, 0); + if (port_str == endptr) { + OPAE_ERR("missing port number: '%s'", port_str); + return false; + } + + if (port >= MAX_PORT) { + OPAE_ERR("port number too big: '%d'", port); + return false; + } + + card->port = port; + return true; +} + + +static int parse_args(int argc, char **argv, struct card *card) +{ + struct option options[] = { + {"help", no_argument, NULL, 'h'}, + {"port", required_argument, NULL, 'p'}, + {NULL, 0, NULL, 0}, + }; + + int c; + + while (1) { + c = getopt_long_only(argc, argv, "hg:m:p:d", options, NULL); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(stdout); + exit(EXIT_SUCCESS); + case 'p': + if (!parse_port(card, optarg)) + return EXIT_FAILURE; + break; + case '?': + default: + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +int main(int argc, char **argv) +{ + struct card card = {.filter = NULL, .port = NO_SPECIFIC_PORT, .cards_found = 0}; + uint16_t segment; + uint8_t bus; + uint8_t device; + uint8_t function; + fpga_result res; + int err; + + res = fpgaGetProperties(NULL, &card.filter); + if (res != FPGA_OK) { + OPAE_ERR("failed to get properties: %s ", fpgaErrStr(res)); + return res; + } + + if (opae_set_properties_from_args(card.filter, &res, &argc, argv)) { + OPAE_ERR("failed arg parse."); + res = FPGA_EXCEPTION; + goto error2; + } else if (res) { + OPAE_ERR("failed to set properties."); + goto error2; + } + + err = parse_args(argc, argv, &card); + if (err != 0) + goto error2; + + // Install Control-C handler + signal(SIGINT, signal_callback_handler); + + res = fpga_open(&card); + if (res != FPGA_OK) + goto error; + + for(uint32_t i = 0; i < card.cards_found; i++) { + get_fpga_sbdf(card.token[i], &segment, &bus, &device, &function); + printf("%-49s : %04X:%02X:%02X.%01X\n", "PCIe s:b:d.f", segment, bus, device, function); + res = qsfp_cable_status(card.token[i], card.port); + } + +error: + fpga_close(&card); + +error2: + fpgaDestroyProperties(&card.filter); + + return res == FPGA_OK ? EXIT_SUCCESS : res; +} diff --git a/binaries/qsfpinfo/sff-common_wrap.c b/binaries/qsfpinfo/sff-common_wrap.c new file mode 100644 index 000000000000..913b15ac847f --- /dev/null +++ b/binaries/qsfpinfo/sff-common_wrap.c @@ -0,0 +1,28 @@ +// Copyright(c) 2024, Silciom Denmark A/S +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of Intel Corporation nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#undef HAVE_CONFIG_H +#include "sff-common.c" diff --git a/doc/sphinx/conf.py.in b/doc/sphinx/conf.py.in index 4e99e66e2595..769de2b22884 100644 --- a/doc/sphinx/conf.py.in +++ b/doc/sphinx/conf.py.in @@ -164,6 +164,7 @@ man_pages = [ ("docs/fpga_tools/hssi/hssi", 'hssi', u'High Speed Serial Interface', [author], 8), ("docs/fpga_tools/opaevfio/opaevfio", 'opaevfio', u'Bind/Unbind accelerator to/from vfio-pci', [author], 8), ("docs/fpga_tools/userclk/userclk", 'userclk', u'Set AFU high and low clock frequency', [author], 8), + ("docs/fpga_tools/qsfpinfo/qsfpinfo", 'qsfpinfo', u'Print QSFP EEPROM information', [author], 8), ("docs/fpga_tools/pci_device/pci_device", 'pci_device', u'Common operations for PCIe devices', [author], 8), ("docs/fpga_tools/opae.io/opae.io", 'opae.io', u'User space access to PCIe devices', [author], 8), ("docs/fpga_tools/host_exerciser/host_exerciser", 'host_exerciser', u'Host Exerciser lpbk and memory', [author], 8), diff --git a/doc/sphinx/index.rst.in b/doc/sphinx/index.rst.in index 6f50e28eec86..c2d3c46f70d7 100644 --- a/doc/sphinx/index.rst.in +++ b/doc/sphinx/index.rst.in @@ -57,6 +57,7 @@ The main documentation for the site is organized into following sections: docs/fpga_tools/mmlink/mmlink docs/fpga_tools/packager/packager docs/fpga_tools/userclk/userclk + docs/fpga_tools/qsfpinfo/qsfpinfo docs/fpga_tools/hssi/hssi docs/fpga_tools/opaevfio/opaevfio docs/fpga_tools/pci_device/pci_device diff --git a/doc/src/fpga_tools/qsfpinfo/qsfpinfo.md b/doc/src/fpga_tools/qsfpinfo/qsfpinfo.md new file mode 100644 index 000000000000..6068f78c65b5 --- /dev/null +++ b/doc/src/fpga_tools/qsfpinfo/qsfpinfo.md @@ -0,0 +1,96 @@ +# qsfpinfo # + +## SYNOPSIS ## + +`qsfpinfo [-h] [-p ] [-S ] [-B ] [-D ] [-F ] [PCI_ADDR]` + + +## DESCRIPTION ## + +qsfpinfo prints QSFP EEPROM information for QSFP modules present in cards with qsfp feature (id 0x13) e.g. N6010, N6011, using the qsfp-mem driver. + +## EXAMPLES ## + +```sh +>sudo qsfpinfo -p 0 0000:98:00.0` +PCIe s:b:d.f : 0000:98:00.0 +QSFP0 : Connected + Identifier : 0x11 (QSFP28) + Extended identifier : 0xcc + Extended identifier description : 3.5W max. Power consumption + Extended identifier description : CDR present in TX, CDR present in RX + Extended identifier description : High Power Class (> 3.5 W) not enabled + Power set : Off + Power override : Off + Connector : 0x0c (MPO Parallel Optic) + Transceiver codes : 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + Transceiver type : 100G Ethernet: 100G Base-SR4 or 25GBase-SR + Encoding : 0x07 ((256B/257B (transcoded FEC-enabled data)) + BR, Nominal : 25500Mbps + Rate identifier : 0x00 + Length (SMF,km) : 0km + Length (OM3 50um) : 70m + Length (OM2 50um) : 0m + Length (OM1 62.5um) : 0m + Length (Copper or Active cable) : 50m + Transmitter technology : 0x00 (850 nm VCSEL) + Laser wavelength : 850.000nm + Laser wavelength tolerance : 10.000nm + Vendor name : FINISAR CORP + Vendor OUI : 00:90:65 + Vendor PN : FTLC9551REPM + Vendor rev : A0 + Vendor SN : XYH0RPZ + Date code : 180512 + Revision Compliance : SFF-8636 Rev 2.5/2.6/2.7 + Rx loss of signal : None + Tx loss of signal : None + Rx loss of lock : [ Yes, Yes, Yes, Yes ] + Tx loss of lock : [ Yes, Yes, Yes, Yes ] + Tx fault : None + Module temperature : 45.22 degrees C / 113.39 degrees F + Module voltage : 3.3359 V + Alarm/warning flags implemented : Yes + Laser tx bias current (Channel 1) : 8.162 mA + Laser tx bias current (Channel 2) : 7.560 mA + Laser tx bias current (Channel 3) : 7.796 mA + Laser tx bias current (Channel 4) : 7.708 mA + Transmit avg optical power (Channel 1) : 1.0391 mW / 0.17 dBm + Transmit avg optical power (Channel 2) : 1.0704 mW / 0.30 dBm + Transmit avg optical power (Channel 3) : 1.0704 mW / 0.30 dBm + Transmit avg optical power (Channel 4) : 0.9449 mW / -0.25 dBm + Rcvr signal avg optical power(Channel 1) : 0.9603 mW / -0.18 dBm + Rcvr signal avg optical power(Channel 2) : 0.9210 mW / -0.36 dBm + Rcvr signal avg optical power(Channel 3) : 1.0281 mW / 0.12 dBm + Rcvr signal avg optical power(Channel 4) : 1.0220 mW / 0.09 dBm +... +``` + +## OPTIONS ## + +`-p,--port` + +Print only information for this QSFP [0..1] + +`-S,--segment` + +FPGA segment number. + +`-B,--bus` + +FPGA Bus number. + +`-D,--device` + +FPGA Device number. + +`-F,--function` + +FPGA function number. + +## Revision History ## + +| Date | Intel Acceleration Stack Version | Changes Made | +|:------|----------------------------|:--------------| +|2024.04.15| IOFS EA | Initial release. | + diff --git a/opae.spec.fedora b/opae.spec.fedora index 4e4e0be3c119..26b24ca0f70c 100644 --- a/opae.spec.fedora +++ b/opae.spec.fedora @@ -93,7 +93,7 @@ Additional OPAE tools -DFETCHCONTENT_FULLY_DISCONNECTED=ON \ -DFETCHCONTENT_BASE_DIR=_build/_deps \ -DOPAE_BUILD_PYTHON_DIST=ON \ - -DOPAE_BUILD_FPGABIST=ON + -DOPAE_BUILD_FPGABIST=ON %if 0%{?rhel} %if 0%{rhel} <= 8 @@ -289,6 +289,7 @@ done %{_bindir}/super-rsu %{_bindir}/mmlink %{_bindir}/userclk +%{_bindir}/qsfpinfo %{_bindir}/hello_fpga %{_bindir}/object_api %{_bindir}/hello_events @@ -385,7 +386,7 @@ done - Various Static code scan bug fixes - Added python3 support. - OPAE USMG API are deprecated. -- Updated OPAE documentation. +- Updated OPAE documentation. * Tue Dec 17 2019 Korde Nakul 1.4.0-1 - Added support to FPGA Linux kernel Device Feature List (DFL) driver patch set2. diff --git a/opae.spec.in b/opae.spec.in index e81cdc6475d9..76127e4dd9ce 100644 --- a/opae.spec.in +++ b/opae.spec.in @@ -215,6 +215,7 @@ ldconfig @CMAKE_INSTALL_PREFIX@/bin/packager @CMAKE_INSTALL_PREFIX@/bin/pac_hssi_config.py @CMAKE_INSTALL_PREFIX@/bin/userclk +@CMAKE_INSTALL_PREFIX@/bin/qsfpinfo @CMAKE_INSTALL_PREFIX@/bin/hps @CMAKE_INSTALL_PREFIX@/bin/vabtool @CMAKE_INSTALL_PREFIX@/@OPAE_LIB_INSTALL_DIR@/libmml-srv.so* diff --git a/opae.spec.rhel b/opae.spec.rhel index 82b1a4d1e50d..e6c7770830e6 100644 --- a/opae.spec.rhel +++ b/opae.spec.rhel @@ -285,6 +285,7 @@ done %{_bindir}/super-rsu %{_bindir}/mmlink %{_bindir}/userclk +%{_bindir}/qsfpinfo %{_bindir}/hello_fpga %{_bindir}/object_api %{_bindir}/hello_events @@ -338,7 +339,7 @@ done - Various Static code scan bug fixes - Added python3 support. - OPAE USMG API are deprecated. -- Updated OPAE documentation. +- Updated OPAE documentation. * Tue Dec 17 2019 Korde Nakul 1.4.0-1 - Added support to FPGA Linux kernel Device Feature List (DFL) driver patch set2. diff --git a/packaging/opae/deb/opae-devel.install b/packaging/opae/deb/opae-devel.install index cc4358c0ad2f..c84c7e982dda 100644 --- a/packaging/opae/deb/opae-devel.install +++ b/packaging/opae/deb/opae-devel.install @@ -30,6 +30,7 @@ usr/bin/fpgaport usr/bin/super-rsu usr/bin/mmlink usr/bin/userclk +usr/bin/qsfpinfo usr/bin/hello_fpga usr/bin/object_api usr/bin/hello_events diff --git a/scripts/opae-clean.py b/scripts/opae-clean.py index 9f7a2972a3a8..c5774c4f15f8 100755 --- a/scripts/opae-clean.py +++ b/scripts/opae-clean.py @@ -449,6 +449,7 @@ def infer_type(expanded): 'super-rsu', 'mmlink', 'userclk', + 'qsfpinfo', 'hello_fpga', 'object_api', 'hello_events', From e7ae66e606d7451425a00fa2d523dce3de0dc574 Mon Sep 17 00:00:00 2001 From: Roger Christensen Date: Fri, 26 Apr 2024 18:06:20 +0200 Subject: [PATCH 3/6] qsfpinfo: only include printing qsfp information (include source from ethertool) if OPAE-SDK is compiled with option OPAE_WITH_QSFPINFO_QSFPPRINT Signed-off-by: Roger Christensen --- CMakeLists.txt | 3 + binaries/qsfpinfo/CMakeLists.txt | 101 ++++++++++++++---------- binaries/qsfpinfo/main.c | 7 +- doc/src/fpga_tools/qsfpinfo/qsfpinfo.md | 3 +- 4 files changed, 72 insertions(+), 42 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ad4d16bea76e..783e5d5084d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,9 @@ mark_as_advanced(OPAE_MINIMAL_BUILD) option(OPAE_BUILD_TESTS "Enable building of OPAE unit tests" OFF) mark_as_advanced(OPAE_BUILD_TESTS) +option(OPAE_WITH_QSFPINFO_QSFPPRINT "Enable qsfpinfo print qsfp" OFF) +mark_as_advanced(OPAE_WITH_QSFPINFO_QSFPPRINT) + ############################################################################ ## Python Interpreter/Build Env ########################################### ############################################################################ diff --git a/binaries/qsfpinfo/CMakeLists.txt b/binaries/qsfpinfo/CMakeLists.txt index 32a70bb3e3cc..085ff8589434 100644 --- a/binaries/qsfpinfo/CMakeLists.txt +++ b/binaries/qsfpinfo/CMakeLists.txt @@ -24,50 +24,71 @@ ## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ## POSSIBILITY OF SUCH DAMAGE. -opae_add_executable(TARGET qsfpinfo - SOURCE - main.c - cmis_wrap.c - sff-common_wrap.c - LIBS - argsfilter - opae-c - m - COMPONENT toolqsfpinfo -) +if (OPAE_WITH_QSFPINFO_QSFPPRINT) -target_include_directories(qsfpinfo - PRIVATE - ${OPAE_LIB_SOURCE}/argsfilter -) + opae_add_executable(TARGET qsfpinfo + SOURCE + main.c + cmis_wrap.c + sff-common_wrap.c + LIBS + argsfilter + opae-c + m + COMPONENT toolqsfpinfo + ) -include(ExternalProject) + include(ExternalProject) -ExternalProject_Add( - ethtool_project - GIT_REPOSITORY "https://git.kernel.org/pub/scm/network/ethtool/ethtool.git" - GIT_TAG "v6.7" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - UPDATE_COMMAND "" -) + ExternalProject_Add( + ethtool_project + GIT_REPOSITORY "https://git.kernel.org/pub/scm/network/ethtool/ethtool.git" + GIT_TAG "v6.7" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + UPDATE_COMMAND "" + ) + + ExternalProject_Get_Property(ethtool_project SOURCE_DIR) + + add_custom_target(get-ethtool-files + DEPENDS ethtool_project + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${SOURCE_DIR}/cmis.c" + "${SOURCE_DIR}/cmis.h" + "${SOURCE_DIR}/qsfp.c" + "${SOURCE_DIR}/qsfp.h" + "${SOURCE_DIR}/sff-common.c" + "${SOURCE_DIR}/sff-common.h" + "${SOURCE_DIR}/internal.h" + "${SOURCE_DIR}/json_print.h" + "${SOURCE_DIR}/json_writer.h" "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${SOURCE_DIR}/netlink/extapi.h" "${CMAKE_CURRENT_SOURCE_DIR}/netlink/extapi.h" + ) + + target_compile_options(qsfpinfo PUBLIC + -D_QSFPPRINT + ) -ExternalProject_Get_Property(ethtool_project SOURCE_DIR) + add_dependencies(qsfpinfo get-ethtool-files) -add_custom_target(get-ethtool-files - DEPENDS ethtool_project - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "${SOURCE_DIR}/cmis.c" - "${SOURCE_DIR}/cmis.h" - "${SOURCE_DIR}/qsfp.c" - "${SOURCE_DIR}/qsfp.h" - "${SOURCE_DIR}/sff-common.c" - "${SOURCE_DIR}/sff-common.h" - "${SOURCE_DIR}/internal.h" - "${SOURCE_DIR}/json_print.h" - "${SOURCE_DIR}/json_writer.h" "${CMAKE_CURRENT_SOURCE_DIR}" - COMMAND ${CMAKE_COMMAND} -E copy_if_different "${SOURCE_DIR}/netlink/extapi.h" "${CMAKE_CURRENT_SOURCE_DIR}/netlink/extapi.h" +else(OPAE_WITH_QSFPINFO_QSFPPRINT) + + opae_add_executable(TARGET qsfpinfo + SOURCE + main.c + LIBS + argsfilter + opae-c + m + COMPONENT toolqsfpinfo ) -add_dependencies(qsfpinfo get-ethtool-files) +endif(OPAE_WITH_QSFPINFO_QSFPPRINT) + +target_include_directories(qsfpinfo + PRIVATE + ${OPAE_LIB_SOURCE}/argsfilter +) + diff --git a/binaries/qsfpinfo/main.c b/binaries/qsfpinfo/main.c index 15a8c801dbec..e218ca9120b6 100644 --- a/binaries/qsfpinfo/main.c +++ b/binaries/qsfpinfo/main.c @@ -40,6 +40,7 @@ #include #include +#ifdef QSFPPRINT // From ethtool #undef HAVE_CONFIG_H #include "qsfp.c" @@ -92,6 +93,7 @@ static int sff8636_show(const char * regmap_path) fclose (fp); return 0; } +#endif // Define the function to be called when ctrl-c (SIGINT) signal is sent to // process @@ -168,7 +170,6 @@ fpga_result qsfp_cable_status(const fpga_token token, const size_t port) uint64_t value = 0; size_t i = 0; char qsfp_path[LPATH_MAX] = { 0 }; - char regmap_path[LPATH_MAX] = { 0 }; int retval = 0; size_t qsfp_count = 0; @@ -201,6 +202,9 @@ fpga_result qsfp_cable_status(const fpga_token token, const size_t port) break; case 1: printf("QSFP%-45ld : %s \n", qsfp_count, "Connected"); + +#ifdef QSFPPRINT + char regmap_path[LPATH_MAX] = { 0 }; retval = snprintf(regmap_path, sizeof(regmap_path), REGMAP_PATH, i); if (retval < 0) { @@ -208,6 +212,7 @@ fpga_result qsfp_cable_status(const fpga_token token, const size_t port) return FPGA_EXCEPTION; } sff8636_show(regmap_path); +#endif break; default: printf("QSFP%-28ld : %s \n", qsfp_count, "N/A"); diff --git a/doc/src/fpga_tools/qsfpinfo/qsfpinfo.md b/doc/src/fpga_tools/qsfpinfo/qsfpinfo.md index 6068f78c65b5..117193efe8d5 100644 --- a/doc/src/fpga_tools/qsfpinfo/qsfpinfo.md +++ b/doc/src/fpga_tools/qsfpinfo/qsfpinfo.md @@ -9,6 +9,7 @@ qsfpinfo prints QSFP EEPROM information for QSFP modules present in cards with qsfp feature (id 0x13) e.g. N6010, N6011, using the qsfp-mem driver. +qsfpinfo only prints QSFP EEPROM information if OPAE-SDK is compiled with "-DOPAE_WITH_QSFPINFO_QSFPPRINT=ON". ## EXAMPLES ## ```sh @@ -92,5 +93,5 @@ FPGA function number. | Date | Intel Acceleration Stack Version | Changes Made | |:------|----------------------------|:--------------| -|2024.04.15| IOFS EA | Initial release. | +|2024.04.26| IOFS EA | Initial release. | From 975a628a5c313c6b82e570fbe45e00b62861c8fb Mon Sep 17 00:00:00 2001 From: Peter Colberg Date: Fri, 19 Jul 2024 19:31:19 -0400 Subject: [PATCH 4/6] mem_tg: fix support for more than 8 memory channels The sizeof() operator returns the size of an object or type in bytes, not bits. Instead of calculating the number of bits using, e.g., CHAR_BIT * sizeof(), hard-code 64 since it is close to the declaration. This is a prerequisite for HBM support with up to 32 channels. Link: https://github.com/OFS/opae-sdk/issues/3137 Fixes: 7bf96dc98995 ("mem_tg test multichannel") Signed-off-by: Peter Colberg --- samples/mem_tg/tg_test.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/mem_tg/tg_test.h b/samples/mem_tg/tg_test.h index 770e3b573606..19db8c0c4a83 100644 --- a/samples/mem_tg/tg_test.h +++ b/samples/mem_tg/tg_test.h @@ -244,8 +244,8 @@ class tg_test : public test_command int num_channels = 0; if ((tg_exe_->mem_ch_[0]).find("all") == 0) { uint64_t mem_capability = tg_exe_->read64(MEM_TG_CTRL); - channels = new int[sizeof(uint64_t)]; // size should be same as mem_capability - for (uint32_t i = 0; i < sizeof(uint64_t); i++) { // number of iterations should be same as mem_capability + channels = new int[64]; // size should be same as mem_capability + for (uint32_t i = 0; i < 64; i++) { // number of iterations should be same as mem_capability if ((mem_capability & (1ULL << i)) != 0) { channels[num_channels] = i; num_channels += 1; From a132211bba3438d981229d6382bc160c71a206ad Mon Sep 17 00:00:00 2001 From: Peter Colberg Date: Fri, 19 Jul 2024 20:09:21 -0400 Subject: [PATCH 5/6] mem_tg: refactor tg_test() to use std::vector for channels Replace manual memory allocation using new[] and delete[] with std::vector, which avoids unintended memory leaks. Drop unneeded vectors for per-thread promise and tg_exe, which may be stored as local variables inside the lambda function. The lambda is declared as mutable to permit moving the promise. Signed-off-by: Peter Colberg --- samples/mem_tg/tg_test.h | 63 ++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 41 deletions(-) diff --git a/samples/mem_tg/tg_test.h b/samples/mem_tg/tg_test.h index 19db8c0c4a83..45d12fcc3141 100644 --- a/samples/mem_tg/tg_test.h +++ b/samples/mem_tg/tg_test.h @@ -239,72 +239,53 @@ class tg_test : public test_command exit(1); } - // Parse mem_ch_ into array of selected channels and number of channels - int *channels = NULL; - int num_channels = 0; + // Parse mem_ch_ into array of selected channels + std::vector channels; if ((tg_exe_->mem_ch_[0]).find("all") == 0) { uint64_t mem_capability = tg_exe_->read64(MEM_TG_CTRL); - channels = new int[64]; // size should be same as mem_capability for (uint32_t i = 0; i < 64; i++) { // number of iterations should be same as mem_capability if ((mem_capability & (1ULL << i)) != 0) { - channels[num_channels] = i; - num_channels += 1; + channels.emplace_back(i); } } - channels[num_channels] = -1; // EOL } else { - channels = new int[tg_exe_->mem_ch_.size()]; - num_channels = tg_exe_->mem_ch_.size(); - try{ - for (unsigned i = 0; i < tg_exe_->mem_ch_.size(); i++) { - channels[i] = std::stoi(tg_exe_->mem_ch_[i]); + try { + for (const std::string &mem_ch: tg_exe_->mem_ch_) { + channels.emplace_back(std::stoi(mem_ch)); } } catch (std::invalid_argument &e) { std::cerr << "Error: invalid argument to std::stoi"; - delete[] channels; return 1; } } - - // Spawn threads for each channel: - mem_tg *thread_tg_exe_objects[num_channels]; + + // Spawn threads for each channel std::vector> futures; - std::vector> promises(num_channels); std::vector threads; - tg_num_threads = num_channels; + tg_num_threads = channels.size(); tg_waiting_threads_counter = 0; - for (int i = 0; i < num_channels; i++) { - if (channels[i] == -1) break; - thread_tg_exe_objects[i] = new mem_tg; - tg_exe_->duplicate(thread_tg_exe_objects[i]); - thread_tg_exe_objects[i]->mem_ch_.clear(); - thread_tg_exe_objects[i]->mem_ch_.push_back(std::to_string(channels[i])); - futures.push_back(promises[i].get_future()); - threads.emplace_back([&, i] { - promises[i].set_value(run_thread_single_channel(thread_tg_exe_objects[i])); + for (auto c: channels) { + std::promise p; + futures.emplace_back(p.get_future()); + threads.emplace_back([this, c, p = std::move(p)]() mutable { + mem_tg tg_exe; + tg_exe_->duplicate(&tg_exe); + tg_exe.mem_ch_.clear(); + tg_exe.mem_ch_.push_back(std::to_string(c)); + p.set_value(run_thread_single_channel(&tg_exe)); }); } - // Wait for all threads to exit then collect their exit statuses + // Wait for all threads to exit for (auto &thread : threads) { thread.join(); } - - std::vector exit_codes; - for (auto &future : futures) { - exit_codes.push_back(future.get()); - } - // Print message showing thread statuses - for (int i = 0; i < num_channels; i++) { - std::cout << "Thread on channel " << channels[i] << " exited with status " << (long)exit_codes[i] << std::endl; + for (size_t i = 0; i < channels.size(); i++) { + int ret = futures[i].get(); + std::cout << "Thread on channel " << channels[i] << " exited with status " << ret << std::endl; } - // Delete dynamic allocations - delete[] channels; - for (int i = 0; i < num_channels; i++) { - delete thread_tg_exe_objects[i]; - } return 0; } From a70f5c0a30f216e780b9305c09826b4def514c66 Mon Sep 17 00:00:00 2001 From: Peter Colberg Date: Wed, 17 Jul 2024 19:37:32 -0400 Subject: [PATCH 6/6] mem_tg: declare methods called within each thread as const This ensures that shared object members are not modified by any thread. Signed-off-by: Peter Colberg --- samples/mem_tg/tg_test.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/mem_tg/tg_test.h b/samples/mem_tg/tg_test.h index 45d12fcc3141..31d323ae91aa 100644 --- a/samples/mem_tg/tg_test.h +++ b/samples/mem_tg/tg_test.h @@ -75,13 +75,13 @@ class tg_test : public test_command } // Convert number of transactions to bandwidth (GB/s) - double bw_calc(uint64_t xfer_bytes, uint64_t num_ticks) + double bw_calc(uint64_t xfer_bytes, uint64_t num_ticks) const { return (double)(xfer_bytes) / ((1000.0 / (double)tg_exe_->mem_speed_ * (double)num_ticks)); } - void tg_perf (mem_tg *tg_exe_) { - + void tg_perf (mem_tg *tg_exe_) const + { // Lock mutex before printing so print statements don't collide between threads. std::unique_lock print_lock(tg_print_mutex); std::cout << "Channel " << std::stoi(tg_exe_->mem_ch_[0]) << ":" << std::endl; @@ -121,7 +121,7 @@ class tg_test : public test_command print_lock.unlock(); } - bool tg_wait_test_completion (mem_tg *tg_exe_) + bool tg_wait_test_completion (mem_tg *tg_exe_) const { /* Wait for test completion */ uint32_t timeout = MEM_TG_TEST_TIMEOUT * tg_exe_->loop_ * tg_exe_->bcnt_; @@ -152,7 +152,7 @@ class tg_test : public test_command return true; } - int config_input_options(mem_tg *tg_exe_) + int config_input_options(mem_tg *tg_exe_) const { if (!tg_exe_) return -1; @@ -178,7 +178,7 @@ class tg_test : public test_command } // The test state has been configured. Run one test instance. - int run_mem_test(mem_tg *tg_exe_) + int run_mem_test(mem_tg *tg_exe_) const { int status = 0;