#!/usr/bin/env python3 ################################################################################ # Copyright © 2025 Advanced Micro Devices, Inc., or its affiliates. # # SPDX-License-Identifier: MIT ################################################################################ import os import re import subprocess import sys import time # get current working directory CWD = os.path.dirname(os.path.realpath(__file__)) ISA_TO_ID = { # Kaveri - Temporary "gfx700" : [0x1304, 0x1305, 0x1306, 0x1307, 0x1309, 0x130a, 0x130b, 0x130c, 0x130d, 0x130e, 0x130f, 0x1310, 0x1311, 0x1312, 0x1313, 0x1315, 0x1316, 0x1317, 0x1318, 0x131b, 0x131c, 0x131d], # Hawaii "gfx701" : [0x67a0, 0x67a1, 0x67a2, 0x67a8, 0x67a9, 0x67aa, 0x67b0, 0x67b1, 0x67b8, 0x67b9, 0x67ba, 0x67be], # Carrizo "gfx801" : [0x9870, 0x9874, 0x9875, 0x9876, 0x9877, 0x98e4], # Tonga "gfx802" : [0x6920, 0x6921, 0x6928, 0x6929, 0x692b, 0x692f, 0x6930, 0x6938, 0x6939], # Fiji "gfx803" : [0x7300, 0x730f, # Polaris10 0x67c0, 0x67c1, 0x67c2, 0x67c4, 0x67c7, 0x67c8, 0x67c9, 0x67ca, 0x67cc, 0x67cf, 0x6fdf, # Polaris11 0x67d0, 0x67df, 0x67e0, 0x67e1, 0x67e3, 0x67e7, 0x67e8, 0x67e9, 0x67eb, 0x67ef, 0x67ff, # Polaris12 0x6980, 0x6981, 0x6985, 0x6986, 0x6987, 0x6995, 0x6997, 0x699f, # VegaM 0x694c, 0x694e, 0x694f], # Vega10 "gfx900" : [0x6860, 0x6861, 0x6862, 0x6863, 0x6864, 0x6867, 0x6868, 0x6869, 0x6869, 0x686a, 0x686b, 0x686c, 0x686d, 0x686e, 0x686f, 0x687f], # Raven "gfx902" : [0x15dd, 0x15d8], # Vega12 "gfx904" : [0x69a0, 0x69a1, 0x69a2, 0x69a3, 0x69af], # Vega20 "gfx906" : [0x66a0, 0x66a1, 0x66a2, 0x66a3, 0x66a4, 0x66a7, 0x66af], # Arcturus "gfx908" : [0x738c, 0x7388, 0x738e, 0x7390], # Aldebaran "gfx90a" : [0x7408, 0x740c, 0x740f, 0x7410], # Renoir "gfx90c" : [0x15e7, 0x1636, 0x1638, 0x164c], # Instinct MI300 series "gfx942" : [0x74a1, 0x74a2, 0x74a5, 0x74a9, 0x74b5, 0x74b6, 0x74bd], # Navi10 "gfx1010" : [0x7310, 0x7312, 0x7318, 0x7319, 0x731a, 0x731b, 0x731e, 0x731f], # Navi12 "gfx1011" : [0x7360, 0x7362], # Navi14 "gfx1012" : [0x7340, 0x7341, 0x7347, 0x734f], # Cyan_Skillfish "gfx1013" : [0x13f9, 0x13fa, 0x13fb, 0x13fc, 0x13f3], # Sienna_Cichlid "gfx1030" : [0x73a0, 0x73a1, 0x73a2, 0x73a3, 0x73a5, 0x73a8, 0x73a9, 0x73ab, 0x73ac, 0x73ad, 0x73ae, 0x73af, 0x73bf], # Navy_Flounder "gfx1031" : [0x73c0, 0x73c1, 0x73c3, 0x73da, 0x73db, 0x73dc, 0x73dd, 0x73de, 0x73df], # Dimgray_Cavefish "gfx1032" : [0x73e0, 0x73e1, 0x73e2, 0x73e3, 0x73e8, 0x73e9, 0x73ea, 0x73eb, 0x73ec, 0x73ed, 0x73ef, 0x73ff], # Van Gogh "gfx1033" : [0x163f], # Beige_Goby "gfx1034" : [0x7420, 0x7421, 0x7422, 0x7423, 0x743f], # Yellow_Carp "gfx1035" : [0x164d, 0x1681], # Navi31 "gfx1100": [0x7448, 0x7449, 0x744a, 0x744c, 0x745e], # Navi32 "gfx1101": [0x7470, 0x747e], # Navi33 "gfx1102": [0x7480, 0x7483, 0x7489, 0x7499], } def staticVars(**kwargs): def deco(func): for k in kwargs: setattr(func, k, kwargs[k]) return func return deco @staticVars(search_term=re.compile(r"gfx[0-9a-fA-F]+(-[0-9a-fA-F]+)?(-generic)?")) def getGCNISA(line, match_from_beginning = False): if match_from_beginning is True: result = getGCNISA.search_term.match(line) else: result = getGCNISA.search_term.search(line) if result is not None: return result.group(0) return None @staticVars(search_name=re.compile(r"gfx[0-9a-fA-F]+(-[0-9a-fA-F]+)?(-generic)?(:[-+:\w]+)?")) def getGCNArchName(line): result = getGCNArchName.search_name.search(line) if result is not None: return result.group(0) return None def readFromTargetLstFile(): target_list = [] # locate target.lst using environment variable or # it should be placed at the same directory with this script target_lst_path = os.environ.get("ROCM_TARGET_LST"); if target_lst_path is None: target_lst_path = os.path.join(CWD, "target.lst") if os.path.isfile(target_lst_path): target_lst_file = open(target_lst_path, 'r') for line in target_lst_file: # for target.lst match from beginning so targets can be disabled by # commenting it out target = getGCNISA(line, match_from_beginning = True) if target is not None: target_list.append(target) return target_list def readFromROCMINFO(search_arch_name = False): target_list = [] # locate rocminfo binary which should be placed at the same directory with # this script rocminfo_executable = os.path.join(CWD, "rocminfo") try: t0 = time.time() while 1: t1 = time.time() # quit after retrying rocminfo for a minute. if t1 - t0 > 60.0: print("Timeout querying rocminfo. Are you compiling with more than 254 threads?", file=sys.stderr) break # run rocminfo rocminfo_output = subprocess.Popen(rocminfo_executable, stdout=subprocess.PIPE).communicate()[0].decode("utf-8").split('\n') term1 = re.compile(r"Cannot allocate memory") term2 = re.compile(r"HSA_STATUS_ERROR_OUT_OF_RESOURCES") done = 1 for line in rocminfo_output: if term1.search(line) is not None or term2.search(line) is not None: done = 0 break if done: break except: rocminfo_output = [] # search AMDGCN gfx ISA if search_arch_name is True: line_search_term = re.compile(r"\A\s+Name:\s+(amdgcn-amd-amdhsa--gfx\d+)") else: line_search_term = re.compile(r"\A\s+Name:\s+(gfx\d+)") for line in rocminfo_output: if line_search_term.match(line) is not None: if search_arch_name is True: target = getGCNArchName(line) else: target = getGCNISA(line) if target is not None: target_list.append(target) return target_list def readFromLSPCI(): target_list = [] try: # run lspci lspci_output = subprocess.Popen(["/usr/bin/lspci", "-n", "-d", "1002:"], stdout=subprocess.PIPE).communicate()[0].decode("utf-8").split('\n') except: lspci_output = [] target_search_term = re.compile(r"1002:\w+") for line in lspci_output: search_result = target_search_term.search(line) if search_result is not None: device_id = int(search_result.group(0).split(':')[1], 16) # try lookup from ISA_TO_ID dict for target in ISA_TO_ID.keys(): for target_device_id in ISA_TO_ID[target]: if device_id == target_device_id: target_list.append(target) break return target_list def readFromKFD(): target_list = [] topology_dir = '/sys/class/kfd/kfd/topology/nodes/' if os.path.isdir(topology_dir): for node in sorted(os.listdir(topology_dir)): node_path = os.path.join(topology_dir, node) if os.path.isdir(node_path): prop_path = node_path + '/properties' if os.path.isfile(prop_path) and os.access(prop_path, os.R_OK): target_search_term = re.compile(r"gfx_target_version.+") with open(prop_path) as f: try: line = f.readline() except PermissionError: # We may have a subsystem (e.g. scheduler) limiting device visibility which # could cause a permission error. line = '' while line != '' : search_result = target_search_term.search(line) if search_result is not None: device_id = int(search_result.group(0).split(' ')[1], 10) if device_id != 0: gfx_override = os.environ.get("HSA_OVERRIDE_GFX_VERSION") if gfx_override is not None: try: override_tokens = gfx_override.split('.') major_ver=int(override_tokens[0]) minor_ver=int(override_tokens[1]) stepping_ver=int(override_tokens[2]) if major_ver > 63 or minor_ver > 255 or stepping_ver > 255: print('Invalid HSA_OVERRIDE_GFX_VERSION value') major_ver = 0 minor_ver = 0 stepping_ver = 0 except Exception as e: print('Invalid HSA_OVERRIDE_GFX_VERSION format expected \"1.2.3\"') major_ver = 0 minor_ver = 0 stepping_ver = 0 else: major_ver = int((device_id / 10000) % 100) minor_ver = int((device_id / 100) % 100) stepping_ver = int(device_id % 100) target_list.append("gfx" + format(major_ver, 'd') + format(minor_ver, 'x') + format(stepping_ver, 'x')) line = f.readline() return target_list def main(): if len(sys.argv) == 2 and sys.argv[1] == '-name' : """ Prints the list of available AMD GCN target names extracted from rocminfo, a tool shipped with this script to enumerate GPU agents available on a working ROCm stack.""" target_list = readFromROCMINFO(True) else: """Prints the list of available AMD GCN ISA The program collects the list in 3 different ways, in the order of precendence: 1. ROCM_TARGET_LST : a user defined environment variable, set to the path and filename where to find the "target.lst" file. This can be used in an install environment with sandbox, where execution of "rocminfo" is not possible. 2. target.lst : user-supplied text file. This is used in a container setting where ROCm stack may usually not available. 3. HSA topology : gathers the information from the HSA node topology in /sys/class/kfd/kfd/topology/nodes/ 4. lspci : enumerate PCI bus and locate supported devices from a hard-coded lookup table. 5. rocminfo : a tool shipped with this script to enumerate GPU agents available on a working ROCm stack. """ target_list = readFromTargetLstFile() if len(target_list) == 0: target_list = readFromKFD() if len(target_list) == 0: target_list = readFromLSPCI() if len(target_list) == 0: target_list = readFromROCMINFO() for gfx in target_list: print(gfx) if __name__ == "__main__": main()