From 10706c544e9bca0cf2dc3867c9d3dbb77c53fa3b Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 14 Feb 2020 14:54:07 -0800 Subject: Add tests for vector register access (#244) * WIP * Add vector register smoketest. Also redo the gdb value parsing code to accommodate the more complicated way that vector registers look. * Test vector access a little more thoroughly. * Revert unnecessary changes. --- debug/gdbserver.py | 21 +++++ debug/targets/RISC-V/spike32-2-hwthread.py | 6 +- debug/targets/RISC-V/spike32.py | 8 +- debug/targets/RISC-V/spike64-2.py | 9 +- debug/testlib.py | 127 +++++++++++++++++++++++------ 5 files changed, 137 insertions(+), 34 deletions(-) diff --git a/debug/gdbserver.py b/debug/gdbserver.py index 04459b8..db339a9 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -117,6 +117,27 @@ class SimpleT1Test(SimpleRegisterTest): def test(self): self.check_reg("t1", "x6") +class SimpleV13Test(SimpleRegisterTest): + def test(self): + if self.hart.extensionSupported('V'): + vlenb = self.gdb.p("$vlenb") + # Can't write quadwords, because gdb won't parse a 128-bit hex + # value. + written = {} + for name, byte_count in (('b', 1), ('s', 2), ('w', 4), ('l', 8)): + written[name] = {} + for i in range(vlenb // byte_count): + written[name][i] = random.randrange(256 ** byte_count) + self.gdb.p("$v13.%s[%d]=0x%x" % (name, i, written[name][i])) + self.gdb.stepi() + self.gdb.p("$v13") + for i in range(vlenb // byte_count): + assertEqual(self.gdb.p("$v13.%s[%d]" % (name, i)), + written[name][i]) + else: + output = self.gdb.p_raw("$v13") + assertRegex(output, r"void|Could not fetch register.*") + class SimpleF18Test(SimpleRegisterTest): def check_reg(self, name, alias): if self.hart.extensionSupported('F'): diff --git a/debug/targets/RISC-V/spike32-2-hwthread.py b/debug/targets/RISC-V/spike32-2-hwthread.py index 93308fb..2ad2998 100644 --- a/debug/targets/RISC-V/spike32-2-hwthread.py +++ b/debug/targets/RISC-V/spike32-2-hwthread.py @@ -4,12 +4,12 @@ import testlib import spike32 # pylint: disable=import-error class spike32_2(targets.Target): - harts = [spike32.spike32_hart(misa=0x40141129), - spike32.spike32_hart(misa=0x40141129)] + harts = [spike32.spike32_hart(misa=0x40341101), + spike32.spike32_hart(misa=0x40341101)] openocd_config_path = "spike-2-hwthread.cfg" timeout_sec = 5 implements_custom_test = True def create(self): - return testlib.Spike(self, support_hasel=True, + return testlib.Spike(self, isa="RV32IMAV", support_hasel=True, support_haltgroups=False) diff --git a/debug/targets/RISC-V/spike32.py b/debug/targets/RISC-V/spike32.py index 614c180..b261f6c 100644 --- a/debug/targets/RISC-V/spike32.py +++ b/debug/targets/RISC-V/spike32.py @@ -13,12 +13,14 @@ class spike32_hart(targets.Hart): self.misa = misa class spike32(targets.Target): - harts = [spike32_hart(misa=0x4014112d)] + harts = [spike32_hart(misa=0x4034112d)] openocd_config_path = "spike-1.cfg" timeout_sec = 30 implements_custom_test = True def create(self): # 64-bit FPRs on 32-bit target - return testlib.Spike(self, isa="RV32IMAFDC", dmi_rti=4, - support_abstract_csr=True, support_haltgroups=False) + return testlib.Spike(self, isa="RV32IMAFDCV", dmi_rti=4, + support_abstract_csr=True, support_haltgroups=False, + # elen must be at least 64 because D is supported. + elen=64) diff --git a/debug/targets/RISC-V/spike64-2.py b/debug/targets/RISC-V/spike64-2.py index 6b9b5c9..5ace23b 100644 --- a/debug/targets/RISC-V/spike64-2.py +++ b/debug/targets/RISC-V/spike64-2.py @@ -4,8 +4,8 @@ import testlib import spike64 # pylint: disable=import-error class spike64_2(targets.Target): - harts = [spike64.spike64_hart(misa=0x8000000000141129), - spike64.spike64_hart(misa=0x8000000000141129)] + harts = [spike64.spike64_hart(misa=0x8000000000341129), + spike64.spike64_hart(misa=0x8000000000341129)] openocd_config_path = "spike-2.cfg" # Increased timeout because we use abstract_rti to artificially slow things # down. @@ -14,5 +14,6 @@ class spike64_2(targets.Target): support_hasel = False def create(self): - return testlib.Spike(self, isa="RV64IMAFD", abstract_rti=30, - support_hasel=False, support_abstract_csr=False) + return testlib.Spike(self, isa="RV64IMAFDV", abstract_rti=30, + support_hasel=False, support_abstract_csr=False, + vlen=512, elen=64) diff --git a/debug/testlib.py b/debug/testlib.py index 1a1a8a9..6f5c9d5 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -54,7 +54,7 @@ class Spike: def __init__(self, target, halted=False, timeout=None, with_jtag_gdb=True, isa=None, progbufsize=None, dmi_rti=None, abstract_rti=None, support_hasel=True, support_abstract_csr=True, - support_haltgroups=True): + support_haltgroups=True, vlen=128, elen=64, slen=128): """Launch spike. Return tuple of its process and the port it's running on.""" self.process = None @@ -65,6 +65,9 @@ class Spike: self.support_abstract_csr = support_abstract_csr self.support_hasel = support_hasel self.support_haltgroups = support_haltgroups + self.vlen = vlen + self.elen = elen + self.slen = slen if target.harts: harts = target.harts @@ -141,6 +144,10 @@ class Spike: if not self.support_haltgroups: cmd.append("--dm-no-halt-groups") + if 'V' in isa[2:]: + cmd.append("--varch=v%d:e%d:s%d" % (self.vlen, self.elen, + self.slen)) + assert len(set(t.ram for t in harts)) == 1, \ "All spike harts must have the same RAM layout" assert len(set(t.ram_size for t in harts)) == 1, \ @@ -381,29 +388,101 @@ class CouldNotFetch(Exception): Thread = collections.namedtuple('Thread', ('id', 'description', 'target_id', 'name', 'frame')) -def parse_rhs(text): - text = text.strip() - if text.startswith("{") and text.endswith("}"): - inner = text[1:-1] - parsed = [parse_rhs(t) for t in inner.split(", ")] - if all([isinstance(p, dict) for p in parsed]): - dictionary = {} - for p in parsed: - for k, v in p.items(): - dictionary[k] = v - parsed = dictionary - return parsed - elif text.startswith('"') and text.endswith('"'): - return text[1:-1] - elif ' = ' in text: - lhs, rhs = text.split(' = ', 1) - return {lhs: parse_rhs(rhs)} - elif re.match(r"-?(\d+\.\d+(e-?\d+)?|inf)", text): - return float(text) - elif re.match(r"-?nan\(0x[a-f0-9]+\)", text): - return float("nan") +class Repeat: + def __init__(self, count): + self.count = count + +def tokenize(text): + index = 0 + while index < len(text): + int_match = re.match(r"-?\d+", text[index:]) + float_match = re.match(r"-?\d*\.\d+(e[-+]\d+)?", text[index:]) + nan_match = re.match(r"-?nan\(0x[a-f0-9]+\)", text) + hex_match = re.match(r"0x[\da-fA-F]+", text[index:]) + whitespace_match = re.match(r"[\s]+", text[index:]) + name_match = re.match(r"[a-zA-Z][a-zA-Z\d]*", text[index:]) + repeat_match = re.match(r"", text[index:]) + string_match = re.match(r'"([^"]*)"', text[index:]) + if text[index] in (",", "{", "}", "="): + yield text[index] + index += 1 + elif nan_match: + index += len(nan_match.group(0)) + yield float("nan") + elif float_match: + index += len(float_match.group(0)) + yield float(float_match.group(0)) + elif hex_match: + yield int(hex_match.group(0)[2:], 16) + index += len(hex_match.group(0)) + elif int_match: + index += len(int_match.group(0)) + yield int(int_match.group(0)) + elif whitespace_match: + index += len(whitespace_match.group(0)) + elif name_match: + index += len(name_match.group(0)) + yield name_match.group(0) + elif repeat_match: + index += len(repeat_match.group(0)) + yield Repeat(int(repeat_match.group(1))) + elif string_match: + # Note: no attempt is made to deal with escaped characters. + index += len(string_match.group(0)) + yield string_match.group(1) + else: + raise Exception(text[index:]) + +def parse_dict(tokens): + assert tokens[0] == "{" + tokens.pop(0) + result = {} + while True: + key = tokens.pop(0) + assert tokens.pop(0) == "=" + value = parse_tokens(tokens) + result[key] = value + token = tokens.pop(0) + if token == "}": + return result + assert token == "," + +def parse_list(tokens): + assert tokens[0] == "{" + tokens.pop(0) + result = [] + while True: + result.append(tokens.pop(0)) + token = tokens.pop(0) + if isinstance(token, Repeat): + result += [result[-1]] * (token.count - 1) + token = tokens.pop(0) + if token == "}": + return result + assert token == "," + +def parse_dict_or_list(tokens): + assert tokens[0] == "{" + if tokens[2] == "=": + return parse_dict(tokens) else: - return int(text, 0) + return parse_list(tokens) + +def parse_tokens(tokens): + if isinstance(tokens[0], (float, int)): + return tokens.pop(0) + if tokens[0] == "{": + return parse_dict_or_list(tokens) + if isinstance(tokens[0], str): + return tokens.pop(0) + raise Exception("Unsupported tokens: %r" % tokens) + +def parse_rhs(text): + tokens = list(tokenize(text)) + result = parse_tokens(tokens) + if tokens: + raise Exception("Unexpected input: %r" % tokens) + return result class Gdb: """A single gdb class which can interact with one or more gdb instances.""" @@ -622,7 +701,7 @@ class Gdb: output = self.command("info registers %s" % group, ops=5) result = {} for line in output.splitlines(): - m = re.match(r"(\w+)\s+({.*})\s+(\(.*\))", line) + m = re.match(r"(\w+)\s+({.*})(?:\s+(\(.*\)))?", line) if m: parts = m.groups() else: -- cgit v1.1