aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Newsome <tim@sifive.com>2017-09-12 09:36:34 -0700
committerGitHub <noreply@github.com>2017-09-12 09:36:34 -0700
commit7ab046f286a5356049221946dea36ee755a13346 (patch)
tree5b8a8f12035b2d29eb08284ab698595b6edce958
parentc2bcbb1a30a1513cc79b9899d41f46870f92a2f3 (diff)
parenta7238f6f683705a92a3216562d91cfc8979c75ed (diff)
downloadriscv-tests-7ab046f286a5356049221946dea36ee755a13346.zip
riscv-tests-7ab046f286a5356049221946dea36ee755a13346.tar.gz
riscv-tests-7ab046f286a5356049221946dea36ee755a13346.tar.bz2
Merge pull request #69 from riscv/multicore
Proper multicore support for debug tests
-rw-r--r--debug/Makefile4
-rwxr-xr-xdebug/gdbserver.py153
-rwxr-xr-xdebug/programs/entry.S53
-rw-r--r--debug/programs/init.c23
-rw-r--r--debug/programs/multicore.c81
-rw-r--r--debug/programs/start.S12
-rw-r--r--debug/targets.py113
-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.py12
-rwxr-xr-xdebug/targets/RISC-V/spike32.lds2
-rw-r--r--debug/targets/RISC-V/spike32.py8
-rw-r--r--debug/targets/RISC-V/spike64-2.py12
-rw-r--r--debug/targets/RISC-V/spike64.cfg19
-rwxr-xr-xdebug/targets/RISC-V/spike64.lds2
-rw-r--r--debug/targets/RISC-V/spike64.py8
-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/Freedom.lds2
-rw-r--r--debug/targets/SiFive/Freedom/U500.py7
-rw-r--r--debug/targets/SiFive/Freedom/U500Sim.py11
-rwxr-xr-xdebug/targets/SiFive/HiFive1.lds2
-rw-r--r--debug/targets/SiFive/HiFive1.py5
-rw-r--r--debug/testlib.py143
23 files changed, 486 insertions, 209 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..21eea4e 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, TestFailed
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,7 @@ class SimpleF18Test(SimpleRegisterTest):
assertLess(abs(float(self.gdb.p_raw("$%s" % name)) - b), .001)
def early_applicable(self):
- return self.target.extensionSupported('F')
+ return self.hart.extensionSupported('F')
def test(self):
self.check_reg("f18")
@@ -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)
@@ -157,7 +157,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,24 +168,25 @@ 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):
+ 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.target.ram))
- for offset in range(0, length, 19*4) + [length-4]:
- value = self.gdb.p("*((int*)0x%x)" % (self.target.ram + offset))
+ self.gdb.command("restore %s 0x%x" % (a.name, self.hart.ram))
+ 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) | \
(ord(data[offset+2]) << 16) | \
@@ -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.target.ram, self.target.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):
@@ -213,7 +217,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 +229,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 +329,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 +349,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")
@@ -415,21 +419,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):
- 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")
+ def test(self):
# Run to main
self.gdb.b("main")
self.gdb.c()
@@ -456,18 +458,47 @@ 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 MulticoreRunHaltStepiTest(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", )
@@ -479,7 +510,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 +589,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 +658,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 +682,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 +708,7 @@ class DownloadTest(GdbTest):
if self.crc < 0:
self.crc += 2**32
- self.binary = self.target.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)
@@ -708,7 +739,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..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 512
+// 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
@@ -61,8 +60,15 @@ handle_reset:
la gp, __global_pointer$
.option pop
- # initialize stack pointer
- la sp, stack_top
+ # Initialize stack pointer.
+ # 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
+ 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:
@@ -157,9 +188,13 @@ 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
+ .fill NHARTS * STACK_SIZE/4, 4, 0x22446688
stack_top:
-#endif
+initialized:
+ .word 0
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/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 <stdint.h>
+
+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);
+ }
+}
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..db8d917 100644
--- a/debug/targets.py
+++ b/debug/targets.py
@@ -5,51 +5,71 @@ import tempfile
import testlib
+class Hart(object):
+ # XLEN of the hart. May be overridden with --32 or --64 command line
+ # options.
+ xlen = 0
+
+ # Will be autodetected (by running ExamineTarget) if left unset. Set to
+ # save a little time.
+ misa = None
+
+ # 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. 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
+
+ # Address where a r/w/x block of RAM starts, together with its size.
+ ram = None
+ ram_size = None
+
+ # Number of instruction triggers the hart supports.
+ instruction_hardware_breakpoint_count = 0
+
+ # Defaults to target-<index>
+ name = None
+
+ def extensionSupported(self, letter):
+ # target.misa is set by testlib.ExamineTarget
+ if self.misa:
+ return self.misa & (1 << (ord(letter.upper()) - ord('A')))
+ 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
- # XLEN of the target. 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
+ # 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 = []
- # Implements dmode in tdata1 as described in the spec. Targets 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
def __init__(self, path, parsed):
# Path to module.
@@ -57,7 +77,8 @@ class Target(object):
self.directory = os.path.dirname(path)
self.server_cmd = parsed.server_cmd
self.sim_cmd = parsed.sim_cmd
- self.isolate = parsed.isolate
+ self.temporary_binary = None
+ Target.isolate = parsed.isolate
if not self.name:
self.name = type(self).__name__
# Default OpenOCD config file to <name>.cfg
@@ -65,11 +86,15 @@ class Target(object):
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)
+ 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."""
@@ -78,42 +103,35 @@ class Target(object):
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)
+ config=self.openocd_config_path)
- def compile(self, *sources):
+ def compile(self, hart, *sources):
binary_name = "%s_%s-%d" % (
self.name,
os.path.basename(os.path.splitext(sources[0])[0]),
- self.xlen)
- if self.isolate:
+ 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" % self.xlen
+ march = "rv%dima" % hart.xlen
for letter in "fdc":
- if self.extensionSupported(letter):
+ 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", self.link_script_path,
+ "-T", hart.link_script_path,
"-nostartfiles",
"-mcmodel=medany",
- "-DXLEN=%d" % self.xlen,
+ "-DXLEN=%d" % hart.xlen,
"-o", binary_name),
- xlen=self.xlen)
+ xlen=hart.xlen)
return binary_name
- def extensionSupported(self, letter):
- # target.misa is set by testlib.ExamineTarget
- if self.misa:
- return self.misa & (1 << (ord(letter.upper()) - ord('A')))
- else:
- return False
-
def add_target_options(parser):
parser.add_argument("target", help=".py file that contains definition for "
"the target to test with.")
@@ -149,4 +167,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..6cf558d
--- /dev/null
+++ b/debug/targets/RISC-V/spike32-2.py
@@ -0,0 +1,12 @@
+import targets
+import testlib
+
+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.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/spike32.py b/debug/targets/RISC-V/spike32.py
index 3bf8b47..665d7e9 100644
--- a/debug/targets/RISC-V/spike32.py
+++ b/debug/targets/RISC-V/spike32.py
@@ -1,12 +1,18 @@
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 = "spike32.lds"
+
+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
new file mode 100644
index 0000000..c6321dc
--- /dev/null
+++ b/debug/targets/RISC-V/spike64-2.py
@@ -0,0 +1,12 @@
+import targets
+import testlib
+
+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.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.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/RISC-V/spike64.py b/debug/targets/RISC-V/spike64.py
index c705857..6e3da89 100644
--- a/debug/targets/RISC-V/spike64.py
+++ b/debug/targets/RISC-V/spike64.py
@@ -1,12 +1,18 @@
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"
+ timeout_sec = 30
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/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/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..065ab08 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(targets.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.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/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..8ac616e 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
@@ -36,13 +39,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 +65,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 +102,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 = target.compile(harts[0],
"programs/checksum.c", "programs/tiny-malloc.c",
"programs/infinite_loop.S", "-DDEFINE_MALLOC", "-DDEFINE_FREE")
cmd.append(self.infinite_loop)
@@ -106,11 +126,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)
@@ -164,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:
@@ -200,7 +223,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:
@@ -215,19 +244,23 @@ 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:
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
except Exception:
header("OpenOCD log")
sys.stdout.write(log)
@@ -291,6 +324,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\)")
@@ -331,13 +368,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)
@@ -391,19 +438,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 +465,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 +539,13 @@ 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.hart = target.harts[-1] #<<<
self.server = None
self.target_process = None
self.binary = None
@@ -514,7 +567,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.target.compile(self.hart, *compile_args)
self.binary = BaseTest.compiled.get(compile_args)
def classSetup(self):
@@ -581,8 +634,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 +651,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 +668,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")