diff options
author | Tim Newsome <tim@sifive.com> | 2017-08-07 12:51:42 -0700 |
---|---|---|
committer | Tim Newsome <tim@sifive.com> | 2017-08-28 12:16:39 -0700 |
commit | 3a44725d27f6b2c77f0ca912d792b6856fde6a17 (patch) | |
tree | e89d52105aa01d59e7d4588ef157d477f0a4335d /debug | |
parent | ab6c2ccaec192684cf4649d5d69bd105d738d1c7 (diff) | |
download | riscv-tests-3a44725d27f6b2c77f0ca912d792b6856fde6a17.zip riscv-tests-3a44725d27f6b2c77f0ca912d792b6856fde6a17.tar.gz riscv-tests-3a44725d27f6b2c77f0ca912d792b6856fde6a17.tar.bz2 |
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.
Diffstat (limited to 'debug')
-rw-r--r-- | debug/Makefile | 4 | ||||
-rwxr-xr-x | debug/gdbserver.py | 93 | ||||
-rwxr-xr-x | debug/programs/entry.S | 7 | ||||
-rw-r--r-- | debug/programs/start.S | 12 | ||||
-rw-r--r-- | debug/targets.py | 144 | ||||
-rw-r--r-- | debug/targets/RISC-V/spike.cfg (renamed from debug/targets/RISC-V/spike32.cfg) | 5 | ||||
-rw-r--r-- | debug/targets/RISC-V/spike32-2.py | 11 | ||||
-rw-r--r-- | debug/targets/RISC-V/spike32.py | 7 | ||||
-rw-r--r-- | debug/targets/RISC-V/spike64-2.py | 11 | ||||
-rw-r--r-- | debug/targets/RISC-V/spike64.cfg | 19 | ||||
-rw-r--r-- | debug/targets/RISC-V/spike64.py | 7 | ||||
-rw-r--r-- | debug/targets/SiFive/Freedom/E300.py | 9 | ||||
-rw-r--r-- | debug/targets/SiFive/Freedom/E300Sim.py | 9 | ||||
-rw-r--r-- | debug/targets/SiFive/Freedom/U500.py | 7 | ||||
-rw-r--r-- | debug/targets/SiFive/Freedom/U500Sim.py | 11 | ||||
-rw-r--r-- | debug/targets/SiFive/HiFive1.py | 5 | ||||
-rw-r--r-- | debug/testlib.py | 95 |
17 files changed, 267 insertions, 189 deletions
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<<self.target.xlen) - b = random.randrange(1<<self.target.xlen) + a = random.randrange(1<<self.hart.xlen) + b = random.randrange(1<<self.hart.xlen) self.gdb.p("$%s=0x%x" % (name, a)) self.gdb.stepi() assertEqual(self.gdb.p("$%s" % name), a) @@ -77,12 +77,12 @@ class SimpleRegisterTest(GdbTest): def setup(self): # 0x13 is nop - self.gdb.command("p *((int*) 0x%x)=0x13" % self.target.ram) - self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 4)) - self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 8)) - self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 12)) - self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 16)) - self.gdb.p("$pc=0x%x" % self.target.ram) + self.gdb.command("p *((int*) 0x%x)=0x13" % self.hart.ram) + self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 4)) + self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 8)) + self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 12)) + self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 16)) + self.gdb.p("$pc=0x%x" % self.hart.ram) class SimpleS0Test(SimpleRegisterTest): def test(self): @@ -114,7 +114,8 @@ class SimpleF18Test(SimpleRegisterTest): assertLess(abs(float(self.gdb.p_raw("$%s" % name)) - b), .001) def early_applicable(self): - return self.target.extensionSupported('F') + print repr(self.hart) + return self.hart.extensionSupported('F') def test(self): self.check_reg("f18") @@ -157,7 +158,7 @@ class MemTest64(SimpleMemoryTest): # assert False, "Read should have failed." # except testlib.CannotAccess as e: # assertEqual(e.address, 0xdeadbeef) -# self.gdb.p("*((int*)0x%x)" % self.target.ram) +# self.gdb.p("*((int*)0x%x)" % self.hart.ram) # #class MemTestWriteInvalid(SimpleMemoryTest): # def test(self): @@ -168,7 +169,7 @@ class MemTest64(SimpleMemoryTest): # assert False, "Write should have failed." # except testlib.CannotAccess as e: # assertEqual(e.address, 0xdeadbeef) -# self.gdb.p("*((int*)0x%x)=6874742" % self.target.ram) +# self.gdb.p("*((int*)0x%x)=6874742" % self.hart.ram) class MemTestBlock(GdbTest): def test(self): @@ -183,9 +184,9 @@ class MemTestBlock(GdbTest): a.write(ihex_line(i * line_length, 0, line_data)) a.flush() - self.gdb.command("restore %s 0x%x" % (a.name, self.target.ram)) + self.gdb.command("restore %s 0x%x" % (a.name, self.hart.ram)) for offset in range(0, length, 19*4) + [length-4]: - value = self.gdb.p("*((int*)0x%x)" % (self.target.ram + offset)) + value = self.gdb.p("*((int*)0x%x)" % (self.hart.ram + offset)) written = ord(data[offset]) | \ (ord(data[offset+1]) << 8) | \ (ord(data[offset+2]) << 16) | \ @@ -194,7 +195,7 @@ class MemTestBlock(GdbTest): b = tempfile.NamedTemporaryFile(suffix=".ihex") self.gdb.command("dump ihex memory %s 0x%x 0x%x" % (b.name, - self.target.ram, self.target.ram + length)) + self.hart.ram, self.hart.ram + length)) for line in b: record_type, address, line_data = ihex_parse(line) if record_type == 0: @@ -213,7 +214,7 @@ class InstantHaltTest(GdbTest): self.gdb.thread(t) pcs.append(self.gdb.p("$pc")) for pc in pcs: - assertEqual(self.target.reset_vector, pc) + assertEqual(self.hart.reset_vector, pc) # mcycle and minstret have no defined reset value. mstatus = self.gdb.p("$mstatus") assertEqual(mstatus & (MSTATUS_MIE | MSTATUS_MPRV | @@ -225,16 +226,16 @@ class InstantChangePc(GdbTest): # 0x13 is nop self.gdb.command("monitor reset halt") self.gdb.command("flushregs") - self.gdb.command("p *((int*) 0x%x)=0x13" % self.target.ram) - self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 4)) - self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 8)) - self.gdb.p("$pc=0x%x" % self.target.ram) + self.gdb.command("p *((int*) 0x%x)=0x13" % self.hart.ram) + self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 4)) + self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 8)) + self.gdb.p("$pc=0x%x" % self.hart.ram) self.gdb.stepi() - assertEqual((self.target.ram + 4), self.gdb.p("$pc")) + assertEqual((self.hart.ram + 4), self.gdb.p("$pc")) self.gdb.stepi() - assertEqual((self.target.ram + 8), self.gdb.p("$pc")) + assertEqual((self.hart.ram + 8), self.gdb.p("$pc")) -class DebugTest(GdbTest): +class DebugTest(GdbSingleHartTest): # Include malloc so that gdb can make function calls. I suspect this malloc # will silently blow through the memory set aside for it, so be careful. compile_args = ("programs/debug.c", "programs/checksum.c", @@ -325,10 +326,10 @@ class DebugBreakpoint(DebugTest): class Hwbp1(DebugTest): def test(self): - if self.target.instruction_hardware_breakpoint_count < 1: + if self.hart.instruction_hardware_breakpoint_count < 1: return 'not_applicable' - if not self.target.honors_tdata1_hmode: + if not self.hart.honors_tdata1_hmode: # Run to main before setting the breakpoint, because startup code # will otherwise clear the trigger that we set. self.gdb.b("main") @@ -345,7 +346,7 @@ class Hwbp1(DebugTest): class Hwbp2(DebugTest): def test(self): - if self.target.instruction_hardware_breakpoint_count < 2: + if self.hart.instruction_hardware_breakpoint_count < 2: return 'not_applicable' self.gdb.hbreak("main") @@ -418,16 +419,15 @@ class UserInterrupt(DebugTest): class MulticoreTest(GdbTest): compile_args = ("programs/infinite_loop.S", ) + def early_applicable(self): + return len(self.target.harts) > 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)+17) & ((1<<self.target.xlen)-1)) + ((0xdeadbeef<<n)+17) & ((1<<self.hart.xlen)-1)) class WriteCsrs(RegsTest): def test(self): @@ -651,7 +652,7 @@ class WriteCsrs(RegsTest): class DownloadTest(GdbTest): def setup(self): # pylint: disable=attribute-defined-outside-init - length = min(2**10, self.target.ram_size - 2048) + length = min(2**10, self.hart.ram_size - 2048) self.download_c = tempfile.NamedTemporaryFile(prefix="download_", suffix=".c", delete=False) self.download_c.write("#include <stdint.h>\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 <name>.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 <name>.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 <name>.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 <name>.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 <name>.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-<index> + 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 <name>.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 <name>.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 <name>.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 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") |