From e4ac75e622f29f5bdbf486e91081b121ce29387f Mon Sep 17 00:00:00 2001 From: Lucian Petrica Date: Fri, 3 Jul 2026 16:04:29 +0100 Subject: [PATCH 1/2] Add shell ID from git hash + dirty bit; report it in SMI --- linker/slashkit/emit/hw/project_gen.py | 49 +++++++++++++- .../resources/base/scripts/create_project.tcl | 6 ++ .../slashkit/resources/base/scripts/top.tcl | 51 ++++++++++++++- smi/src/CMakeLists.txt | 1 + smi/src/inspect.cpp | 23 ++++++- smi/src/list.cpp | 27 ++++++++ smi/src/shell_build_id.cpp | 64 +++++++++++++++++++ smi/src/shell_build_id.hpp | 59 +++++++++++++++++ 8 files changed, 276 insertions(+), 4 deletions(-) create mode 100644 smi/src/shell_build_id.cpp create mode 100644 smi/src/shell_build_id.hpp diff --git a/linker/slashkit/emit/hw/project_gen.py b/linker/slashkit/emit/hw/project_gen.py index 80fbef62..cd6f05a8 100644 --- a/linker/slashkit/emit/hw/project_gen.py +++ b/linker/slashkit/emit/hw/project_gen.py @@ -218,6 +218,50 @@ def generate_base_pdi_with_aved(config: CommandConfiguration) -> Path: return aved_pdi +def _compute_build_id_env() -> Dict[str, str]: + """ + Derive the shell build-ID constants from the git commit of the SLASH source + tree and return them as environment variables consumed by create_project.tcl. + + Encoding (63-bit hash + dirty), split across two 32-bit GPIO channels: + - SLASH_BUILD_ID_LO = low 32 bits of the SHA-1 + - SLASH_BUILD_ID_HI = next 31 bits in bits[30:0], dirty flag in bit[31] + + Falls back to hash 0 with the dirty bit set when git information is + unavailable (e.g. building from an exported tarball, not a git checkout). + """ + repo_dir = Path(__file__).resolve().parents[3] + + def _git(*args: str) -> Optional[str]: + try: + out = subprocess.run( + ["git", "-C", str(repo_dir), *args], + check=True, capture_output=True, text=True, + ) + return out.stdout.strip() + except (subprocess.CalledProcessError, FileNotFoundError): + return None + + sha = _git("rev-parse", "HEAD") + if sha is None: + logger.warning("Not a git checkout; shell build-ID will be 0 (dirty).") + return {"SLASH_BUILD_ID_LO": "0x0", "SLASH_BUILD_ID_HI": "0x80000000"} + + # `git diff --quiet` exits non-zero when the working tree has changes. + dirty = subprocess.run( + ["git", "-C", str(repo_dir), "diff", "--quiet"] + ).returncode != 0 + + sha_int = int(sha, 16) + lo = sha_int & 0xFFFFFFFF + hi = (sha_int >> 32) & 0x7FFFFFFF + if dirty: + hi |= 0x80000000 + + logger.info("Shell build-ID: commit %s%s", sha[:14], " (dirty)" if dirty else "") + return {"SLASH_BUILD_ID_LO": f"0x{lo:08x}", "SLASH_BUILD_ID_HI": f"0x{hi:08x}"} + + def create_build_project( config: CommandConfiguration, action: Optional[str] = None @@ -246,8 +290,11 @@ def create_build_project( cmd.append(str(config.n_jobs)) + env = _environment_with_udev_ld_preload() + env.update(_compute_build_id_env()) + subprocess.run(cmd, cwd=str(config.build_dir), check=True, - env=_environment_with_udev_ld_preload()) + env=env) class RM_KIND(Enum): diff --git a/linker/slashkit/resources/base/scripts/create_project.tcl b/linker/slashkit/resources/base/scripts/create_project.tcl index 85474818..2366eb3d 100644 --- a/linker/slashkit/resources/base/scripts/create_project.tcl +++ b/linker/slashkit/resources/base/scripts/create_project.tcl @@ -44,6 +44,12 @@ set iprepos $default_iprepos set action "all" set jobs "14" +# Shell build-ID constants injected into the static-region GPIO register +# (top.tcl reads these globals). Set from the git commit at build time via +# SLASH_BUILD_ID_LO/HI; default to 0 so manual/interactive runs still work. +set ::build_id_lo [expr {[info exists ::env(SLASH_BUILD_ID_LO)] ? $::env(SLASH_BUILD_ID_LO) : 0}] +set ::build_id_hi [expr {[info exists ::env(SLASH_BUILD_ID_HI)] ? $::env(SLASH_BUILD_ID_HI) : 0}] + if {[llength $argv] > 0} { set project_name [lindex $argv 0] set remaining_args [lrange $argv 1 end] diff --git a/linker/slashkit/resources/base/scripts/top.tcl b/linker/slashkit/resources/base/scripts/top.tcl index 87efb9e9..d27af218 100644 --- a/linker/slashkit/resources/base/scripts/top.tcl +++ b/linker/slashkit/resources/base/scripts/top.tcl @@ -59,6 +59,11 @@ if { $list_projs eq "" } { create_project project_1 myproj -part xcv80-lsva4737-2MHP-e-S } +# Shell build-ID constants for the static-region GPIO register. Normally set by +# create_project.tcl from the git commit; default to 0 if sourced standalone. +if { ![info exists ::build_id_lo] } { set ::build_id_lo 0 } +if { ![info exists ::build_id_hi] } { set ::build_id_hi 0 } + # CHANGE DESIGN NAME HERE variable design_name @@ -153,6 +158,7 @@ xilinx.com:ip:cmd_queue:2.0\ xilinx.com:ip:axi_gpio:2.0\ xilinx.com:ip:xlconcat:2.1\ xilinx.com:ip:util_reduced_logic:2.0\ +xilinx.com:ip:xlconstant:1.1\ " set list_ips_missing "" @@ -701,11 +707,43 @@ proc create_hier_cell_clk_rst_shell { parentCell nameHier } { # Create instance: smartconnect_0, and set properties set smartconnect_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:smartconnect:1.0 smartconnect_0 ] set_property -dict [list \ - CONFIG.NUM_MI {2} \ + CONFIG.NUM_MI {3} \ CONFIG.NUM_SI {1} \ ] $smartconnect_0 + # Create instance: build_id_gpio, and set properties. + # Read-only shell build-ID register: git commit hash (with dirty flag) baked in + # via constant blocks. Dual-channel AXI GPIO, both channels inputs. + set build_id_gpio [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_gpio:2.0 build_id_gpio ] + set_property -dict [list \ + CONFIG.C_ALL_INPUTS {1} \ + CONFIG.C_ALL_INPUTS_2 {1} \ + CONFIG.C_GPIO2_WIDTH {32} \ + CONFIG.C_GPIO_WIDTH {32} \ + CONFIG.C_IS_DUAL {1} \ + ] $build_id_gpio + + + # Create instance: build_id_lo, and set properties. + # Low 32 bits of the git SHA-1. Value injected at build time via ::build_id_lo. + set build_id_lo [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 build_id_lo ] + set_property -dict [list \ + CONFIG.CONST_WIDTH {32} \ + CONFIG.CONST_VAL $::build_id_lo \ + ] $build_id_lo + + + # Create instance: build_id_hi, and set properties. + # Bits [30:0] = next 31 bits of the git SHA-1; bit [31] = dirty flag. + # Value injected at build time via ::build_id_hi. + set build_id_hi [ create_bd_cell -type ip -vlnv xilinx.com:ip:xlconstant:1.1 build_id_hi ] + set_property -dict [list \ + CONFIG.CONST_WIDTH {32} \ + CONFIG.CONST_VAL $::build_id_hi \ + ] $build_id_hi + + # Create instance: clk_wizard_slash, and set properties set clk_wizard_slash [ create_bd_cell -type ip -vlnv xilinx.com:ip:clk_wizard:1.0 clk_wizard_slash ] set_property -dict [list \ @@ -781,12 +819,18 @@ proc create_hier_cell_clk_rst_shell { parentCell nameHier } { connect_bd_intf_net -intf_net axi_noc_0_M00_AXI [get_bd_intf_pins smartconnect_0/S00_AXI] [get_bd_intf_pins axi_noc_0/M00_AXI] connect_bd_intf_net -intf_net smartconnect_0_M00_AXI [get_bd_intf_pins clk_wizard_slash/s_axi_lite] [get_bd_intf_pins smartconnect_0/M00_AXI] connect_bd_intf_net -intf_net smartconnect_0_M01_AXI [get_bd_intf_pins clk_wizard_service/s_axi_lite] [get_bd_intf_pins smartconnect_0/M01_AXI] + connect_bd_intf_net -intf_net smartconnect_0_M02_AXI [get_bd_intf_pins build_id_gpio/S_AXI] [get_bd_intf_pins smartconnect_0/M02_AXI] # Create port connections + connect_bd_net -net build_id_lo_dout [get_bd_pins build_id_lo/dout] \ + [get_bd_pins build_id_gpio/gpio_io_i] + connect_bd_net -net build_id_hi_dout [get_bd_pins build_id_hi/dout] \ + [get_bd_pins build_id_gpio/gpio2_io_i] connect_bd_net -net aresetn_1 [get_bd_pins aresetn] \ [get_bd_pins smartconnect_0/aresetn] \ [get_bd_pins clk_wizard_slash/s_axi_aresetn] \ [get_bd_pins clk_wizard_service/s_axi_aresetn] \ + [get_bd_pins build_id_gpio/s_axi_aresetn] \ [get_bd_pins proc_sys_reset_0/ext_reset_in] \ [get_bd_pins proc_sys_reset_1/ext_reset_in] connect_bd_net -net clk_wizard_service_clk_out1 [get_bd_pins clk_wizard_service/clk_out1] \ @@ -805,7 +849,8 @@ proc create_hier_cell_clk_rst_shell { parentCell nameHier } { [get_bd_pins axi_noc_0/aclk0] \ [get_bd_pins smartconnect_0/aclk] \ [get_bd_pins clk_wizard_slash/s_axi_aclk] \ - [get_bd_pins clk_wizard_service/s_axi_aclk] + [get_bd_pins clk_wizard_service/s_axi_aclk] \ + [get_bd_pins build_id_gpio/s_axi_aclk] connect_bd_net -net pl3_ref_clk_1 [get_bd_pins refclk] \ [get_bd_pins clk_wizard_slash/clk_in1] \ [get_bd_pins clk_wizard_service/clk_in1] @@ -4953,6 +4998,7 @@ proc create_root_design { parentCell } { assign_bd_address -offset 0x000102100000 -range 0x00010000 -target_address_space [get_bd_addr_spaces static_region/aved/cips/CPM_PCIE_NOC_0] [get_bd_addr_segs static_region/aved/cips/NOC_PMC_AXI_0/pspmc_0_psv_pmc_slave_boot_stream] -force assign_bd_address -offset 0x020400010000 -range 0x00010000 -target_address_space [get_bd_addr_spaces static_region/aved/cips/CPM_PCIE_NOC_0] [get_bd_addr_segs static_region/clk_rst_shell/clk_wizard_service/s_axi_lite/Reg] -force assign_bd_address -offset 0x020400000000 -range 0x00010000 -target_address_space [get_bd_addr_spaces static_region/aved/cips/CPM_PCIE_NOC_0] [get_bd_addr_segs static_region/clk_rst_shell/clk_wizard_slash/s_axi_lite/Reg] -force + assign_bd_address -offset 0x020400020000 -range 0x00010000 -target_address_space [get_bd_addr_spaces static_region/aved/cips/CPM_PCIE_NOC_0] [get_bd_addr_segs static_region/clk_rst_shell/build_id_gpio/S_AXI/Reg] -force assign_bd_address -offset 0x020302000000 -range 0x00040000 -target_address_space [get_bd_addr_spaces static_region/aved/cips/CPM_PCIE_NOC_0] [get_bd_addr_segs service_layer/qsfp_0_n_1/DCMAC_subsys/dcmac_0_core/s_axi/Reg] -force assign_bd_address -offset 0x020303000000 -range 0x00040000 -target_address_space [get_bd_addr_spaces static_region/aved/cips/CPM_PCIE_NOC_0] [get_bd_addr_segs service_layer/qsfp_2_n_3/DCMAC_subsys/dcmac_1_core/s_axi/Reg] -force assign_bd_address -offset 0x020200480000 -range 0x00010000 -target_address_space [get_bd_addr_spaces static_region/aved/cips/CPM_PCIE_NOC_0] [get_bd_addr_segs slash/ddr_bandwidth_64/s_axi_control/Reg] -force @@ -5109,6 +5155,7 @@ proc create_root_design { parentCell } { assign_bd_address -offset 0x000102100000 -range 0x00010000 -target_address_space [get_bd_addr_spaces static_region/aved/cips/CPM_PCIE_NOC_1] [get_bd_addr_segs static_region/aved/cips/NOC_PMC_AXI_0/pspmc_0_psv_pmc_slave_boot_stream] -force assign_bd_address -offset 0x020400010000 -range 0x00010000 -target_address_space [get_bd_addr_spaces static_region/aved/cips/CPM_PCIE_NOC_1] [get_bd_addr_segs static_region/clk_rst_shell/clk_wizard_service/s_axi_lite/Reg] -force assign_bd_address -offset 0x020400000000 -range 0x00010000 -target_address_space [get_bd_addr_spaces static_region/aved/cips/CPM_PCIE_NOC_1] [get_bd_addr_segs static_region/clk_rst_shell/clk_wizard_slash/s_axi_lite/Reg] -force + assign_bd_address -offset 0x020400020000 -range 0x00010000 -target_address_space [get_bd_addr_spaces static_region/aved/cips/CPM_PCIE_NOC_1] [get_bd_addr_segs static_region/clk_rst_shell/build_id_gpio/S_AXI/Reg] -force assign_bd_address -offset 0x020302000000 -range 0x00040000 -target_address_space [get_bd_addr_spaces static_region/aved/cips/CPM_PCIE_NOC_1] [get_bd_addr_segs service_layer/qsfp_0_n_1/DCMAC_subsys/dcmac_0_core/s_axi/Reg] -force assign_bd_address -offset 0x020303000000 -range 0x00040000 -target_address_space [get_bd_addr_spaces static_region/aved/cips/CPM_PCIE_NOC_1] [get_bd_addr_segs service_layer/qsfp_2_n_3/DCMAC_subsys/dcmac_1_core/s_axi/Reg] -force assign_bd_address -offset 0x020200480000 -range 0x00010000 -target_address_space [get_bd_addr_spaces static_region/aved/cips/CPM_PCIE_NOC_1] [get_bd_addr_segs slash/ddr_bandwidth_64/s_axi_control/Reg] -force diff --git a/smi/src/CMakeLists.txt b/smi/src/CMakeLists.txt index ad0c721a..670ccd5c 100644 --- a/smi/src/CMakeLists.txt +++ b/smi/src/CMakeLists.txt @@ -21,6 +21,7 @@ add_executable( v80-smi + shell_build_id.cpp debug/bar_poke.cpp debug/clockwiz.cpp debug/mem_poke.cpp diff --git a/smi/src/inspect.cpp b/smi/src/inspect.cpp index 131a0107..5a0e2832 100644 --- a/smi/src/inspect.cpp +++ b/smi/src/inspect.cpp @@ -38,6 +38,7 @@ #include "bdf.hpp" +#include "shell_build_id.hpp" #include "utils.hpp" //. BDF string corresponding to the all-ones sentinel value (0xFFFF). @@ -289,6 +290,7 @@ struct VbinData { uint64_t clockFrequency{}; ///< Design clock frequency in Hz. std::map kernels; ///< Kernels keyed by name. std::optional utilization; ///< Utilization report (if present). + std::optional shellBuildId; ///< Shell build ID read from HW (device query only). /// Builds a VbinData from an already-parsed system-map XMLParser. static VbinData fromParser(vrt::XMLParser& parser, const std::string& name) { @@ -388,6 +390,11 @@ std::ostream& operator<<(std::ostream& out, const VbinData& vbin) { << INDENT1 << "Platform: " << toString(vbin.platform) << "\n" << INDENT1 << "Clock frequency: " << vbin.clockFrequency << "\n"; + if (vbin.shellBuildId) { + out << INDENT1 << "Shell build commit: " << vbin.shellBuildId->commitHex() + << (vbin.shellBuildId->dirty ? " (dirty)" : "") << "\n"; + } + if (vbin.utilization) { out << INDENT1 << "Utilization:\n" << *vbin.utilization; } @@ -405,6 +412,11 @@ Json::Value toJson(const VbinData& vbin) { j["clock_frequency"] = toHexString(vbin.clockFrequency); + if (vbin.shellBuildId) { + j["shell_build_commit"] = vbin.shellBuildId->commitHex(); + j["shell_build_dirty"] = vbin.shellBuildId->dirty; + } + if (vbin.utilization) { j["utilization"] = toJson(*vbin.utilization); } @@ -428,7 +440,16 @@ Json::Value toJson(const VbinData& vbin) { VbinData getVbinData(const Inspect::Options& options) { if (options.isBdfQuery) { std::string bdf = resolveBoardBdf(options.bdf, "query"); - return VbinData::fromBdf(bdf); + auto data = VbinData::fromBdf(bdf); + // Read the shell build ID from hardware. Best-effort: an older shell + // without the register (or a transient access error) shouldn't fail the + // whole query. + try { + data.shellBuildId = readBuildId(bdf); + } catch (const std::exception& e) { + std::cerr << "warning: could not read shell build ID: " << e.what() << "\n"; + } + return data; } else { return VbinData::fromPath(options.vbinPath); } diff --git a/smi/src/list.cpp b/smi/src/list.cpp index 153d1a64..3950e429 100644 --- a/smi/src/list.cpp +++ b/smi/src/list.cpp @@ -37,6 +37,7 @@ #include +#include "shell_build_id.hpp" #include "utils.hpp" /// Root sysfs directory that contains one symlink per PCI device. @@ -402,6 +403,9 @@ struct V80Board { /// Sensor readings (populated only when -s/--sensors is given and VRTD is reachable). std::vector sensors; + /// Shell build ID read from hardware (populated only when longPrinting). + std::optional shellBuildId; + /// True when all three PFs and VRTD are ready. bool ok() const { return pf0.ok && pf1.ok && pf2.ok && vrtd.ok; } }; @@ -445,6 +449,15 @@ static std::vector discoverBoards(bool longPrinting, bool sensors) { board.pf0Device = tryReadDevice(pf0Dev.bdf, true); board.pf1Device = tryReadDevice(pf1Bdf, true); board.pf2Device = tryReadDevice(pf2Bdf, true); + + // Best-effort: read the shell build ID from hardware. Requires a + // usable BAR4 (via vrtd); swallow errors so list still works on + // boards without the register or with vrtd down. + try { + board.shellBuildId = readBuildId(base); + } catch (...) { + // Leave unset. + } } if (sensors && board.vrtd.ok) { @@ -624,6 +637,15 @@ std::ostream& operator<<(std::ostream& out, const V80Board& board) { out << "NOT READY: " << board.vrtd.reason; } out << "\n"; + + out << INDENT1 << "Shell build: "; + if (board.shellBuildId) { + out << board.shellBuildId->commitHex() + << (board.shellBuildId->dirty ? " (dirty)" : ""); + } else { + out << "unavailable"; + } + out << "\n"; } if (!board.sensors.empty()) { @@ -687,6 +709,11 @@ Json::Value toJson(const V80Board& board) { } j["vrtd"] = vrtdJson; + if (board.shellBuildId) { + j["shell_build_commit"] = board.shellBuildId->commitHex(); + j["shell_build_dirty"] = board.shellBuildId->dirty; + } + if (!board.sensors.empty()) { Json::Value sensorsJson(Json::arrayValue); for (const auto& s : board.sensors) { diff --git a/smi/src/shell_build_id.cpp b/smi/src/shell_build_id.cpp new file mode 100644 index 00000000..23dac945 --- /dev/null +++ b/smi/src/shell_build_id.cpp @@ -0,0 +1,64 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/// @file shell_build_id.cpp +/// @brief Implementation of the shell build-ID hardware read. + +#include "shell_build_id.hpp" + +#include +#include + +#include + +std::string BuildId::commitHex() const { + // 63-bit prefix: 31 high hash bits followed by the 32 low bits. + char buf[32]; + std::snprintf(buf, sizeof(buf), "0x%08x%08x", hiHash, lo); + return std::string(buf); +} + +BuildId readBuildId(const std::string& bdf) { + vrtd::Session session; + auto device = session.getDeviceByBdf(bdf); + auto bar = device.getBar(BUILD_ID_BAR); + + if (!bar.isUsable()) { + throw std::runtime_error("BAR4 is not usable; cannot read shell build ID"); + } + + vrtd::BarFile barFile = bar.openBarFile(); + if (barFile.getLen() < BUILD_ID_REG_HI + sizeof(uint32_t)) { + throw std::runtime_error("BAR4 too small for build-ID register"); + } + + const uint32_t lo = + *barFile.getPtr(vrtd::BarFile::Direction::Read, + static_cast(BUILD_ID_REG_LO)); + const uint32_t hi = + *barFile.getPtr(vrtd::BarFile::Direction::Read, + static_cast(BUILD_ID_REG_HI)); + + BuildId id; + id.lo = lo; + id.hiHash = hi & 0x7FFFFFFF; + id.dirty = (hi & 0x80000000u) != 0; + return id; +} diff --git a/smi/src/shell_build_id.hpp b/smi/src/shell_build_id.hpp new file mode 100644 index 00000000..d600025d --- /dev/null +++ b/smi/src/shell_build_id.hpp @@ -0,0 +1,59 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/// @file shell_build_id.hpp +/// @brief Reads the shell build-ID register (git commit + dirty flag) from +/// hardware over PCIe. + +#ifndef SMI_SHELL_BUILD_ID_HPP +#define SMI_SHELL_BUILD_ID_HPP + +#include +#include + +/// BAR index carrying the static-region build-ID GPIO (shared with the clock +/// wizards). Must match the address assigned in the shell block design. +constexpr uint8_t BUILD_ID_BAR = 4; + +/// Offset of the build-ID AXI GPIO within BAR4. Its BD address is +/// 0x0204_0002_0000, and BAR4 maps the 0x0204_0000_0000 aperture. +constexpr uint64_t BUILD_ID_OFFSET = 0x20000; + +/// AXI GPIO channel-1 data register (low 32 bits of the SHA-1). +constexpr uint64_t BUILD_ID_REG_LO = BUILD_ID_OFFSET + 0x0; + +/// AXI GPIO channel-2 data register (bits[30:0] hash, bit[31] dirty). +constexpr uint64_t BUILD_ID_REG_HI = BUILD_ID_OFFSET + 0x8; + +/// @brief Shell build-ID decoded from the hardware register. +struct BuildId { + uint32_t lo{}; ///< Low 32 bits of the git SHA-1. + uint32_t hiHash{}; ///< Next 31 bits of the git SHA-1 (bits[30:0]). + bool dirty{}; ///< True if the shell was built from a dirty tree. + + /// Returns the 63-bit commit prefix as a "0x"-prefixed hex string. + std::string commitHex() const; +}; + +/// @brief Reads the build-ID register from the device at @p bdf over PCIe. +/// @throws std::exception on any vrtd/BAR access failure. +BuildId readBuildId(const std::string& bdf); + +#endif // SMI_SHELL_BUILD_ID_HPP From 5f0b0ceefdaf7be5521abfa3fad8f2f9609adc48 Mon Sep 17 00:00:00 2001 From: Lucian Petrica Date: Fri, 3 Jul 2026 16:33:24 +0100 Subject: [PATCH 2/2] pep8 --- linker/slashkit/emit/hw/project_gen.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linker/slashkit/emit/hw/project_gen.py b/linker/slashkit/emit/hw/project_gen.py index cd6f05a8..6209c699 100644 --- a/linker/slashkit/emit/hw/project_gen.py +++ b/linker/slashkit/emit/hw/project_gen.py @@ -258,7 +258,8 @@ def _git(*args: str) -> Optional[str]: if dirty: hi |= 0x80000000 - logger.info("Shell build-ID: commit %s%s", sha[:14], " (dirty)" if dirty else "") + logger.info("Shell build-ID: commit %s%s", + sha[:14], " (dirty)" if dirty else "") return {"SLASH_BUILD_ID_LO": f"0x{lo:08x}", "SLASH_BUILD_ID_HI": f"0x{hi:08x}"}