Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion linker/slashkit/emit/hw/project_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,51 @@ 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
Expand Down Expand Up @@ -246,8 +291,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):
Expand Down
6 changes: 6 additions & 0 deletions linker/slashkit/resources/base/scripts/create_project.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
51 changes: 49 additions & 2 deletions linker/slashkit/resources/base/scripts/top.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 ""
Expand Down Expand Up @@ -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 \
Expand Down Expand Up @@ -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] \
Expand All @@ -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]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions smi/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
add_executable(
v80-smi

shell_build_id.cpp
debug/bar_poke.cpp
debug/clockwiz.cpp
debug/mem_poke.cpp
Expand Down
23 changes: 22 additions & 1 deletion smi/src/inspect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -289,6 +290,7 @@ struct VbinData {
uint64_t clockFrequency{}; ///< Design clock frequency in Hz.
std::map<std::string, KernelData> kernels; ///< Kernels keyed by name.
std::optional<vrt::UtilizationReport> utilization; ///< Utilization report (if present).
std::optional<BuildId> 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) {
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down
27 changes: 27 additions & 0 deletions smi/src/list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

#include <vrtd/session.hpp>

#include "shell_build_id.hpp"
#include "utils.hpp"

/// Root sysfs directory that contains one symlink per PCI device.
Expand Down Expand Up @@ -402,6 +403,9 @@ struct V80Board {
/// Sensor readings (populated only when -s/--sensors is given and VRTD is reachable).
std::vector<vrtd::SensorEntry> sensors;

/// Shell build ID read from hardware (populated only when longPrinting).
std::optional<BuildId> shellBuildId;

/// True when all three PFs and VRTD are ready.
bool ok() const { return pf0.ok && pf1.ok && pf2.ok && vrtd.ok; }
};
Expand Down Expand Up @@ -445,6 +449,15 @@ static std::vector<V80Board> 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) {
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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) {
Expand Down
64 changes: 64 additions & 0 deletions smi/src/shell_build_id.cpp
Original file line number Diff line number Diff line change
@@ -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 <cstdio>
#include <stdexcept>

#include <vrtd/session.hpp>

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<uint32_t>(vrtd::BarFile::Direction::Read,
static_cast<size_t>(BUILD_ID_REG_LO));
const uint32_t hi =
*barFile.getPtr<uint32_t>(vrtd::BarFile::Direction::Read,
static_cast<size_t>(BUILD_ID_REG_HI));

BuildId id;
id.lo = lo;
id.hiHash = hi & 0x7FFFFFFF;
id.dirty = (hi & 0x80000000u) != 0;
return id;
}
Loading
Loading