From 3a44725d27f6b2c77f0ca912d792b6856fde6a17 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 7 Aug 2017 12:51:42 -0700 Subject: 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 ++++++++++++++++++-------------- debug/targets/RISC-V/spike.cfg | 16 ++++ debug/targets/RISC-V/spike32-2.py | 11 +++ debug/targets/RISC-V/spike32.cfg | 19 ----- 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 ++++++++++++++------- 18 files changed, 282 insertions(+), 204 deletions(-) delete mode 100644 debug/programs/start.S create mode 100644 debug/targets/RISC-V/spike.cfg create mode 100644 debug/targets/RISC-V/spike32-2.py delete mode 100644 debug/targets/RISC-V/spike32.cfg 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/spike.cfg b/debug/targets/RISC-V/spike.cfg new file mode 100644 index 0000000..9b1841c --- /dev/null +++ b/debug/targets/RISC-V/spike.cfg @@ -0,0 +1,16 @@ +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 + +gdb_report_data_abort enable + +init +reset halt 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.cfg b/debug/targets/RISC-V/spike32.cfg deleted file mode 100644 index 2742335..0000000 --- a/debug/targets/RISC-V/spike32.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/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") -- cgit v1.1 From 5d6b15402a5402239dee9bad68e3d57d5c1e430e Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 7 Aug 2017 15:13:09 -0700 Subject: WIP towards multiple gdb instances. --- debug/testlib.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/debug/testlib.py b/debug/testlib.py index d6c7f8a..856b903 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -218,7 +218,13 @@ class Openocd(object): logfile = open(Openocd.logname, "w") logfile.write("+ %s\n" % " ".join(cmd)) logfile.flush() - self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, + + self.ports = [] + self.port = None + self.process = self.start(cmd, logfile) + + def start(self, cmd, logfile): + process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=logfile, stderr=logfile) try: @@ -233,10 +239,14 @@ class Openocd(object): m = re.search(r"Listening on port (\d+) for gdb connections", log) if m: - self.port = int(m.group(1)) + if not self.ports: + self.port = int(m.group(1)) + self.ports.append(int(m.group(1))) + + if "telnet server disabled" in log: break - if not self.process.poll() is None: + if not process.poll() is None: raise Exception( "OpenOCD exited before completing riscv_examine()") if not messaged and time.time() - start > 1: @@ -245,7 +255,7 @@ class Openocd(object): if (time.time() - start) > timeout: raise Exception("ERROR: Timed out waiting for OpenOCD to " "listen for gdb") - + return process except Exception: header("OpenOCD log") sys.stdout.write(log) -- cgit v1.1 From efe2f16d12c23eca1e66d2a304d89aafb22f005f Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Thu, 10 Aug 2017 11:27:04 -0700 Subject: WIP multicore testing. --- debug/gdbserver.py | 36 ++++++++++++++++++++++---- debug/programs/entry.S | 43 ++++++++++++++++++++++++++------ debug/targets/RISC-V/spike32.lds | 2 ++ debug/targets/RISC-V/spike64.lds | 2 ++ debug/targets/SiFive/Freedom/Freedom.lds | 2 ++ debug/targets/SiFive/HiFive1.lds | 2 ++ debug/testlib.py | 26 ++++++++++++++----- 7 files changed, 95 insertions(+), 18 deletions(-) diff --git a/debug/gdbserver.py b/debug/gdbserver.py index 092f018..7ae3aa3 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -114,7 +114,6 @@ class SimpleF18Test(SimpleRegisterTest): assertLess(abs(float(self.gdb.p_raw("$%s" % name)) - b), .001) def early_applicable(self): - print repr(self.hart) return self.hart.extensionSupported('F') def test(self): @@ -416,20 +415,19 @@ class UserInterrupt(DebugTest): self.gdb.p("i=0") self.exit() -class MulticoreTest(GdbTest): - compile_args = ("programs/infinite_loop.S", ) +class MulticoreRegTest(GdbTest): + compile_args = ("programs/infinite_loop.S", "-DMULTICORE") def early_applicable(self): return len(self.target.harts) > 1 def setup(self): self.gdb.load() - - def test(self): for hart in self.target.harts: self.gdb.select_hart(hart) self.gdb.p("$pc=_start") + def test(self): # Run to main self.gdb.b("main") self.gdb.c() @@ -470,6 +468,34 @@ class MulticoreTest(GdbTest): value = self.gdb.p("$x%d" % n) assertEqual(value, hart.index * 0x800 + n - 1) +class MulticoreRunHaltTest(GdbTest): + compile_args = ("programs/multicore.c", "-DMULTICORE") + + def early_applicable(self): + return len(self.target.harts) > 1 + + def setup(self): + self.gdb.load() + for hart in self.target.harts: + self.gdb.select_hart(hart) + self.gdb.p("$pc=_start") + + def test(self): + previous_hart_count = [0 for h in self.target.harts] + for _ in range(10): + self.gdb.c(wait=False) + time.sleep(1) + self.gdb.interrupt() + self.gdb.p("buf", fmt="") + hart_count = self.gdb.p("hart_count") + for i, h in enumerate(self.target.harts): + assertGreater(hart_count[i], previous_hart_count[i]) + self.gdb.select_hart(h) + pc = self.gdb.p("$pc") + self.gdb.stepi() + stepped_pc = self.gdb.p("$pc") + assertNotEqual(pc, stepped_pc) + class StepTest(GdbTest): compile_args = ("programs/step.S", ) diff --git a/debug/programs/entry.S b/debug/programs/entry.S index c3be611..866636b 100755 --- a/debug/programs/entry.S +++ b/debug/programs/entry.S @@ -3,7 +3,7 @@ #include "encoding.h" -#define STACK_SIZE 512 +#define STACK_SIZE 1024 #if XLEN == 64 # define LREG ld @@ -61,8 +61,14 @@ handle_reset: la gp, __global_pointer$ .option pop - # initialize stack pointer - la sp, stack_top + # Initialize stack pointer. + # Support up to 4 harts, with IDs 0--3. + csrr t0, CSR_MHARTID + addi t0, t0, 1 + li t1, STACK_SIZE / 4 + mul t0, t0, t1 + la sp, stack_bottom + add sp, sp, t0 # Clear all hardware triggers li t0, ~0 @@ -73,8 +79,33 @@ handle_reset: csrr t1, CSR_TSELECT beq t0, t1, 1b +#ifdef MULTICORE + csrr t0, CSR_MHARTID + bnez t0, wait_until_initialized +#endif + + la t0, __bss_start + la t1, __bss_end +1: + bge t0, t1, 2f + sb zero, 0(t0) + addi t0, t0, 1 + j 1b +2: +#ifdef MULTICORE + la t0, initialized + li t1, 1 + sw t1, 0(t0) + +wait_until_initialized: # Wait for hart 0 to perform initialization. + la t0, initialized +1: + lw t1, 0(t0) + beqz t1, 1b +#endif + # perform the rest of initialization in C - j _init + j _init trap_entry: @@ -165,8 +196,6 @@ loop_forever: 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. +initialized: .word 0 #endif diff --git a/debug/targets/RISC-V/spike32.lds b/debug/targets/RISC-V/spike32.lds index 01d0e3d..84216db 100755 --- a/debug/targets/RISC-V/spike32.lds +++ b/debug/targets/RISC-V/spike32.lds @@ -22,11 +22,13 @@ SECTIONS } /* bss segment */ + __bss_start = .; .sbss : { *(.sbss .sbss.* .gnu.linkonce.sb.*) *(.scommon) } .bss : { *(.bss) } + __bss_end = .; __malloc_start = .; . = . + 512; diff --git a/debug/targets/RISC-V/spike64.lds b/debug/targets/RISC-V/spike64.lds index dc7cb63..2e7d65d 100755 --- a/debug/targets/RISC-V/spike64.lds +++ b/debug/targets/RISC-V/spike64.lds @@ -20,11 +20,13 @@ SECTIONS } /* bss segment */ + __bss_start = .; .sbss : { *(.sbss .sbss.* .gnu.linkonce.sb.*) *(.scommon) } .bss : { *(.bss) } + __bss_end = .; __malloc_start = .; . = . + 512; diff --git a/debug/targets/SiFive/Freedom/Freedom.lds b/debug/targets/SiFive/Freedom/Freedom.lds index 1e0645a..9354d3f 100644 --- a/debug/targets/SiFive/Freedom/Freedom.lds +++ b/debug/targets/SiFive/Freedom/Freedom.lds @@ -20,11 +20,13 @@ SECTIONS } /* bss segment */ + __bss_start = .; .sbss : { *(.sbss .sbss.* .gnu.linkonce.sb.*) *(.scommon) } .bss : { *(.bss) } + __bss_end = .; __malloc_start = .; . = . + 512; diff --git a/debug/targets/SiFive/HiFive1.lds b/debug/targets/SiFive/HiFive1.lds index 1e0645a..9354d3f 100755 --- a/debug/targets/SiFive/HiFive1.lds +++ b/debug/targets/SiFive/HiFive1.lds @@ -20,11 +20,13 @@ SECTIONS } /* bss segment */ + __bss_start = .; .sbss : { *(.sbss .sbss.* .gnu.linkonce.sb.*) *(.scommon) } .bss : { *(.bss) } + __bss_end = .; __malloc_start = .; . = . + 512; diff --git a/debug/testlib.py b/debug/testlib.py index 856b903..8ef50e6 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -17,8 +17,11 @@ import pexpect def find_file(path): for directory in (os.getcwd(), os.path.dirname(__file__)): fullpath = os.path.join(directory, path) - if os.path.exists(fullpath): - return fullpath + relpath = os.path.relpath(fullpath) + if len(relpath) >= len(fullpath): + relpath = fullpath + if os.path.exists(relpath): + return relpath return None def compile(args, xlen=32): # pylint: disable=redefined-builtin @@ -363,13 +366,23 @@ class Gdb(object): raise CannotAccess(int(m.group(1), 0)) return output.split('=')[-1].strip() - def p(self, obj): - output = self.command("p/x %s" % obj) + def parse_string(self, text): + text = text.strip() + if text.startswith("{") and text.endswith("}"): + inner = text[1:-1] + return [self.parse_string(t) for t in inner.split(", ")] + elif text.startswith('"') and text.endswith('"'): + return text[1:-1] + else: + return int(text, 0) + + def p(self, obj, fmt="/x"): + output = self.command("p%s %s" % (fmt, obj)) m = re.search("Cannot access memory at address (0x[0-9a-f]+)", output) if m: raise CannotAccess(int(m.group(1), 0)) - value = int(output.split('=')[-1].strip(), 0) - return value + rhs = output.split('=')[-1] + return self.parse_string(rhs) def p_string(self, obj): output = self.command("p %s" % obj) @@ -530,6 +543,7 @@ class BaseTest(object): self.hart = hart else: self.hart = random.choice(target.harts) + self.hart = target.harts[-1] #<<< self.server = None self.target_process = None self.binary = None -- cgit v1.1 From 28fbcac967d6c2a0085faa2007c98d64d3203491 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 11 Aug 2017 12:55:25 -0700 Subject: Make pylint happy. --- debug/targets/RISC-V/spike32-2.py | 2 +- debug/targets/RISC-V/spike64-2.py | 2 +- debug/targets/SiFive/Freedom/U500Sim.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/debug/targets/RISC-V/spike32-2.py b/debug/targets/RISC-V/spike32-2.py index 3f87d26..09bab1d 100644 --- a/debug/targets/RISC-V/spike32-2.py +++ b/debug/targets/RISC-V/spike32-2.py @@ -1,7 +1,7 @@ import targets import testlib -import spike32 +import spike32 # pylint: disable=import-error class spike32_2(targets.Target): harts = [spike32.spike32_hart(), spike32.spike32_hart()] diff --git a/debug/targets/RISC-V/spike64-2.py b/debug/targets/RISC-V/spike64-2.py index e1df6d2..f2e0a8a 100644 --- a/debug/targets/RISC-V/spike64-2.py +++ b/debug/targets/RISC-V/spike64-2.py @@ -1,7 +1,7 @@ import targets import testlib -import spike64 +import spike64 # pylint: disable=import-error class spike64_2(targets.Target): harts = [spike64.spike64_hart(), spike64.spike64_hart()] diff --git a/debug/targets/SiFive/Freedom/U500Sim.py b/debug/targets/SiFive/Freedom/U500Sim.py index 5c500c4..065ab08 100644 --- a/debug/targets/SiFive/Freedom/U500Sim.py +++ b/debug/targets/SiFive/Freedom/U500Sim.py @@ -8,7 +8,7 @@ class U500Hart(targets.Hart): instruction_hardware_breakpoint_count = 2 link_script_path = "Freedom.lds" -class U500Sim(Target): +class U500Sim(targets.Target): timeout_sec = 6000 openocd_config_path = "Freedom.cfg" harts = [U500Hart()] -- cgit v1.1 From 732d871e2af7b405f3f4a24ec1625c2fc8cfd1e1 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 11 Aug 2017 13:25:06 -0700 Subject: Rename test to MulticoreRunHaltStepiTest. --- debug/gdbserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug/gdbserver.py b/debug/gdbserver.py index 7ae3aa3..d5125a7 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -468,7 +468,7 @@ class MulticoreRegTest(GdbTest): value = self.gdb.p("$x%d" % n) assertEqual(value, hart.index * 0x800 + n - 1) -class MulticoreRunHaltTest(GdbTest): +class MulticoreRunHaltStepiTest(GdbTest): compile_args = ("programs/multicore.c", "-DMULTICORE") def early_applicable(self): -- cgit v1.1 From 46352a740f8d932411dcc21e420c77d9b804ae34 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Sun, 13 Aug 2017 12:50:39 -0700 Subject: Fix MemTest* after sloppy rebase. --- debug/gdbserver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debug/gdbserver.py b/debug/gdbserver.py index d5125a7..3fa67df 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -124,8 +124,8 @@ class SimpleMemoryTest(GdbTest): assertEqual(self.gdb.p("sizeof(%s)" % data_type), size) a = 0x86753095555aaaa & ((1<<(size*8))-1) b = 0xdeadbeef12345678 & ((1<<(size*8))-1) - addrA = self.target.ram - addrB = self.target.ram + self.target.ram_size - size + addrA = self.hart.ram + addrB = self.hart.ram + self.hart.ram_size - size self.gdb.p("*((%s*)0x%x) = 0x%x" % (data_type, addrA, a)) self.gdb.p("*((%s*)0x%x) = 0x%x" % (data_type, addrB, b)) assertEqual(self.gdb.p("*((%s*)0x%x)" % (data_type, addrA)), a) -- cgit v1.1 From 4ea3f0be40f1ed31dabfc060865d5af863106cfe Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Sun, 13 Aug 2017 13:56:44 -0700 Subject: Make MemTestBlock output a more descriptive error. --- debug/gdbserver.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/debug/gdbserver.py b/debug/gdbserver.py index 3fa67df..78ac9aa 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, GdbSingleHartTest +from testlib import GdbTest, GdbSingleHartTest, TestFailed MSTATUS_UIE = 0x00000001 MSTATUS_SIE = 0x00000002 @@ -171,20 +171,21 @@ class MemTest64(SimpleMemoryTest): # self.gdb.p("*((int*)0x%x)=6874742" % self.hart.ram) class MemTestBlock(GdbTest): + length = 1024 + line_length = 16 + def test(self): - length = 1024 - line_length = 16 a = tempfile.NamedTemporaryFile(suffix=".ihex") data = "" - for i in range(length / line_length): + for i in range(self.length / self.line_length): line_data = "".join(["%c" % random.randrange(256) - for _ in range(line_length)]) + for _ in range(self.line_length)]) data += line_data - a.write(ihex_line(i * line_length, 0, line_data)) + a.write(ihex_line(i * self.line_length, 0, line_data)) a.flush() self.gdb.command("restore %s 0x%x" % (a.name, self.hart.ram)) - for offset in range(0, length, 19*4) + [length-4]: + for offset in range(0, self.length, 19*4) + [self.length-4]: value = self.gdb.p("*((int*)0x%x)" % (self.hart.ram + offset)) written = ord(data[offset]) | \ (ord(data[offset+1]) << 8) | \ @@ -194,13 +195,16 @@ class MemTestBlock(GdbTest): b = tempfile.NamedTemporaryFile(suffix=".ihex") self.gdb.command("dump ihex memory %s 0x%x 0x%x" % (b.name, - self.hart.ram, self.hart.ram + length)) + self.hart.ram, self.hart.ram + self.length)) for line in b: record_type, address, line_data = ihex_parse(line) if record_type == 0: - assertEqual(readable_binary_string(line_data), - readable_binary_string( - data[address:address+len(line_data)])) + written_data = data[address:address+len(line_data)] + if line_data != written_data: + raise TestFailed( + "Data mismatch at 0x%x; wrote %s but read %s" % ( + address, readable_binary_string(written_data), + readable_binary_string(line_data))) class InstantHaltTest(GdbTest): def test(self): -- cgit v1.1 From b8646a70258674f7de88145dbd2a7ab8938b77ae Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Tue, 15 Aug 2017 15:32:24 -0700 Subject: Fix rebase bug. --- debug/testlib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/debug/testlib.py b/debug/testlib.py index 8ef50e6..692afb6 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -185,6 +185,8 @@ class Openocd(object): print "OpenOCD Temporary Log File: %s" % logname def __init__(self, server_cmd=None, config=None, debug=False, timeout=60): + self.timeout = timeout + if server_cmd: cmd = shlex.split(server_cmd) else: @@ -255,7 +257,7 @@ class Openocd(object): if not messaged and time.time() - start > 1: messaged = True print "Waiting for OpenOCD to start..." - if (time.time() - start) > timeout: + if (time.time() - start) > self.timeout: raise Exception("ERROR: Timed out waiting for OpenOCD to " "listen for gdb") return process -- cgit v1.1 From 6650f4e3f7b11d581a8be07a2beb16f69530fb36 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 28 Aug 2017 12:17:37 -0700 Subject: Increase remotetimeout for spike targets. On overloaded systems, when executing compare-sections, otherwise gdb might hit a timeout and the compare-sections code doesn't deal with it. (You get an error message complaining that 130 is not a valid hex digit.) --- debug/targets/RISC-V/spike32-2.py | 1 + debug/targets/RISC-V/spike32.py | 1 + debug/targets/RISC-V/spike64-2.py | 1 + debug/targets/RISC-V/spike64-2gdb.py | 11 +++++++++++ debug/targets/RISC-V/spike64.py | 1 + 5 files changed, 15 insertions(+) create mode 100644 debug/targets/RISC-V/spike64-2gdb.py diff --git a/debug/targets/RISC-V/spike32-2.py b/debug/targets/RISC-V/spike32-2.py index 09bab1d..6cf558d 100644 --- a/debug/targets/RISC-V/spike32-2.py +++ b/debug/targets/RISC-V/spike32-2.py @@ -6,6 +6,7 @@ import spike32 # pylint: disable=import-error class spike32_2(targets.Target): harts = [spike32.spike32_hart(), spike32.spike32_hart()] openocd_config_path = "spike.cfg" + timeout_sec = 30 def create(self): return testlib.Spike(self) diff --git a/debug/targets/RISC-V/spike32.py b/debug/targets/RISC-V/spike32.py index e80f60a..fc7d598 100644 --- a/debug/targets/RISC-V/spike32.py +++ b/debug/targets/RISC-V/spike32.py @@ -12,6 +12,7 @@ class spike32_hart(targets.Hart): class spike32(targets.Target): harts = [spike32_hart()] openocd_config_path = "spike.cfg" + timeout_sec = 30 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 index f2e0a8a..c6321dc 100644 --- a/debug/targets/RISC-V/spike64-2.py +++ b/debug/targets/RISC-V/spike64-2.py @@ -6,6 +6,7 @@ import spike64 # pylint: disable=import-error class spike64_2(targets.Target): harts = [spike64.spike64_hart(), spike64.spike64_hart()] openocd_config_path = "spike.cfg" + timeout_sec = 30 def create(self): return testlib.Spike(self) diff --git a/debug/targets/RISC-V/spike64-2gdb.py b/debug/targets/RISC-V/spike64-2gdb.py new file mode 100644 index 0000000..200ffaa --- /dev/null +++ b/debug/targets/RISC-V/spike64-2gdb.py @@ -0,0 +1,11 @@ +import targets +import testlib + +import spike64 + +class spike64_2gdb(targets.Target): + harts = [spike64.spike64_hart(), spike64.spike64_hart()] + openocd_config_path = "spike-2gdb.cfg" + + def create(self): + return testlib.Spike(self) diff --git a/debug/targets/RISC-V/spike64.py b/debug/targets/RISC-V/spike64.py index 84586e3..6e3da89 100644 --- a/debug/targets/RISC-V/spike64.py +++ b/debug/targets/RISC-V/spike64.py @@ -12,6 +12,7 @@ class spike64_hart(targets.Hart): class spike64(targets.Target): harts = [spike64_hart()] openocd_config_path = "spike.cfg" + timeout_sec = 30 def create(self): return testlib.Spike(self) -- cgit v1.1 From 1bfc5f01ffb5bb838416a621770ae9d102c9a715 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 28 Aug 2017 12:43:08 -0700 Subject: Forgot to add this file. --- debug/programs/multicore.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 debug/programs/multicore.c diff --git a/debug/programs/multicore.c b/debug/programs/multicore.c new file mode 100644 index 0000000..d7dd845 --- /dev/null +++ b/debug/programs/multicore.c @@ -0,0 +1,81 @@ +#include + +typedef struct { + int counter; +} atomic_t; + +static inline int atomic_xchg(atomic_t *v, int n) +{ + register int c; + + __asm__ __volatile__ ( + "amoswap.w.aqrl %0, %2, %1" + : "=r" (c), "+A" (v->counter) + : "r" (n)); + return c; +} + +#define csr_read(csr) \ +({ \ + register unsigned long __v; \ + __asm__ __volatile__ ("csrr %0, " #csr \ + : "=r" (__v)); \ + __v; \ +}) + +static inline void mb(void) +{ + __asm__ __volatile__ ("fence"); +} + +void get_lock(atomic_t *lock) +{ + while (atomic_xchg(lock, 1) == 1) + ; + mb(); +} + +void put_lock(atomic_t *lock) +{ + mb(); + atomic_xchg(lock, 0); +} + +static atomic_t buf_lock = { .counter = 0 }; +static char buf[32]; +static int buf_initialized; +static unsigned hart_count[2]; + +static const char case_bit = 'a' - 'A'; +volatile int initialized; + +int main() +{ + uint32_t hartid = csr_read(mhartid); + hart_count[hartid] = 0; + + while (1) { + get_lock(&buf_lock); + hart_count[hartid]++; + + if (!buf_initialized) { + for (unsigned i = 0; i < sizeof(buf); i++) { + buf[i] = 'A' + (i % 26); + } + buf_initialized = 1; + } + + char first = buf[0]; + int offset = (first & ~0x20) - 'A'; + for (unsigned i = 0; i < sizeof(buf); i++) { + while (buf[i] != (first - offset + ((offset + i) % 26))) + ; + + if (hartid & 1) + buf[i] = 'A' + ((i + hartid + hart_count[hartid]) % 26); + else + buf[i] = 'a' + ((i + hartid + hart_count[hartid]) % 26); + } + put_lock(&buf_lock); + } +} -- cgit v1.1 From ea039ea3cec43fefa1df96339bea93a5dcb06d5a Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 28 Aug 2017 13:40:15 -0700 Subject: This file isn't ready yet. --- debug/targets/RISC-V/spike64-2gdb.py | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 debug/targets/RISC-V/spike64-2gdb.py diff --git a/debug/targets/RISC-V/spike64-2gdb.py b/debug/targets/RISC-V/spike64-2gdb.py deleted file mode 100644 index 200ffaa..0000000 --- a/debug/targets/RISC-V/spike64-2gdb.py +++ /dev/null @@ -1,11 +0,0 @@ -import targets -import testlib - -import spike64 - -class spike64_2gdb(targets.Target): - harts = [spike64.spike64_hart(), spike64.spike64_hart()] - openocd_config_path = "spike-2gdb.cfg" - - def create(self): - return testlib.Spike(self) -- cgit v1.1 From 5acbf6414ddaa6552ead5099868897a161bd945f Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 1 Sep 2017 12:03:40 -0700 Subject: Use 32-bit link script for 32-bit target. --- debug/targets/RISC-V/spike32.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug/targets/RISC-V/spike32.py b/debug/targets/RISC-V/spike32.py index fc7d598..665d7e9 100644 --- a/debug/targets/RISC-V/spike32.py +++ b/debug/targets/RISC-V/spike32.py @@ -7,7 +7,7 @@ class spike32_hart(targets.Hart): ram_size = 0x10000000 instruction_hardware_breakpoint_count = 4 reset_vector = 0x1000 - link_script_path = "spike64.lds" + link_script_path = "spike32.lds" class spike32(targets.Target): harts = [spike32_hart()] -- cgit v1.1 From a7238f6f683705a92a3216562d91cfc8979c75ed Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 1 Sep 2017 12:31:15 -0700 Subject: Add some infrastructure for multicore tests. When compiling, define the number of harts. This means we only need to allocate a lot of stack if there are a lot of harts. --- debug/gdbserver.py | 2 +- debug/programs/entry.S | 15 +++++++------ debug/programs/init.c | 23 ++++++++++++++++++++ debug/targets.py | 57 +++++++++++++++++++++++++------------------------- debug/testlib.py | 4 ++-- 5 files changed, 61 insertions(+), 40 deletions(-) diff --git a/debug/gdbserver.py b/debug/gdbserver.py index 78ac9aa..21eea4e 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -708,7 +708,7 @@ class DownloadTest(GdbTest): if self.crc < 0: self.crc += 2**32 - self.binary = self.hart.compile(self.download_c.name, + self.binary = self.target.compile(self.hart, self.download_c.name, "programs/checksum.c") self.gdb.command("file %s" % self.binary) diff --git a/debug/programs/entry.S b/debug/programs/entry.S index 866636b..a2ea955 100755 --- a/debug/programs/entry.S +++ b/debug/programs/entry.S @@ -1,9 +1,8 @@ -#ifndef ENTRY_S -#define ENTRY_S - #include "encoding.h" -#define STACK_SIZE 1024 +// Enough stack to store every register in case a trap handler is executed, +// plus 33 more values. +#define STACK_SIZE (64 * XLEN / 8) #if XLEN == 64 # define LREG ld @@ -62,10 +61,11 @@ handle_reset: .option pop # Initialize stack pointer. - # Support up to 4 harts, with IDs 0--3. + # Give each hart STACK_SIZE of stack. + # Assume hart IDs are contiguous and start at 0. csrr t0, CSR_MHARTID addi t0, t0, 1 - li t1, STACK_SIZE / 4 + li t1, STACK_SIZE mul t0, t0, t1 la sp, stack_bottom add sp, sp, t0 @@ -194,8 +194,7 @@ 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 + .fill NHARTS * STACK_SIZE/4, 4, 0x22446688 stack_top: initialized: .word 0 -#endif diff --git a/debug/programs/init.c b/debug/programs/init.c index a2b41b0..9933c23 100644 --- a/debug/programs/init.c +++ b/debug/programs/init.c @@ -1,7 +1,30 @@ +#include "init.h" +#include "encoding.h" + int main(void); +trap_handler_t trap_handler[NHARTS] = {0}; + +void set_trap_handler(trap_handler_t handler) +{ + unsigned hartid = csr_read(mhartid); + trap_handler[hartid] = handler; +} + +void enable_timer_interrupts() +{ + set_csr(mie, MIP_MTIP); + set_csr(mstatus, MSTATUS_MIE); +} + void handle_trap(unsigned int mcause, unsigned int mepc, unsigned int sp) { + unsigned hartid = csr_read(mhartid); + if (trap_handler[hartid]) { + trap_handler[hartid](hartid, mcause, mepc, sp); + return; + } + while (1) ; } diff --git a/debug/targets.py b/debug/targets.py index 8efa255..db8d917 100644 --- a/debug/targets.py +++ b/debug/targets.py @@ -34,35 +34,6 @@ class Hart(object): # Defaults to target- name = None - 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 Target.isolate: - self.temporary_binary = tempfile.NamedTemporaryFile( - prefix=binary_name + "_") - binary_name = self.temporary_binary.name - Target.temporary_files.append(self.temporary_binary) - march = "rv%dima" % self.xlen - for letter in "fdc": - if self.extensionSupported(letter): - march += letter - testlib.compile(sources + - ("programs/entry.S", "programs/init.c", - "-I", "../env", - "-march=%s" % march, - "-T", self.link_script_path, - "-nostartfiles", - "-mcmodel=medany", - "-DXLEN=%d" % self.xlen, - "-o", binary_name), - xlen=self.xlen) - return binary_name - def extensionSupported(self, letter): # target.misa is set by testlib.ExamineTarget if self.misa: @@ -106,6 +77,7 @@ class Target(object): self.directory = os.path.dirname(path) self.server_cmd = parsed.server_cmd self.sim_cmd = parsed.sim_cmd + self.temporary_binary = None Target.isolate = parsed.isolate if not self.name: self.name = type(self).__name__ @@ -133,6 +105,33 @@ class Target(object): return testlib.Openocd(server_cmd=self.server_cmd, config=self.openocd_config_path) + def compile(self, hart, *sources): + binary_name = "%s_%s-%d" % ( + self.name, + os.path.basename(os.path.splitext(sources[0])[0]), + hart.xlen) + if Target.isolate: + self.temporary_binary = tempfile.NamedTemporaryFile( + prefix=binary_name + "_") + binary_name = self.temporary_binary.name + Target.temporary_files.append(self.temporary_binary) + march = "rv%dima" % hart.xlen + for letter in "fdc": + if hart.extensionSupported(letter): + march += letter + testlib.compile(sources + + ("programs/entry.S", "programs/init.c", + "-DNHARTS=%d" % len(self.harts), + "-I", "../env", + "-march=%s" % march, + "-T", hart.link_script_path, + "-nostartfiles", + "-mcmodel=medany", + "-DXLEN=%d" % hart.xlen, + "-o", binary_name), + xlen=hart.xlen) + return binary_name + def add_target_options(parser): parser.add_argument("target", help=".py file that contains definition for " "the target to test with.") diff --git a/debug/testlib.py b/debug/testlib.py index 692afb6..8ac616e 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -102,7 +102,7 @@ class Spike(object): if with_jtag_gdb: cmd += ['--rbb-port', '0'] os.environ['REMOTE_BITBANG_HOST'] = 'localhost' - self.infinite_loop = harts[0].compile( + self.infinite_loop = target.compile(harts[0], "programs/checksum.c", "programs/tiny-malloc.c", "programs/infinite_loop.S", "-DDEFINE_MALLOC", "-DDEFINE_FREE") cmd.append(self.infinite_loop) @@ -567,7 +567,7 @@ class BaseTest(object): if compile_args not in BaseTest.compiled: # pylint: disable=star-args BaseTest.compiled[compile_args] = \ - self.hart.compile(*compile_args) + self.target.compile(self.hart, *compile_args) self.binary = BaseTest.compiled.get(compile_args) def classSetup(self): -- cgit v1.1