aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debug/Makefile4
-rwxr-xr-xdebug/gdbserver.py93
-rwxr-xr-xdebug/programs/entry.S7
-rw-r--r--debug/programs/start.S12
-rw-r--r--debug/targets.py144
-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.py11
-rw-r--r--debug/targets/RISC-V/spike32.py7
-rw-r--r--debug/targets/RISC-V/spike64-2.py11
-rw-r--r--debug/targets/RISC-V/spike64.cfg19
-rw-r--r--debug/targets/RISC-V/spike64.py7
-rw-r--r--debug/targets/SiFive/Freedom/E300.py9
-rw-r--r--debug/targets/SiFive/Freedom/E300Sim.py9
-rw-r--r--debug/targets/SiFive/Freedom/U500.py7
-rw-r--r--debug/targets/SiFive/Freedom/U500Sim.py11
-rw-r--r--debug/targets/SiFive/HiFive1.py5
-rw-r--r--debug/testlib.py95
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")