From 3a44725d27f6b2c77f0ca912d792b6856fde6a17 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 7 Aug 2017 12:51:42 -0700 Subject: [PATCH] Make the debug tests aware of multicore. Targets now contain an array of harts. When running a regular test, one hart is selected to run the test on while the remaining harts are parked in a safe infinite loop. There's currently only one test that tests multicore behavior, but there could be more. The infrastructure should be able to support heterogeneous multicore, but I don't have a target like that to test with. --- debug/Makefile | 4 +- debug/gdbserver.py | 93 +++++------ debug/programs/entry.S | 7 + debug/programs/start.S | 12 -- debug/targets.py | 144 ++++++++++-------- .../targets/RISC-V/{spike32.cfg => spike.cfg} | 5 +- debug/targets/RISC-V/spike32-2.py | 11 ++ debug/targets/RISC-V/spike32.py | 7 +- debug/targets/RISC-V/spike64-2.py | 11 ++ debug/targets/RISC-V/spike64.cfg | 19 --- debug/targets/RISC-V/spike64.py | 7 +- debug/targets/SiFive/Freedom/E300.py | 9 +- debug/targets/SiFive/Freedom/E300Sim.py | 9 +- debug/targets/SiFive/Freedom/U500.py | 7 +- debug/targets/SiFive/Freedom/U500Sim.py | 11 +- debug/targets/SiFive/HiFive1.py | 5 +- debug/testlib.py | 95 ++++++++---- 17 files changed, 267 insertions(+), 189 deletions(-) delete mode 100644 debug/programs/start.S rename debug/targets/RISC-V/{spike32.cfg => spike.cfg} (63%) create mode 100644 debug/targets/RISC-V/spike32-2.py create mode 100644 debug/targets/RISC-V/spike64-2.py delete mode 100644 debug/targets/RISC-V/spike64.cfg diff --git a/debug/Makefile b/debug/Makefile index 2d8d367..33988dd 100644 --- a/debug/Makefile +++ b/debug/Makefile @@ -4,9 +4,9 @@ XLEN ?= 64 src_dir ?= . GDBSERVER_PY = $(src_dir)/gdbserver.py -default: spike$(XLEN) +default: spike$(XLEN)-2 -all: pylint spike32 spike64 +all: pylint spike32 spike64 spike32-2 spike64-2 pylint: pylint --rcfile=pylint.rc `git ls-files '*.py'` diff --git a/debug/gdbserver.py b/debug/gdbserver.py index cbb1299..092f018 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -12,7 +12,7 @@ import targets import testlib from testlib import assertEqual, assertNotEqual, assertIn, assertNotIn from testlib import assertGreater, assertRegexpMatches, assertLess -from testlib import GdbTest +from testlib import GdbTest, GdbSingleHartTest MSTATUS_UIE = 0x00000001 MSTATUS_SIE = 0x00000002 @@ -66,8 +66,8 @@ def readable_binary_string(s): class SimpleRegisterTest(GdbTest): def check_reg(self, name): - a = random.randrange(1< 1 + def setup(self): self.gdb.load() def test(self): - threads = self.gdb.threads() - if len(threads) < 2: - return 'not_applicable' - - for t in threads: - self.gdb.thread(t) + for hart in self.target.harts: + self.gdb.select_hart(hart) self.gdb.p("$pc=_start") # Run to main @@ -456,18 +456,19 @@ class MulticoreTest(GdbTest): # Confirmed that we read different register values for different harts. # Write a new value to x1, and run through the add sequence again. - for t in threads: - self.gdb.thread(t) - self.gdb.p("$x1=0x%x" % (int(t.id) * 0x800)) + for hart in self.target.harts: + self.gdb.select_hart(hart) + self.gdb.p("$x1=0x%x" % (hart.index * 0x800)) self.gdb.p("$pc=main_post_csrr") self.gdb.c() for t in self.gdb.threads(): assertIn("main_end", t.frame) + for hart in self.target.harts: # Check register values. - self.gdb.thread(t) + self.gdb.select_hart(hart) for n in range(1, 32): value = self.gdb.p("$x%d" % n) - assertEqual(value, int(t.id) * 0x800 + n - 1) + assertEqual(value, hart.index * 0x800 + n - 1) class StepTest(GdbTest): compile_args = ("programs/step.S", ) @@ -479,7 +480,7 @@ class StepTest(GdbTest): def test(self): main_address = self.gdb.p("$pc") - if self.target.extensionSupported("c"): + if self.hart.extensionSupported("c"): sequence = (4, 8, 0xc, 0xe, 0x14, 0x18, 0x22, 0x1c, 0x24, 0x24) else: sequence = (4, 8, 0xc, 0x10, 0x18, 0x1c, 0x28, 0x20, 0x2c, 0x2c) @@ -558,16 +559,16 @@ class TriggerStoreAddressInstant(TriggerTest): class TriggerDmode(TriggerTest): def early_applicable(self): - return self.target.honors_tdata1_hmode + return self.hart.honors_tdata1_hmode def check_triggers(self, tdata1_lsbs, tdata2): - dmode = 1 << (self.target.xlen-5) + dmode = 1 << (self.hart.xlen-5) triggers = [] - if self.target.xlen == 32: + if self.hart.xlen == 32: xlen_type = 'int' - elif self.target.xlen == 64: + elif self.hart.xlen == 64: xlen_type = 'long long' else: raise NotImplementedError @@ -627,7 +628,7 @@ class WriteGprs(RegsTest): self.gdb.command("info registers") for n in range(len(regs)): assertEqual(self.gdb.x("data+%d" % (8*n), 'g'), - ((0xdeadbeef<\n") @@ -677,7 +678,7 @@ class DownloadTest(GdbTest): if self.crc < 0: self.crc += 2**32 - self.binary = self.target.compile(self.download_c.name, + self.binary = self.hart.compile(self.download_c.name, "programs/checksum.c") self.gdb.command("file %s" % self.binary) @@ -708,7 +709,7 @@ class DownloadTest(GdbTest): # # pylint: disable=attribute-defined-outside-init # self.gdb.load() # -# misa = self.target.misa +# misa = self.hart.misa # self.supported = set() # if misa & (1<<20): # self.supported.add(0) diff --git a/debug/programs/entry.S b/debug/programs/entry.S index ff8ae30..c3be611 100755 --- a/debug/programs/entry.S +++ b/debug/programs/entry.S @@ -157,9 +157,16 @@ trap_entry: addi sp, sp, 32*REGBYTES mret +loop_forever: + j loop_forever + // Fill the stack with data so we can see if it was overrun. .align 4 stack_bottom: .fill STACK_SIZE/4, 4, 0x22446688 stack_top: + // Prevent stack_top from being identical to next symbol, which may cause gdb + // to report we're halted at stack_top which happens to be the same address + // as main. + .word 0 #endif diff --git a/debug/programs/start.S b/debug/programs/start.S deleted file mode 100644 index 76c37bb..0000000 --- a/debug/programs/start.S +++ /dev/null @@ -1,12 +0,0 @@ - .global _start - -_start: - la sp, stack_end - jal main -done: - j done - - .data -stack: - .fill 4096, 1, 0 -stack_end: diff --git a/debug/targets.py b/debug/targets.py index 7183a38..8efa255 100644 --- a/debug/targets.py +++ b/debug/targets.py @@ -5,88 +5,44 @@ import tempfile import testlib -class Target(object): - # pylint: disable=too-many-instance-attributes - - # Name of the target. Defaults to the name of the class. - name = None - - # XLEN of the target. May be overridden with --32 or --64 command line +class Hart(object): + # XLEN of the hart. May be overridden with --32 or --64 command line # options. xlen = 0 - # GDB remotetimeout setting. - timeout_sec = 2 - - # Path to OpenOCD configuration file relative to the .py file where the - # target is defined. Defaults to .cfg. - openocd_config_path = None - - # Timeout waiting for the server to start up. This is different than the - # GDB timeout, which is how long GDB waits for commands to execute. - # The server_timeout is how long this script waits for the Server to be - # ready for GDB connections. - server_timeout_sec = 60 - - # Path to linker script relative to the .py file where the target is - # defined. Defaults to .lds. - link_script_path = None - # Will be autodetected (by running ExamineTarget) if left unset. Set to # save a little time. misa = None - # List of commands that should be executed in gdb after connecting but - # before starting the test. - gdb_setup = [] + # Path to linker script relative to the .py file where the target is + # defined. Defaults to .lds. + link_script_path = None - # Implements dmode in tdata1 as described in the spec. Targets that need + # Implements dmode in tdata1 as described in the spec. Harts that need # this value set to False are not compliant with the spec (but still usable # as long as running code doesn't try to mess with triggers set by an # external debugger). honors_tdata1_hmode = True - # Internal variables: - directory = None - temporary_files = [] - temporary_binary = None + # Address where a r/w/x block of RAM starts, together with its size. + ram = None + ram_size = None - def __init__(self, path, parsed): - # Path to module. - self.path = path - self.directory = os.path.dirname(path) - self.server_cmd = parsed.server_cmd - self.sim_cmd = parsed.sim_cmd - self.isolate = parsed.isolate - if not self.name: - self.name = type(self).__name__ - # Default OpenOCD config file to .cfg - if not self.openocd_config_path: - self.openocd_config_path = "%s.cfg" % self.name - self.openocd_config_path = os.path.join(self.directory, - self.openocd_config_path) - # Default link script to .lds - if not self.link_script_path: - self.link_script_path = "%s.lds" % self.name - self.link_script_path = os.path.join(self.directory, - self.link_script_path) + # Number of instruction triggers the hart supports. + instruction_hardware_breakpoint_count = 0 - def create(self): - """Create the target out of thin air, eg. start a simulator.""" - pass + # Defaults to target- + name = None - def server(self): - """Start the debug server that gdb connects to, eg. OpenOCD.""" - return testlib.Openocd(server_cmd=self.server_cmd, - config=self.openocd_config_path, - timeout=self.server_timeout_sec) + def __init__(self): + self.temporary_binary = None def compile(self, *sources): binary_name = "%s_%s-%d" % ( self.name, os.path.basename(os.path.splitext(sources[0])[0]), self.xlen) - if self.isolate: + if Target.isolate: self.temporary_binary = tempfile.NamedTemporaryFile( prefix=binary_name + "_") binary_name = self.temporary_binary.name @@ -114,6 +70,69 @@ class Target(object): else: return False +class Target(object): + # pylint: disable=too-many-instance-attributes + + # List of Hart object instances, one for each hart in the target. + harts = [] + + # Name of the target. Defaults to the name of the class. + name = None + + # GDB remotetimeout setting. + timeout_sec = 2 + + # Timeout waiting for the server to start up. This is different than the + # GDB timeout, which is how long GDB waits for commands to execute. + # The server_timeout is how long this script waits for the Server to be + # ready for GDB connections. + server_timeout_sec = 60 + + # Path to OpenOCD configuration file relative to the .py file where the + # target is defined. Defaults to .cfg. + openocd_config_path = None + + # List of commands that should be executed in gdb after connecting but + # before starting the test. + gdb_setup = [] + + # Internal variables: + directory = None + temporary_files = [] + + def __init__(self, path, parsed): + # Path to module. + self.path = path + self.directory = os.path.dirname(path) + self.server_cmd = parsed.server_cmd + self.sim_cmd = parsed.sim_cmd + Target.isolate = parsed.isolate + if not self.name: + self.name = type(self).__name__ + # Default OpenOCD config file to .cfg + if not self.openocd_config_path: + self.openocd_config_path = "%s.cfg" % self.name + self.openocd_config_path = os.path.join(self.directory, + self.openocd_config_path) + for i, hart in enumerate(self.harts): + hart.index = i + if not hart.name: + hart.name = "%s-%d" % (self.name, i) + # Default link script to .lds + if not hart.link_script_path: + hart.link_script_path = "%s.lds" % self.name + hart.link_script_path = os.path.join(self.directory, + hart.link_script_path) + + def create(self): + """Create the target out of thin air, eg. start a simulator.""" + pass + + def server(self): + """Start the debug server that gdb connects to, eg. OpenOCD.""" + return testlib.Openocd(server_cmd=self.server_cmd, + config=self.openocd_config_path) + def add_target_options(parser): parser.add_argument("target", help=".py file that contains definition for " "the target to test with.") @@ -149,4 +168,7 @@ def target(parsed): assert len(found) == 1, "%s does not define exactly one subclass of " \ "targets.Target" % parsed.target - return found[0](parsed.target, parsed) + t = found[0](parsed.target, parsed) + assert t.harts, "%s doesn't have any harts defined!" % t.name + + return t diff --git a/debug/targets/RISC-V/spike32.cfg b/debug/targets/RISC-V/spike.cfg similarity index 63% rename from debug/targets/RISC-V/spike32.cfg rename to debug/targets/RISC-V/spike.cfg index 2742335..9b1841c 100644 --- a/debug/targets/RISC-V/spike32.cfg +++ b/debug/targets/RISC-V/spike.cfg @@ -8,12 +8,9 @@ set _CHIPNAME riscv jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913 set _TARGETNAME $_CHIPNAME.cpu -#target create $_TARGETNAME riscv -chain-position $_TARGETNAME -rtos riscv -target create $_TARGETNAME riscv -chain-position $_TARGETNAME +target create $_TARGETNAME riscv -chain-position $_TARGETNAME -rtos riscv gdb_report_data_abort enable init reset halt - -echo "Ready for Remote Connections" diff --git a/debug/targets/RISC-V/spike32-2.py b/debug/targets/RISC-V/spike32-2.py new file mode 100644 index 0000000..3f87d26 --- /dev/null +++ b/debug/targets/RISC-V/spike32-2.py @@ -0,0 +1,11 @@ +import targets +import testlib + +import spike32 + +class spike32_2(targets.Target): + harts = [spike32.spike32_hart(), spike32.spike32_hart()] + openocd_config_path = "spike.cfg" + + def create(self): + return testlib.Spike(self) diff --git a/debug/targets/RISC-V/spike32.py b/debug/targets/RISC-V/spike32.py index 3bf8b47..e80f60a 100644 --- a/debug/targets/RISC-V/spike32.py +++ b/debug/targets/RISC-V/spike32.py @@ -1,12 +1,17 @@ import targets import testlib -class spike32(targets.Target): +class spike32_hart(targets.Hart): xlen = 32 ram = 0x10000000 ram_size = 0x10000000 instruction_hardware_breakpoint_count = 4 reset_vector = 0x1000 + link_script_path = "spike64.lds" + +class spike32(targets.Target): + harts = [spike32_hart()] + openocd_config_path = "spike.cfg" def create(self): return testlib.Spike(self) diff --git a/debug/targets/RISC-V/spike64-2.py b/debug/targets/RISC-V/spike64-2.py new file mode 100644 index 0000000..e1df6d2 --- /dev/null +++ b/debug/targets/RISC-V/spike64-2.py @@ -0,0 +1,11 @@ +import targets +import testlib + +import spike64 + +class spike64_2(targets.Target): + harts = [spike64.spike64_hart(), spike64.spike64_hart()] + openocd_config_path = "spike.cfg" + + def create(self): + return testlib.Spike(self) diff --git a/debug/targets/RISC-V/spike64.cfg b/debug/targets/RISC-V/spike64.cfg deleted file mode 100644 index 2742335..0000000 --- a/debug/targets/RISC-V/spike64.cfg +++ /dev/null @@ -1,19 +0,0 @@ -adapter_khz 10000 - -interface remote_bitbang -remote_bitbang_host $::env(REMOTE_BITBANG_HOST) -remote_bitbang_port $::env(REMOTE_BITBANG_PORT) - -set _CHIPNAME riscv -jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913 - -set _TARGETNAME $_CHIPNAME.cpu -#target create $_TARGETNAME riscv -chain-position $_TARGETNAME -rtos riscv -target create $_TARGETNAME riscv -chain-position $_TARGETNAME - -gdb_report_data_abort enable - -init -reset halt - -echo "Ready for Remote Connections" diff --git a/debug/targets/RISC-V/spike64.py b/debug/targets/RISC-V/spike64.py index c705857..84586e3 100644 --- a/debug/targets/RISC-V/spike64.py +++ b/debug/targets/RISC-V/spike64.py @@ -1,12 +1,17 @@ import targets import testlib -class spike64(targets.Target): +class spike64_hart(targets.Hart): xlen = 64 ram = 0x1212340000 ram_size = 0x10000000 instruction_hardware_breakpoint_count = 4 reset_vector = 0x1000 + link_script_path = "spike64.lds" + +class spike64(targets.Target): + harts = [spike64_hart()] + openocd_config_path = "spike.cfg" def create(self): return testlib.Spike(self) diff --git a/debug/targets/SiFive/Freedom/E300.py b/debug/targets/SiFive/Freedom/E300.py index 95ddcfd..170de40 100644 --- a/debug/targets/SiFive/Freedom/E300.py +++ b/debug/targets/SiFive/Freedom/E300.py @@ -1,9 +1,12 @@ import targets -class E300(targets.Target): +class E300Hart(targets.Hart): xlen = 32 ram = 0x80000000 - ram_size = 16 * 1024 + ram_size = 256 * 1024 * 1024 instruction_hardware_breakpoint_count = 2 - openocd_config_path = "Freedom.cfg" link_script_path = "Freedom.lds" + +class E300(targets.Target): + openocd_config_path = "Freedom.cfg" + harts = [E300Hart()] diff --git a/debug/targets/SiFive/Freedom/E300Sim.py b/debug/targets/SiFive/Freedom/E300Sim.py index 91be2e8..f9428d0 100644 --- a/debug/targets/SiFive/Freedom/E300Sim.py +++ b/debug/targets/SiFive/Freedom/E300Sim.py @@ -1,14 +1,17 @@ import targets import testlib -class E300Sim(targets.Target): +class E300Hart(targets.Hart): xlen = 32 - timeout_sec = 6000 ram = 0x80000000 ram_size = 256 * 1024 * 1024 instruction_hardware_breakpoint_count = 2 - openocd_config_path = "Freedom.cfg" link_script_path = "Freedom.lds" +class E300Sim(targets.Target): + timeout_sec = 6000 + openocd_config_path = "Freedom.cfg" + harts = [E300Hart()] + def create(self): return testlib.VcsSim(sim_cmd=self.sim_cmd, debug=False) diff --git a/debug/targets/SiFive/Freedom/U500.py b/debug/targets/SiFive/Freedom/U500.py index c22aa4c..6da3ac5 100644 --- a/debug/targets/SiFive/Freedom/U500.py +++ b/debug/targets/SiFive/Freedom/U500.py @@ -1,9 +1,12 @@ import targets -class U500(targets.Target): +class U500Hart(targets.Hart): xlen = 64 ram = 0x80000000 ram_size = 16 * 1024 instruction_hardware_breakpoint_count = 2 - openocd_config_path = "Freedom.cfg" link_script_path = "Freedom.lds" + +class U500(targets.Target): + openocd_config_path = "Freedom.cfg" + harts = [U500Hart()] diff --git a/debug/targets/SiFive/Freedom/U500Sim.py b/debug/targets/SiFive/Freedom/U500Sim.py index 62bc827..5c500c4 100644 --- a/debug/targets/SiFive/Freedom/U500Sim.py +++ b/debug/targets/SiFive/Freedom/U500Sim.py @@ -1,14 +1,17 @@ import targets import testlib -class U500Sim(targets.Target): +class U500Hart(targets.Hart): xlen = 64 - timeout_sec = 6000 ram = 0x80000000 ram_size = 256 * 1024 * 1024 instruction_hardware_breakpoint_count = 2 - openocd_config_path = "Freedom.cfg" link_script_path = "Freedom.lds" - def create(self): +class U500Sim(Target): + timeout_sec = 6000 + openocd_config_path = "Freedom.cfg" + harts = [U500Hart()] + + def target(self): return testlib.VcsSim(sim_cmd=self.sim_cmd, debug=False) diff --git a/debug/targets/SiFive/HiFive1.py b/debug/targets/SiFive/HiFive1.py index 813829e..3cb508c 100644 --- a/debug/targets/SiFive/HiFive1.py +++ b/debug/targets/SiFive/HiFive1.py @@ -1,8 +1,11 @@ import targets -class HiFive1(targets.Target): +class HiFive1Hart(targets.Hart): xlen = 32 ram = 0x80000000 ram_size = 16 * 1024 instruction_hardware_breakpoint_count = 2 misa = 0x40001105 + +class HiFive1(targets.Target): + harts = [HiFive1Hart()] diff --git a/debug/testlib.py b/debug/testlib.py index b76f320..d6c7f8a 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -36,13 +36,12 @@ def compile(args, xlen=32): # pylint: disable=redefined-builtin cmd.append(found) else: cmd.append(arg) + header("Compile") + print "+", " ".join(cmd) process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = process.communicate() if process.returncode: - print - header("Compile failed") - print "+", " ".join(cmd) print stdout, print stderr, header("") @@ -63,16 +62,34 @@ class Spike(object): def __init__(self, target, halted=False, timeout=None, with_jtag_gdb=True): """Launch spike. Return tuple of its process and the port it's running on.""" + self.process = None + + if target.harts: + harts = target.harts + else: + harts = [target] + if target.sim_cmd: cmd = shlex.split(target.sim_cmd) else: spike = os.path.expandvars("$RISCV/bin/spike") cmd = [spike] - if target.xlen == 32: + + cmd += ["-p%d" % len(harts)] + + assert len(set(t.xlen for t in harts)) == 1, \ + "All spike harts must have the same XLEN" + + if harts[0].xlen == 32: cmd += ["--isa", "RV32G"] else: cmd += ["--isa", "RV64G"] - cmd += ["-m0x%x:0x%x" % (target.ram, target.ram_size)] + + 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, \ + "All spike harts must have the same RAM layout" + cmd += ["-m0x%x:0x%x" % (harts[0].ram, harts[0].ram_size)] if timeout: cmd = ["timeout", str(timeout)] + cmd @@ -82,7 +99,7 @@ class Spike(object): if with_jtag_gdb: cmd += ['--rbb-port', '0'] os.environ['REMOTE_BITBANG_HOST'] = 'localhost' - self.infinite_loop = target.compile( + self.infinite_loop = harts[0].compile( "programs/checksum.c", "programs/tiny-malloc.c", "programs/infinite_loop.S", "-DDEFINE_MALLOC", "-DDEFINE_FREE") cmd.append(self.infinite_loop) @@ -106,11 +123,12 @@ class Spike(object): "connection" def __del__(self): - try: - self.process.kill() - self.process.wait() - except OSError: - pass + if self.process: + try: + self.process.kill() + self.process.wait() + except OSError: + pass def wait(self, *args, **kwargs): return self.process.wait(*args, **kwargs) @@ -291,6 +309,10 @@ class Gdb(object): # Force consistency. self.command("set print entry-values no") + def select_hart(self, hart): + output = self.command("thread %d" % (hart.index + 1)) + assert "Unknown" not in output + def wait(self): """Wait for prompt.""" self.child.expect(r"\(gdb\)") @@ -391,19 +413,20 @@ def run_all_tests(module, target, parsed): gdb_cmd = parsed.gdb todo = [] - if parsed.misaval: - target.misa = int(parsed.misaval, 16) - print "Using $misa from command line: 0x%x" % target.misa - elif target.misa: - print "Using $misa from target definition: 0x%x" % target.misa - else: - todo.append(("ExamineTarget", ExamineTarget)) + for hart in target.harts: + if parsed.misaval: + hart.misa = int(parsed.misaval, 16) + print "Using $misa from command line: 0x%x" % hart.misa + elif hart.misa: + print "Using $misa from hart definition: 0x%x" % hart.misa + else: + todo.append(("ExamineTarget", ExamineTarget, hart)) for name in dir(module): definition = getattr(module, name) if type(definition) == type and hasattr(definition, 'test') and \ (not parsed.test or any(test in name for test in parsed.test)): - todo.append((name, definition)) + todo.append((name, definition, None)) results, count = run_tests(parsed, target, todo) @@ -417,12 +440,12 @@ def run_tests(parsed, target, todo): results = {} count = 0 - for name, definition in todo: - instance = definition(target) + for name, definition, hart in todo: log_name = os.path.join(parsed.logs, "%s-%s-%s.log" % (time.strftime("%Y%m%d-%H%M%S"), type(target).__name__, name)) log_fd = open(log_name, 'w') print "Running %s > %s ..." % (name, log_name), + instance = definition(target, hart) sys.stdout.flush() log_fd.write("Test: %s\n" % name) log_fd.write("Target: %s\n" % type(target).__name__) @@ -491,8 +514,12 @@ def print_log(path): class BaseTest(object): compiled = {} - def __init__(self, target): + def __init__(self, target, hart=None): self.target = target + if hart: + self.hart = hart + else: + self.hart = random.choice(target.harts) self.server = None self.target_process = None self.binary = None @@ -514,7 +541,7 @@ class BaseTest(object): if compile_args not in BaseTest.compiled: # pylint: disable=star-args BaseTest.compiled[compile_args] = \ - self.target.compile(*compile_args) + self.hart.compile(*compile_args) self.binary = BaseTest.compiled.get(compile_args) def classSetup(self): @@ -581,8 +608,8 @@ class BaseTest(object): gdb_cmd = None class GdbTest(BaseTest): - def __init__(self, target): - BaseTest.__init__(self, target) + def __init__(self, target, hart=None): + BaseTest.__init__(self, target, hart=hart) self.gdb = None def classSetup(self): @@ -598,15 +625,12 @@ class GdbTest(BaseTest): if self.binary: self.gdb.command("file %s" % self.binary) if self.target: - self.gdb.command("set arch riscv:rv%d" % self.target.xlen) + self.gdb.command("set arch riscv:rv%d" % self.hart.xlen) self.gdb.command("set remotetimeout %d" % self.target.timeout_sec) if self.server.port: self.gdb.command( "target extended-remote localhost:%d" % self.server.port) - # Select a random thread. - # TODO: Allow a command line option to force a specific thread. - thread = random.choice(self.gdb.threads()) - self.gdb.thread(thread) + self.gdb.select_hart(self.hart) for cmd in self.target.gdb_setup: self.gdb.command(cmd) @@ -618,6 +642,17 @@ class GdbTest(BaseTest): del self.gdb BaseTest.classTeardown(self) +class GdbSingleHartTest(GdbTest): + def classSetup(self): + GdbTest.classSetup(self) + + for hart in self.target.harts: + # Park all harts that we're not using in a safe place. + if hart != self.hart: + self.gdb.select_hart(hart) + self.gdb.p("$pc=loop_forever") + self.gdb.select_hart(self.hart) + class ExamineTarget(GdbTest): def test(self): self.target.misa = self.gdb.p("$misa") -- 2.30.2