You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
301 lines
8.8 KiB
301 lines
8.8 KiB
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import sys
|
|
import unittest
|
|
import elftools.elf.elffile
|
|
import elftools.elf.enums
|
|
import elftools.elf.sections
|
|
|
|
QEMU_EXT_OPTS = {
|
|
"zba": "zba=true",
|
|
"zbb": "zbb=true",
|
|
"zbc": "zbc=true",
|
|
"zbs": "zbs=true",
|
|
"v": "v=true,vext_spec=v1.0",
|
|
"zve32f": "Zve32f=true",
|
|
"zve64f": "Zve64f=true",
|
|
"zfh": "Zfh=true",
|
|
"zfhmin": "Zfhmin=true",
|
|
"zhinx": "zhinx=true",
|
|
"zfinx": "zfinx=true",
|
|
"zdinx": "zdinx=true",
|
|
}
|
|
|
|
SPIKE_EXT_NOT_ALLOWED = [
|
|
"zve32x",
|
|
"zve32f",
|
|
"zve64x",
|
|
"zve64f",
|
|
"zve64d",
|
|
"zvl32b",
|
|
"zvl64b",
|
|
"zvl128b",
|
|
"zvl256b",
|
|
"zvl512b",
|
|
"zvl1024b",
|
|
"zvl2048b",
|
|
"zvl4096b",
|
|
]
|
|
|
|
CPU_OPTIONS = {
|
|
"xlen": "",
|
|
"vlen": "",
|
|
"elen": "",
|
|
"extensions": [],
|
|
}
|
|
|
|
SUPPORTTED_EXTS = "iemafdcbvph"
|
|
MC_EXT_PREFIX = "zsx"
|
|
|
|
def parse_opt(argv):
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('-march', '--with-arch', type=str, dest='march')
|
|
parser.add_argument('-selftest', action='store_true')
|
|
parser.add_argument('--elf-file-path', type=str)
|
|
parser.add_argument('--print-xlen', action='store_true', default=False)
|
|
parser.add_argument('--print-vlen', action='store_true', default=False)
|
|
parser.add_argument('--print-qemu-cpu', action='store_true', default=False)
|
|
parser.add_argument('--print-spike-isa', action='store_true', default=False)
|
|
parser.add_argument('--print-spike-varch', action='store_true',
|
|
default=False)
|
|
opt = parser.parse_args()
|
|
return opt
|
|
|
|
def parse_mc_ext(ext_str, idx):
|
|
end_idx = ext_str[idx+1:].find('_')
|
|
if end_idx == -1:
|
|
end_idx = len(ext_str)
|
|
else:
|
|
end_idx = end_idx + idx + 1
|
|
major = 0
|
|
minor = 0
|
|
version_begin_idx = end_idx
|
|
if ext_str[end_idx-1].isdigit():
|
|
# This ext is come with version.
|
|
v_idx = end_idx - 1
|
|
while (ext_str[v_idx].isdigit()) and v_idx > idx:
|
|
v_idx -= 1
|
|
major = int(ext_str[v_idx+1:end_idx])
|
|
version_begin_idx = v_idx+1
|
|
if (ext_str[v_idx] == 'p'):
|
|
minor = major
|
|
major_v_idx = v_idx - 1
|
|
while (ext_str[major_v_idx].isdigit()) and major_v_idx > idx:
|
|
major_v_idx -= 1
|
|
major = int(ext_str[major_v_idx+1:v_idx])
|
|
version_begin_idx = major_v_idx+1
|
|
|
|
return end_idx, ext_str[idx:version_begin_idx], major, minor
|
|
|
|
def parse_version(ext_str, idx):
|
|
major = 2
|
|
minor = 0
|
|
strlen = len(ext_str)
|
|
end_idx = idx + 1
|
|
if idx+1 < strlen and ext_str[idx+1].isdigit():
|
|
v_idx = idx + 1
|
|
while v_idx < strlen and (ext_str[v_idx].isdigit()):
|
|
v_idx += 1
|
|
major = int(ext_str[idx+1:v_idx])
|
|
end_idx = v_idx
|
|
if (ext_str[v_idx] == 'p'):
|
|
minor_v_idx = v_idx + 1
|
|
while minor_v_idx < strlen and (ext_str[minor_v_idx].isdigit()):
|
|
minor_v_idx += 1
|
|
minor = int(ext_str[v_idx+1:minor_v_idx])
|
|
end_idx = minor_v_idx
|
|
|
|
return end_idx, ext_str[idx], major, minor
|
|
|
|
def parse_march(march):
|
|
if len(march) < 5:
|
|
return None
|
|
march = march.replace("rv64g", "rv64imafd").replace("rv32g", "rv32imafd")
|
|
if march[0:5] not in ['rv64i', 'rv32i', 'rv32e']:
|
|
print (march[0:5])
|
|
return None
|
|
|
|
ext_str = march[4:]
|
|
idx = 0
|
|
extstrlens = len(ext_str)
|
|
exts = dict()
|
|
while idx < extstrlens:
|
|
if ext_str[idx] in SUPPORTTED_EXTS:
|
|
idx, ext_name, major, minor = parse_version(ext_str, idx)
|
|
elif ext_str[idx] in MC_EXT_PREFIX:
|
|
idx, ext_name, major, minor = parse_mc_ext(ext_str, idx)
|
|
elif ext_str[idx] == '_':
|
|
idx = idx + 1
|
|
continue
|
|
else:
|
|
raise Exception("Unrecognized ext : `%s`, %s" %
|
|
(ext_str[idx], ext_str))
|
|
exts[ext_name] = (major, minor)
|
|
return exts
|
|
|
|
def get_vlen(ext_dict):
|
|
vlen = 0
|
|
for ext in ext_dict.keys():
|
|
if ext == 'v':
|
|
vlen = max(vlen, 128)
|
|
elif (ext.startswith('zvl') and ext[-1] == 'b'):
|
|
zvlen = int(ext[3:-1])
|
|
vlen = max(vlen, zvlen)
|
|
elif ext.startswith("zve"):
|
|
zvelen = int(ext[3:-1])
|
|
vlen = max(vlen, zvelen)
|
|
return vlen
|
|
|
|
def get_elen(ext_dict, xlen):
|
|
elen = xlen
|
|
|
|
if "zve32x" in ext_dict or "zve32f" in ext_dict:
|
|
elen = 32
|
|
if "zve64x" in ext_dict or "zve64f" in ext_dict or "zve64d" in ext_dict:
|
|
elen = 64
|
|
|
|
return elen
|
|
|
|
def print_qemu_cpu():
|
|
cpu_options = []
|
|
cpu_options.append("rv{0}".format(CPU_OPTIONS['xlen']))
|
|
|
|
if CPU_OPTIONS['vlen']:
|
|
cpu_options.append("vlen={0}".format(CPU_OPTIONS['vlen']))
|
|
|
|
disable_all_fd = False
|
|
for ext in CPU_OPTIONS['extensions']:
|
|
if ext in QEMU_EXT_OPTS:
|
|
cpu_options.append(QEMU_EXT_OPTS[ext])
|
|
|
|
if ext in ['zhinx', 'zfinx', 'zdinx']:
|
|
disable_all_fd = True
|
|
|
|
if disable_all_fd:
|
|
cpu_options.append("f=false")
|
|
cpu_options.append("d=false")
|
|
|
|
return ",".join(cpu_options)
|
|
|
|
def print_spike_isa():
|
|
cpu_options = []
|
|
cpu_options.append("rv{0}".format(CPU_OPTIONS['xlen']))
|
|
|
|
for ext in CPU_OPTIONS['extensions']:
|
|
if ext not in SPIKE_EXT_NOT_ALLOWED:
|
|
normalized_ext_name = ext
|
|
|
|
if len(ext) > 1:
|
|
normalized_ext_name = "_{0}".format(normalized_ext_name)
|
|
|
|
cpu_options.append(normalized_ext_name)
|
|
|
|
return "".join(cpu_options)
|
|
|
|
def print_spike_varch():
|
|
if not CPU_OPTIONS['vlen']:
|
|
return ""
|
|
|
|
return "vlen:{0},elen:{1}".format(CPU_OPTIONS['vlen'], CPU_OPTIONS['elen'])
|
|
|
|
class TestArchStringParse(unittest.TestCase):
|
|
def _test(self, arch, expected_arch_list, expected_vlen=0):
|
|
exts = parse_march(arch)
|
|
vlen = get_vlen(exts)
|
|
self.assertEqual(expected_vlen, vlen)
|
|
self.assertEqual(set(expected_arch_list), set(exts.keys()))
|
|
|
|
def test_rv64gc(self):
|
|
self._test("rv64gc", ['i', 'm', 'a', 'f', 'd', 'c'])
|
|
self._test("rv32imc_zve32x", ['i', 'm', 'c', 'zve32x'], expected_vlen=32)
|
|
self._test("rv32imc_zve32x_zvl128b", ['i', 'm', 'c', 'zve32x', 'zvl128b'], expected_vlen=128)
|
|
|
|
|
|
def selftest():
|
|
unittest.main(argv=sys.argv[1:])
|
|
|
|
def open_elf(path):
|
|
try:
|
|
elffile = elftools.elf.elffile.ELFFile(open(path, 'rb'))
|
|
except elftools.common.exceptions.ELFError:
|
|
raise Exception("%s is not ELF file!" % path)
|
|
return elffile
|
|
|
|
def get_xlen(path):
|
|
elffile = open_elf(path)
|
|
return elffile.elfclass
|
|
|
|
def read_arch_attr (path):
|
|
elffile = open_elf(path)
|
|
|
|
attr_sec = elffile.get_section_by_name(".riscv.attributes")
|
|
if attr_sec:
|
|
# pyelftools has support RISC-V attribute but not contain in any
|
|
# release yet, so use ARMAttributesSection for now...
|
|
xattr_section = \
|
|
elftools.elf.sections.ARMAttributesSection (
|
|
attr_sec.header,
|
|
attr_sec.name,
|
|
elffile)
|
|
for subsec in xattr_section.subsections:
|
|
for subsubsec in subsec.subsubsections:
|
|
for attr in subsubsec.iter_attributes():
|
|
val = attr.value
|
|
if (not isinstance(val, str)):
|
|
continue
|
|
pos32 = val.find("rv32")
|
|
pos64 = val.find("rv64")
|
|
# MAGIC WORKAROUND
|
|
# Some version of pyelftools has issue for parsing
|
|
# Tag number = 5, it will wrongly parse it become
|
|
# Tag number = 4 + 0x10 + 0x5
|
|
if (pos32 == 2) or (pos64 == 2):
|
|
val = val[2:]
|
|
# End of MAGIC WORKAROUND
|
|
|
|
if (pos32 != -1 or pos64 != -1):
|
|
return val
|
|
raise Exception("Not found ELF attribute in %s?" % path)
|
|
|
|
def parse_elf_file(elf_file_path):
|
|
extensions = []
|
|
extension_dict = parse_march(read_arch_attr(elf_file_path))
|
|
|
|
for extension in extension_dict.keys():
|
|
extensions.append(extension)
|
|
|
|
xlen = get_xlen(elf_file_path)
|
|
|
|
CPU_OPTIONS["extensions"] = extensions
|
|
CPU_OPTIONS["vlen"] = get_vlen(extension_dict)
|
|
CPU_OPTIONS["elen"] = get_elen(extension_dict, xlen)
|
|
CPU_OPTIONS["xlen"] = xlen
|
|
|
|
def main(argv):
|
|
opt = parse_opt(argv)
|
|
if opt.selftest:
|
|
selftest()
|
|
return 0
|
|
|
|
parse_elf_file(opt.elf_file_path)
|
|
|
|
if opt.print_xlen:
|
|
print(CPU_OPTIONS['xlen'])
|
|
return
|
|
|
|
if opt.print_vlen:
|
|
print(CPU_OPTIONS['vlen'])
|
|
return
|
|
|
|
if opt.print_qemu_cpu:
|
|
print(print_qemu_cpu())
|
|
|
|
if opt.print_spike_isa:
|
|
print(print_spike_isa())
|
|
|
|
if opt.print_spike_varch:
|
|
print(print_spike_varch())
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main(sys.argv))
|
|
|