From e0b295faa65c8e6bae880fc17a53d481ef74402f Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Thu, 14 Sep 2017 11:11:44 -0700 Subject: Move link options to end of gcc command line --- benchmarks/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/Makefile b/benchmarks/Makefile index d1c2362..6d87063 100644 --- a/benchmarks/Makefile +++ b/benchmarks/Makefile @@ -48,7 +48,7 @@ objs := define compile_template $(1).riscv: $(wildcard $(src_dir)/$(1)/*) $(wildcard $(src_dir)/common/*) - $$(RISCV_GCC) $$(incs) $$(RISCV_GCC_OPTS) $$(RISCV_LINK_OPTS) -o $$@ $(wildcard $(src_dir)/$(1)/*.c) $(wildcard $(src_dir)/common/*.c) $(wildcard $(src_dir)/common/*.S) + $$(RISCV_GCC) $$(incs) $$(RISCV_GCC_OPTS) -o $$@ $(wildcard $(src_dir)/$(1)/*.c) $(wildcard $(src_dir)/common/*.c) $(wildcard $(src_dir)/common/*.S) $$(RISCV_LINK_OPTS) endef $(foreach bmark,$(bmarks),$(eval $(call compile_template,$(bmark)))) -- cgit v1.1 From b6bc6a7c84188d2be78c69a345c884f76e7b4c38 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Tue, 12 Sep 2017 11:20:27 -0700 Subject: Clarify timeout units. --- debug/testlib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/debug/testlib.py b/debug/testlib.py index 8ac616e..c41c332 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -333,6 +333,7 @@ class Gdb(object): self.child.expect(r"\(gdb\)") def command(self, command, timeout=6000): + """timeout is in seconds""" self.child.sendline(command) self.child.expect("\n", timeout=timeout) self.child.expect(r"\(gdb\)", timeout=timeout) -- cgit v1.1 From 706b6476a2eb320a84fef39716a7c19a83b68a39 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Tue, 12 Sep 2017 11:21:06 -0700 Subject: Call postMortem() when a test fails. --- debug/testlib.py | 10 ++++++++++ env | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/debug/testlib.py b/debug/testlib.py index c41c332..bb81cfb 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -588,6 +588,9 @@ class BaseTest(object): del self.server del self.target_process + def postMortem(self): + pass + def run(self): """ If compile_args is set, compile a program and set self.binary. @@ -621,6 +624,7 @@ class BaseTest(object): print e.message header("Traceback") traceback.print_exc(file=sys.stdout) + self.postMortem() return result finally: @@ -665,6 +669,12 @@ class GdbTest(BaseTest): # FIXME: OpenOCD doesn't handle PRIV now #self.gdb.p("$priv=3") + def postMortem(self): + if not self.gdb: + return + self.gdb.interrupt() + self.gdb.command("info registers all", timeout=10) + def classTeardown(self): del self.gdb BaseTest.classTeardown(self) diff --git a/env b/env index 1b76fd1..db0bfa2 160000 --- a/env +++ b/env @@ -1 +1 @@ -Subproject commit 1b76fd1f1c63dfe4cea93f426434a7384d2bf801 +Subproject commit db0bfa223142e56b17dae6d92610f195014bbb80 -- cgit v1.1 From 6c2ad1c5c27f5e19e005541f7665a32814d32e0f Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Tue, 12 Sep 2017 18:48:44 -0700 Subject: Test debugging code with interrupts. --- debug/gdbserver.py | 43 +++++++++++++++++++++++++++++++++++++++++++ debug/programs/entry.S | 4 +--- debug/programs/init.c | 2 +- debug/programs/interrupt.c | 32 ++++++++++++++++++++++++++++++++ debug/targets.py | 3 +++ 5 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 debug/programs/interrupt.c diff --git a/debug/gdbserver.py b/debug/gdbserver.py index 21eea4e..9fedbca 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -419,6 +419,49 @@ class UserInterrupt(DebugTest): self.gdb.p("i=0") self.exit() +class InterruptTest(GdbSingleHartTest): + compile_args = ("programs/interrupt.c",) + + def early_applicable(self): + return self.target.supports_clint_mtime + + def setup(self): + self.gdb.load() + + def test(self): + self.gdb.b("main") + output = self.gdb.c() + assertIn(" main ", output) + self.gdb.b("trap_entry") + output = self.gdb.c() + assertIn(" trap_entry ", output) + assertEqual(self.gdb.p("$mip") & 0x80, 0x80) + assertEqual(self.gdb.p("interrupt_count"), 0) + # You'd expect local to still be 0, but it looks like spike doesn't + # jump to the interrupt handler immediately after the write to + # mtimecmp. + assertLess(self.gdb.p("local"), 1000) + self.gdb.command("delete breakpoints") + for _ in range(10): + self.gdb.c(wait=False) + time.sleep(2) + self.gdb.interrupt() + interrupt_count = self.gdb.p("interrupt_count") + local = self.gdb.p("local") + if interrupt_count > 1000 and \ + local > 1000: + return + + assertGreater(interrupt_count, 1000) + assertGreater(local, 1000) + + def postMortem(self): + GdbSingleHartTest.postMortem(self) + self.gdb.p("*((long long*) 0x200bff8)") + self.gdb.p("*((long long*) 0x2004000)") + self.gdb.p("interrupt_count") + self.gdb.p("local") + class MulticoreRegTest(GdbTest): compile_args = ("programs/infinite_loop.S", "-DMULTICORE") diff --git a/debug/programs/entry.S b/debug/programs/entry.S index a2ea955..97b62a3 100755 --- a/debug/programs/entry.S +++ b/debug/programs/entry.S @@ -1,8 +1,6 @@ #include "encoding.h" -// Enough stack to store every register in case a trap handler is executed, -// plus 33 more values. -#define STACK_SIZE (64 * XLEN / 8) +#define STACK_SIZE (74 * XLEN / 8) #if XLEN == 64 # define LREG ld diff --git a/debug/programs/init.c b/debug/programs/init.c index 9933c23..8b047de 100644 --- a/debug/programs/init.c +++ b/debug/programs/init.c @@ -17,7 +17,7 @@ void enable_timer_interrupts() set_csr(mstatus, MSTATUS_MIE); } -void handle_trap(unsigned int mcause, unsigned int mepc, unsigned int sp) +void handle_trap(unsigned int mcause, void *mepc, void *sp) { unsigned hartid = csr_read(mhartid); if (trap_handler[hartid]) { diff --git a/debug/programs/interrupt.c b/debug/programs/interrupt.c new file mode 100644 index 0000000..c2dd5ec --- /dev/null +++ b/debug/programs/interrupt.c @@ -0,0 +1,32 @@ +#include "init.h" +#include "encoding.h" + +static volatile unsigned interrupt_count; +static volatile unsigned local; + +static unsigned delta = 0x100; +void *increment_count(unsigned hartid, unsigned mcause, void *mepc, void *sp) +{ + interrupt_count++; + // There is no guarantee that the interrupt is cleared immediately when + // MTIMECMP is written, so stick around here until that happens. + while (csr_read(mip) & MIP_MTIP) { + MTIMECMP[hartid] = MTIME + delta; + } + return mepc; +} + +int main() +{ + interrupt_count = 0; + local = 0; + unsigned hartid = csr_read(mhartid); + + set_trap_handler(increment_count); + MTIMECMP[hartid] = MTIME - 1; + enable_timer_interrupts(); + + while (1) { + local++; + } +} diff --git a/debug/targets.py b/debug/targets.py index db8d917..d661d14 100644 --- a/debug/targets.py +++ b/debug/targets.py @@ -67,6 +67,9 @@ class Target(object): # before starting the test. gdb_setup = [] + # Supports mtime at 0x2004000 + supports_clint_mtime = True + # Internal variables: directory = None temporary_files = [] -- cgit v1.1 From 28265b69cf55ebc783189a1371bcb49e4d9927e0 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Thu, 14 Sep 2017 12:59:08 -0700 Subject: When spike fails to launch, display its output. --- debug/testlib.py | 50 +++++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/debug/testlib.py b/debug/testlib.py index bb81cfb..f4b85c6 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -72,6 +72,34 @@ class Spike(object): else: harts = [target] + cmd = self.command(target, harts, halted, timeout, with_jtag_gdb) + 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) + logfile = open(self.logname, "w") + logfile.write("+ %s\n" % " ".join(cmd)) + logfile.flush() + self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, + stdout=logfile, stderr=logfile) + + if with_jtag_gdb: + self.port = None + for _ in range(30): + m = re.search(r"Listening for remote bitbang connection on " + r"port (\d+).", open(self.logname).read()) + if m: + self.port = int(m.group(1)) + os.environ['REMOTE_BITBANG_PORT'] = m.group(1) + break + time.sleep(0.11) + if not self.port: + print_log(self.logname) + raise Exception("Didn't get spike message about bitbang " + "connection") + + def command(self, target, harts, halted, timeout, with_jtag_gdb): + # pylint: disable=no-self-use if target.sim_cmd: cmd = shlex.split(target.sim_cmd) else: @@ -102,28 +130,8 @@ class Spike(object): if with_jtag_gdb: cmd += ['--rbb-port', '0'] os.environ['REMOTE_BITBANG_HOST'] = 'localhost' - 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) - logfile = open(self.logname, "w") - logfile.write("+ %s\n" % " ".join(cmd)) - logfile.flush() - self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, - stdout=logfile, stderr=logfile) - if with_jtag_gdb: - self.port = None - for _ in range(30): - m = re.search(r"Listening for remote bitbang connection on " - r"port (\d+).", open(self.logname).read()) - if m: - self.port = int(m.group(1)) - os.environ['REMOTE_BITBANG_PORT'] = m.group(1) - break - time.sleep(0.11) - assert self.port, "Didn't get spike message about bitbang " \ - "connection" + return cmd def __del__(self): if self.process: -- cgit v1.1 From 910f39c9b46a883d58f0b77ae847c535834a48da Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Thu, 14 Sep 2017 16:26:51 -0700 Subject: misa is stored in the hart now, not the target --- debug/testlib.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/debug/testlib.py b/debug/testlib.py index f4b85c6..3eeace9 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -700,21 +700,21 @@ class GdbSingleHartTest(GdbTest): class ExamineTarget(GdbTest): def test(self): - self.target.misa = self.gdb.p("$misa") + self.hart.misa = self.gdb.p("$misa") txt = "RV" - if (self.target.misa >> 30) == 1: + if (self.hart.misa >> 30) == 1: txt += "32" - elif (self.target.misa >> 62) == 2: + elif (self.hart.misa >> 62) == 2: txt += "64" - elif (self.target.misa >> 126) == 3: + elif (self.hart.misa >> 126) == 3: txt += "128" else: raise TestFailed("Couldn't determine XLEN from $misa (0x%x)" % - self.target.misa) + self.hart.misa) for i in range(26): - if self.target.misa & (1< Date: Fri, 15 Sep 2017 12:03:52 -0700 Subject: Don't read entire log into RAM just to print it. --- debug/testlib.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/debug/testlib.py b/debug/testlib.py index 3eeace9..996c188 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -540,8 +540,7 @@ def header(title, dash='-', length=78): def print_log(path): header(path) - lines = open(path, "r").readlines() - for l in lines: + for l in open(path, "r"): sys.stdout.write(l) print -- cgit v1.1 From a0259333dcd4bbba2dbfaeb7bfb9145c56cf96ca Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Mon, 18 Sep 2017 11:31:08 -0700 Subject: Add interrupts to MulticoreRunHaltStepiTest. Just to hammer on anything at once, and hopefully catch weird interactions if they exist. --- debug/gdbserver.py | 9 ++++++++- debug/programs/entry.S | 2 +- debug/programs/multicore.c | 31 +++++++++++++++++++------------ debug/testlib.py | 3 +-- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/debug/gdbserver.py b/debug/gdbserver.py index 9fedbca..d0ad46e 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -529,14 +529,21 @@ class MulticoreRunHaltStepiTest(GdbTest): def test(self): previous_hart_count = [0 for h in self.target.harts] + previous_interrupt_count = [0 for h in self.target.harts] for _ in range(10): self.gdb.c(wait=False) - time.sleep(1) + time.sleep(2) self.gdb.interrupt() + self.gdb.p("$mie") + self.gdb.p("$mip") + self.gdb.p("$mstatus") + self.gdb.p("$priv") self.gdb.p("buf", fmt="") hart_count = self.gdb.p("hart_count") + interrupt_count = self.gdb.p("interrupt_count") for i, h in enumerate(self.target.harts): assertGreater(hart_count[i], previous_hart_count[i]) + assertGreater(interrupt_count[i], previous_interrupt_count[i]) self.gdb.select_hart(h) pc = self.gdb.p("$pc") self.gdb.stepi() diff --git a/debug/programs/entry.S b/debug/programs/entry.S index 97b62a3..35c233e 100755 --- a/debug/programs/entry.S +++ b/debug/programs/entry.S @@ -1,6 +1,6 @@ #include "encoding.h" -#define STACK_SIZE (74 * XLEN / 8) +#define STACK_SIZE (90 * XLEN / 8) #if XLEN == 64 # define LREG ld diff --git a/debug/programs/multicore.c b/debug/programs/multicore.c index d7dd845..272baea 100644 --- a/debug/programs/multicore.c +++ b/debug/programs/multicore.c @@ -1,5 +1,7 @@ #include +#include "init.h" + typedef struct { int counter; } atomic_t; @@ -15,14 +17,6 @@ static inline int atomic_xchg(atomic_t *v, int 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"); @@ -44,19 +38,31 @@ void put_lock(atomic_t *lock) static atomic_t buf_lock = { .counter = 0 }; static char buf[32]; static int buf_initialized; -static unsigned hart_count[2]; +static unsigned hart_count[NHARTS]; +static unsigned interrupt_count[NHARTS]; -static const char case_bit = 'a' - 'A'; -volatile int initialized; +static unsigned delta = 0x100; +void *increment_count(unsigned hartid, unsigned mcause, void *mepc, void *sp) +{ + interrupt_count[hartid]++; + MTIMECMP[hartid] = MTIME + delta; + return mepc; +} int main() { uint32_t hartid = csr_read(mhartid); hart_count[hartid] = 0; + interrupt_count[hartid] = 0; + + set_trap_handler(increment_count); + // Despite being memory-mapped, there appears to be one mtimecmp + // register per hart. The spec does not address this. + MTIMECMP[hartid] = MTIME + delta; + enable_timer_interrupts(); while (1) { get_lock(&buf_lock); - hart_count[hartid]++; if (!buf_initialized) { for (unsigned i = 0; i < sizeof(buf); i++) { @@ -77,5 +83,6 @@ int main() buf[i] = 'a' + ((i + hartid + hart_count[hartid]) % 26); } put_lock(&buf_lock); + hart_count[hartid]++; } } diff --git a/debug/testlib.py b/debug/testlib.py index 3eeace9..996c188 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -540,8 +540,7 @@ def header(title, dash='-', length=78): def print_log(path): header(path) - lines = open(path, "r").readlines() - for l in lines: + for l in open(path, "r"): sys.stdout.write(l) print -- cgit v1.1 From e087d97b130ec0a04c8d3498a66503c1746dadd3 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Tue, 19 Sep 2017 11:23:35 -0700 Subject: Forgot to commit this earlier. Fixes #77. --- debug/programs/init.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 debug/programs/init.h diff --git a/debug/programs/init.h b/debug/programs/init.h new file mode 100644 index 0000000..9aaa398 --- /dev/null +++ b/debug/programs/init.h @@ -0,0 +1,20 @@ +#ifndef INIT_H +#define INIT_H + +#define MTIME (*(volatile long long *)(0x02000000 + 0xbff8)) +#define MTIMECMP ((volatile long long *)(0x02000000 + 0x4000)) + +#define csr_read(csr) \ +({ \ + register unsigned long __v; \ + __asm__ __volatile__ ("csrr %0, " #csr \ + : "=r" (__v)); \ + __v; \ +}) + +typedef void* (*trap_handler_t)(unsigned hartid, unsigned mcause, void *mepc, + void *sp); +void set_trap_handler(trap_handler_t handler); +void enable_timer_interrupts(); + +#endif -- cgit v1.1 From 51b823c7ed8391756a83cab854cd4c646dd13acf Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Tue, 19 Sep 2017 14:34:42 -0700 Subject: Link against libm for fma() --- benchmarks/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/Makefile b/benchmarks/Makefile index 6d87063..fb0d297 100644 --- a/benchmarks/Makefile +++ b/benchmarks/Makefile @@ -39,7 +39,7 @@ RISCV_PREFIX ?= riscv$(XLEN)-unknown-elf- RISCV_GCC ?= $(RISCV_PREFIX)gcc RISCV_GCC_OPTS ?= -DPREALLOCATE=1 -mcmodel=medany -static -std=gnu99 -O2 -ffast-math -fno-common -fno-builtin-printf RISCV_LINK ?= $(RISCV_GCC) -T $(src_dir)/common/test.ld $(incs) -RISCV_LINK_OPTS ?= -static -nostdlib -nostartfiles -lgcc -T $(src_dir)/common/test.ld +RISCV_LINK_OPTS ?= -static -nostdlib -nostartfiles -lm -lgcc -T $(src_dir)/common/test.ld RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump --disassemble-all --disassemble-zeroes --section=.text --section=.text.startup --section=.data RISCV_SIM ?= spike --isa=rv$(XLEN)gc -- cgit v1.1 From fcd0e956339560021e2a16a143697b8123f227d6 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Tue, 19 Sep 2017 17:10:36 -0700 Subject: Allow multiple reset vectors. Some boards have jumpers that control the reset vector, and forcing them one way or another is more annoying than dealing with it in software. --- debug/gdbserver.py | 2 +- debug/targets.py | 5 +++++ debug/targets/RISC-V/spike32.py | 2 +- debug/targets/RISC-V/spike64.py | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/debug/gdbserver.py b/debug/gdbserver.py index d0ad46e..135dab8 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -217,7 +217,7 @@ class InstantHaltTest(GdbTest): self.gdb.thread(t) pcs.append(self.gdb.p("$pc")) for pc in pcs: - assertEqual(self.hart.reset_vector, pc) + assertIn(pc, self.hart.reset_vectors) # mcycle and minstret have no defined reset value. mstatus = self.gdb.p("$mstatus") assertEqual(mstatus & (MSTATUS_MIE | MSTATUS_MPRV | diff --git a/debug/targets.py b/debug/targets.py index d661d14..d09b576 100644 --- a/debug/targets.py +++ b/debug/targets.py @@ -34,6 +34,11 @@ class Hart(object): # Defaults to target- name = None + # When reset, the PC must be at one of the values listed here. + # This is a list because on some boards the reset vector depends on + # jumpers. + reset_vectors = [] + def extensionSupported(self, letter): # target.misa is set by testlib.ExamineTarget if self.misa: diff --git a/debug/targets/RISC-V/spike32.py b/debug/targets/RISC-V/spike32.py index 665d7e9..bcb5892 100644 --- a/debug/targets/RISC-V/spike32.py +++ b/debug/targets/RISC-V/spike32.py @@ -6,7 +6,7 @@ class spike32_hart(targets.Hart): ram = 0x10000000 ram_size = 0x10000000 instruction_hardware_breakpoint_count = 4 - reset_vector = 0x1000 + reset_vectors = [0x1000] link_script_path = "spike32.lds" class spike32(targets.Target): diff --git a/debug/targets/RISC-V/spike64.py b/debug/targets/RISC-V/spike64.py index 6e3da89..9c37f87 100644 --- a/debug/targets/RISC-V/spike64.py +++ b/debug/targets/RISC-V/spike64.py @@ -6,7 +6,7 @@ class spike64_hart(targets.Hart): ram = 0x1212340000 ram_size = 0x10000000 instruction_hardware_breakpoint_count = 4 - reset_vector = 0x1000 + reset_vectors = [0x1000] link_script_path = "spike64.lds" class spike64(targets.Target): -- cgit v1.1 From 35c41f1391b51d4d9c4e0ab40fdfc45dbea346b2 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Thu, 21 Sep 2017 15:19:47 -0700 Subject: Add coverage for single-core non-rtos OpenOCD. --- debug/targets/RISC-V/spike-rtos.cfg | 16 ++++++++++++++++ debug/targets/RISC-V/spike.cfg | 2 +- debug/targets/RISC-V/spike32-2.py | 2 +- debug/targets/RISC-V/spike64-2.py | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 debug/targets/RISC-V/spike-rtos.cfg diff --git a/debug/targets/RISC-V/spike-rtos.cfg b/debug/targets/RISC-V/spike-rtos.cfg new file mode 100644 index 0000000..9b1841c --- /dev/null +++ b/debug/targets/RISC-V/spike-rtos.cfg @@ -0,0 +1,16 @@ +adapter_khz 10000 + +interface remote_bitbang +remote_bitbang_host $::env(REMOTE_BITBANG_HOST) +remote_bitbang_port $::env(REMOTE_BITBANG_PORT) + +set _CHIPNAME riscv +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913 + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME riscv -chain-position $_TARGETNAME -rtos riscv + +gdb_report_data_abort enable + +init +reset halt diff --git a/debug/targets/RISC-V/spike.cfg b/debug/targets/RISC-V/spike.cfg index 9b1841c..fc20b53 100644 --- a/debug/targets/RISC-V/spike.cfg +++ b/debug/targets/RISC-V/spike.cfg @@ -8,7 +8,7 @@ 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 diff --git a/debug/targets/RISC-V/spike32-2.py b/debug/targets/RISC-V/spike32-2.py index 6cf558d..a7b9a1c 100644 --- a/debug/targets/RISC-V/spike32-2.py +++ b/debug/targets/RISC-V/spike32-2.py @@ -5,7 +5,7 @@ import spike32 # pylint: disable=import-error class spike32_2(targets.Target): harts = [spike32.spike32_hart(), spike32.spike32_hart()] - openocd_config_path = "spike.cfg" + openocd_config_path = "spike-rtos.cfg" timeout_sec = 30 def create(self): diff --git a/debug/targets/RISC-V/spike64-2.py b/debug/targets/RISC-V/spike64-2.py index c6321dc..4f6f1ff 100644 --- a/debug/targets/RISC-V/spike64-2.py +++ b/debug/targets/RISC-V/spike64-2.py @@ -5,7 +5,7 @@ import spike64 # pylint: disable=import-error class spike64_2(targets.Target): harts = [spike64.spike64_hart(), spike64.spike64_hart()] - openocd_config_path = "spike.cfg" + openocd_config_path = "spike-rtos.cfg" timeout_sec = 30 def create(self): -- cgit v1.1 From b9957ef9690dc83c684e113294b068fe676b468a Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 22 Sep 2017 12:57:51 -0700 Subject: Remove unused function. --- debug/testlib.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/debug/testlib.py b/debug/testlib.py index 996c188..0bdb433 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -50,15 +50,6 @@ def compile(args, xlen=32): # pylint: disable=redefined-builtin header("") raise Exception("Compile failed!") -def unused_port(): - # http://stackoverflow.com/questions/2838244/get-open-tcp-port-in-python/2838309#2838309 - import socket - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.bind(("", 0)) - port = s.getsockname()[1] - s.close() - return port - class Spike(object): logname = "spike-%d.log" % os.getpid() -- cgit v1.1 From 49fc83aa23045abee5d396ef5a9d96b80c03178d Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 29 Sep 2017 13:20:30 -0700 Subject: Fix tests to work in multi-gdb mode. The Gdb class now can handle connecting to more than one gdb. It enumerates the harts across all connections, and when asked to select a hart, it transparently sends future gdb commands to the correct instance. Multicore tests still have to be aware of some differences. The main one is that when executing 'c' in RTOS mode, all harts resume, while in multi-gdb mode only the current one resumes. Additionally, gdb doesn't set breakpoints until 'c' is issued, so the hart where breakpoints are set needs to be resumed before other harts might see them. --- debug/Makefile | 2 +- debug/gdbserver.py | 29 ++-- debug/targets.py | 2 + debug/targets/RISC-V/spike-1.cfg | 16 +++ debug/targets/RISC-V/spike-2.cfg | 19 +++ debug/targets/RISC-V/spike-rtos.cfg | 1 + debug/targets/RISC-V/spike.cfg | 16 --- debug/targets/RISC-V/spike32-2-rtos.py | 12 ++ debug/targets/RISC-V/spike32-2.py | 2 +- debug/targets/RISC-V/spike32.py | 2 +- debug/targets/RISC-V/spike64-2-rtos.py | 12 ++ debug/targets/RISC-V/spike64-2.py | 2 +- debug/targets/RISC-V/spike64.py | 2 +- debug/testlib.py | 238 +++++++++++++++++++++++---------- 14 files changed, 252 insertions(+), 103 deletions(-) create mode 100644 debug/targets/RISC-V/spike-1.cfg create mode 100644 debug/targets/RISC-V/spike-2.cfg delete mode 100644 debug/targets/RISC-V/spike.cfg create mode 100644 debug/targets/RISC-V/spike32-2-rtos.py create mode 100644 debug/targets/RISC-V/spike64-2-rtos.py diff --git a/debug/Makefile b/debug/Makefile index 33988dd..9f7cb2e 100644 --- a/debug/Makefile +++ b/debug/Makefile @@ -6,7 +6,7 @@ GDBSERVER_PY = $(src_dir)/gdbserver.py default: spike$(XLEN)-2 -all: pylint spike32 spike64 spike32-2 spike64-2 +all: pylint spike32 spike32-2 spike32-2-rtos spike64 spike64-2 spike64-2-rtos pylint: pylint --rcfile=pylint.rc `git ls-files '*.py'` diff --git a/debug/gdbserver.py b/debug/gdbserver.py index 135dab8..924f42a 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -363,7 +363,7 @@ class Hwbp2(DebugTest): self.exit() class TooManyHwbp(DebugTest): - def run(self): + def test(self): for i in range(30): self.gdb.hbreak("*rot13 + %d" % (i * 4)) @@ -476,21 +476,27 @@ class MulticoreRegTest(GdbTest): def test(self): # Run to main + # Hart 0 is the first to be resumed, so we have to set the breakpoint + # there. gdb won't actually set the breakpoint until we tell it to + # resume. + self.gdb.select_hart(self.target.harts[0]) self.gdb.b("main") - self.gdb.c() - for t in self.gdb.threads(): - assertIn("main", t.frame) + self.gdb.c_all() + for hart in self.target.harts: + self.gdb.select_hart(hart) + assertIn("main", self.gdb.where()) + self.gdb.select_hart(self.target.harts[0]) self.gdb.command("delete breakpoints") # Run through the entire loop. self.gdb.b("main_end") - self.gdb.c() + self.gdb.c_all() hart_ids = [] - for t in self.gdb.threads(): - assertIn("main_end", t.frame) + for hart in self.target.harts: + self.gdb.select_hart(hart) + assertIn("main_end", self.gdb.where()) # Check register values. - self.gdb.thread(t) hart_id = self.gdb.p("$x1") assertNotIn(hart_id, hart_ids) hart_ids.append(hart_id) @@ -505,12 +511,11 @@ class MulticoreRegTest(GdbTest): 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) + self.gdb.c_all() for hart in self.target.harts: - # Check register values. self.gdb.select_hart(hart) + assertIn("main", self.gdb.where()) + # Check register values. for n in range(1, 32): value = self.gdb.p("$x%d" % n) assertEqual(value, hart.index * 0x800 + n - 1) diff --git a/debug/targets.py b/debug/targets.py index d09b576..624eb71 100644 --- a/debug/targets.py +++ b/debug/targets.py @@ -96,6 +96,8 @@ class Target(object): self.openocd_config_path) for i, hart in enumerate(self.harts): hart.index = i + if not hasattr(hart, 'id'): + hart.id = i if not hart.name: hart.name = "%s-%d" % (self.name, i) # Default link script to .lds diff --git a/debug/targets/RISC-V/spike-1.cfg b/debug/targets/RISC-V/spike-1.cfg new file mode 100644 index 0000000..fc20b53 --- /dev/null +++ b/debug/targets/RISC-V/spike-1.cfg @@ -0,0 +1,16 @@ +adapter_khz 10000 + +interface remote_bitbang +remote_bitbang_host $::env(REMOTE_BITBANG_HOST) +remote_bitbang_port $::env(REMOTE_BITBANG_PORT) + +set _CHIPNAME riscv +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913 + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME riscv -chain-position $_TARGETNAME + +gdb_report_data_abort enable + +init +reset halt diff --git a/debug/targets/RISC-V/spike-2.cfg b/debug/targets/RISC-V/spike-2.cfg new file mode 100644 index 0000000..17526ec --- /dev/null +++ b/debug/targets/RISC-V/spike-2.cfg @@ -0,0 +1,19 @@ +# Connect to a mult-icore RISC-V target, exposing each hart as a thread. +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_0 $_CHIPNAME.cpu0 +set _TARGETNAME_1 $_CHIPNAME.cpu1 +target create $_TARGETNAME_0 riscv -chain-position $_CHIPNAME.cpu -coreid 0 +target create $_TARGETNAME_1 riscv -chain-position $_CHIPNAME.cpu -coreid 1 + +gdb_report_data_abort enable + +init +reset halt diff --git a/debug/targets/RISC-V/spike-rtos.cfg b/debug/targets/RISC-V/spike-rtos.cfg index 9b1841c..799e3cb 100644 --- a/debug/targets/RISC-V/spike-rtos.cfg +++ b/debug/targets/RISC-V/spike-rtos.cfg @@ -1,3 +1,4 @@ +# Connect to a mult-icore RISC-V target, exposing each hart as a thread. adapter_khz 10000 interface remote_bitbang diff --git a/debug/targets/RISC-V/spike.cfg b/debug/targets/RISC-V/spike.cfg deleted file mode 100644 index fc20b53..0000000 --- a/debug/targets/RISC-V/spike.cfg +++ /dev/null @@ -1,16 +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 - -gdb_report_data_abort enable - -init -reset halt diff --git a/debug/targets/RISC-V/spike32-2-rtos.py b/debug/targets/RISC-V/spike32-2-rtos.py new file mode 100644 index 0000000..a7b9a1c --- /dev/null +++ b/debug/targets/RISC-V/spike32-2-rtos.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-rtos.cfg" + timeout_sec = 30 + + def create(self): + return testlib.Spike(self) diff --git a/debug/targets/RISC-V/spike32-2.py b/debug/targets/RISC-V/spike32-2.py index a7b9a1c..719009d 100644 --- a/debug/targets/RISC-V/spike32-2.py +++ b/debug/targets/RISC-V/spike32-2.py @@ -5,7 +5,7 @@ import spike32 # pylint: disable=import-error class spike32_2(targets.Target): harts = [spike32.spike32_hart(), spike32.spike32_hart()] - openocd_config_path = "spike-rtos.cfg" + openocd_config_path = "spike-2.cfg" timeout_sec = 30 def create(self): diff --git a/debug/targets/RISC-V/spike32.py b/debug/targets/RISC-V/spike32.py index bcb5892..809463c 100644 --- a/debug/targets/RISC-V/spike32.py +++ b/debug/targets/RISC-V/spike32.py @@ -11,7 +11,7 @@ class spike32_hart(targets.Hart): class spike32(targets.Target): harts = [spike32_hart()] - openocd_config_path = "spike.cfg" + openocd_config_path = "spike-1.cfg" timeout_sec = 30 def create(self): diff --git a/debug/targets/RISC-V/spike64-2-rtos.py b/debug/targets/RISC-V/spike64-2-rtos.py new file mode 100644 index 0000000..d65d2ab --- /dev/null +++ b/debug/targets/RISC-V/spike64-2-rtos.py @@ -0,0 +1,12 @@ +import targets +import testlib + +import spike64 # pylint: disable=import-error + +class spike64_2_rtos(targets.Target): + harts = [spike64.spike64_hart(), spike64.spike64_hart()] + openocd_config_path = "spike-rtos.cfg" + timeout_sec = 30 + + def create(self): + return testlib.Spike(self) diff --git a/debug/targets/RISC-V/spike64-2.py b/debug/targets/RISC-V/spike64-2.py index 4f6f1ff..709ebbe 100644 --- a/debug/targets/RISC-V/spike64-2.py +++ b/debug/targets/RISC-V/spike64-2.py @@ -5,7 +5,7 @@ import spike64 # pylint: disable=import-error class spike64_2(targets.Target): harts = [spike64.spike64_hart(), spike64.spike64_hart()] - openocd_config_path = "spike-rtos.cfg" + openocd_config_path = "spike-2.cfg" timeout_sec = 30 def create(self): diff --git a/debug/targets/RISC-V/spike64.py b/debug/targets/RISC-V/spike64.py index 9c37f87..2cd67a5 100644 --- a/debug/targets/RISC-V/spike64.py +++ b/debug/targets/RISC-V/spike64.py @@ -11,7 +11,7 @@ class spike64_hart(targets.Hart): class spike64(targets.Target): harts = [spike64_hart()] - openocd_config_path = "spike.cfg" + openocd_config_path = "spike-1.cfg" timeout_sec = 30 def create(self): diff --git a/debug/testlib.py b/debug/testlib.py index 0bdb433..385034b 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -1,4 +1,5 @@ import collections +import os import os.path import random import re @@ -51,8 +52,6 @@ def compile(args, xlen=32): # pylint: disable=redefined-builtin raise Exception("Compile failed!") class Spike(object): - logname = "spike-%d.log" % os.getpid() - 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.""" @@ -68,11 +67,13 @@ class Spike(object): "programs/checksum.c", "programs/tiny-malloc.c", "programs/infinite_loop.S", "-DDEFINE_MALLOC", "-DDEFINE_FREE") cmd.append(self.infinite_loop) - logfile = open(self.logname, "w") - logfile.write("+ %s\n" % " ".join(cmd)) - logfile.flush() + self.logfile = tempfile.NamedTemporaryFile(prefix="spike-", + suffix=".log") + self.logname = self.logfile.name + self.logfile.write("+ %s\n" % " ".join(cmd)) + self.logfile.flush() self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, - stdout=logfile, stderr=logfile) + stdout=self.logfile, stderr=self.logfile) if with_jtag_gdb: self.port = None @@ -223,8 +224,7 @@ class Openocd(object): logfile.write("+ %s\n" % " ".join(cmd)) logfile.flush() - self.ports = [] - self.port = None + self.gdb_ports = [] self.process = self.start(cmd, logfile) def start(self, cmd, logfile): @@ -238,31 +238,32 @@ class Openocd(object): # attempt too early. start = time.time() messaged = False + fd = open(Openocd.logname, "r") while True: - log = open(Openocd.logname).read() + line = fd.readline() + if not line: + if not process.poll() is None: + raise Exception("OpenOCD exited early.") + time.sleep(0.1) + continue + m = re.search(r"Listening on port (\d+) for gdb connections", - log) + line) if m: - if not self.ports: - self.port = int(m.group(1)) - self.ports.append(int(m.group(1))) + self.gdb_ports.append(int(m.group(1))) - if "telnet server disabled" in log: - break + if "telnet server disabled" in line: + return process - 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) > self.timeout: - raise Exception("ERROR: Timed out waiting for OpenOCD to " + raise Exception("Timed out waiting for OpenOCD to " "listen for gdb") - return process + except Exception: - header("OpenOCD log") - sys.stdout.write(log) + print_log(Openocd.logname) raise def __del__(self): @@ -303,42 +304,109 @@ class CannotAccess(Exception): Exception.__init__(self) self.address = address -Thread = collections.namedtuple('Thread', ('id', 'target_id', 'name', - 'frame')) +Thread = collections.namedtuple('Thread', ('id', 'description', 'target_id', + 'name', 'frame')) class Gdb(object): - logfile = tempfile.NamedTemporaryFile(prefix="gdb", suffix=".log") - logname = logfile.name - print "GDB Temporary Log File: %s" % logname - - def __init__(self, - cmd=os.path.expandvars("$RISCV/bin/riscv64-unknown-elf-gdb")): - self.child = pexpect.spawn(cmd) - self.child.logfile = open(self.logname, "w") - self.child.logfile.write("+ %s\n" % cmd) - self.wait() - self.command("set confirm off") - self.command("set width 0") - self.command("set height 0") - # Force consistency. - self.command("set print entry-values no") + """A single gdb class which can interact with one or more gdb instances.""" + + # pylint: disable=too-many-public-methods + + def __init__(self, ports, + cmd=os.path.expandvars("$RISCV/bin/riscv64-unknown-elf-gdb"), + binary=None): + assert ports + + self.stack = [] + + self.logfiles = [] + self.children = [] + for port in ports: + logfile = tempfile.NamedTemporaryFile(prefix="gdb@%d-" % port, + suffix=".log") + self.logfiles.append(logfile) + child = pexpect.spawn(cmd) + child.logfile = logfile + child.logfile.write("+ %s\n" % cmd) + self.children.append(child) + self.active_child = self.children[0] + + self.harts = {} + for port, child in zip(ports, self.children): + self.select_child(child) + self.wait() + self.command("set confirm off") + self.command("set width 0") + self.command("set height 0") + # Force consistency. + self.command("set print entry-values no") + self.command("target extended-remote localhost:%d" % port) + if binary: + self.command("file %s" % binary) + threads = self.threads() + for t in threads: + hartid = None + if t.name: + m = re.search(r"Hart (\d+)", t.name) + if m: + hartid = int(m.group(1)) + if hartid is None: + if self.harts: + hartid = max(self.harts) + 1 + else: + hartid = 0 + self.harts[hartid] = (child, t) + + def __del__(self): + for child in self.children: + del child + + def lognames(self): + return [logfile.name for logfile in self.logfiles] + + def select_child(self, child): + self.active_child = child def select_hart(self, hart): - output = self.command("thread %d" % (hart.index + 1)) + child, thread = self.harts[hart.id] + self.select_child(child) + output = self.command("thread %s" % thread.id) assert "Unknown" not in output + def push_state(self): + self.stack.append({ + 'active_child': self.active_child + }) + + def pop_state(self): + state = self.stack.pop() + self.active_child = state['active_child'] + def wait(self): """Wait for prompt.""" - self.child.expect(r"\(gdb\)") + self.active_child.expect(r"\(gdb\)") def command(self, command, timeout=6000): """timeout is in seconds""" - self.child.sendline(command) - self.child.expect("\n", timeout=timeout) - self.child.expect(r"\(gdb\)", timeout=timeout) - return self.child.before.strip() + self.active_child.sendline(command) + self.active_child.expect("\n", timeout=timeout) + self.active_child.expect(r"\(gdb\)", timeout=timeout) + return self.active_child.before.strip() + + def global_command(self, command): + """Execute this command on every gdb that we control.""" + with PrivateState(self): + for child in self.children: + self.select_child(child) + self.command(command) def c(self, wait=True, timeout=-1, async=False): + """ + Dumb c command. + In RTOS mode, gdb will resume all harts. + In multi-gdb mode, this command will just go to the current gdb, so + will only resume one hart. + """ if async: async = "&" else: @@ -348,13 +416,24 @@ class Gdb(object): assert "Continuing" in output return output else: - self.child.sendline("c%s" % async) - self.child.expect("Continuing") + self.active_child.sendline("c%s" % async) + self.active_child.expect("Continuing") + + def c_all(self): + """Resume every hart.""" + with PrivateState(self): + for child in self.children: + child.sendline("c") + child.expect("Continuing") + + # Now wait for them all to halt + for child in self.children: + child.expect(r"\(gdb\)") def interrupt(self): - self.child.send("\003") - self.child.expect(r"\(gdb\)", timeout=6000) - return self.child.before.strip() + self.active_child.send("\003") + self.active_child.expect(r"\(gdb\)", timeout=6000) + return self.active_child.before.strip() def x(self, address, size='w'): output = self.command("x/%s %s" % (size, address)) @@ -417,17 +496,32 @@ class Gdb(object): threads = [] for line in output.splitlines(): m = re.match( - r"[\s\*]*(\d+)\s*Thread (\d+)\s*\(Name: ([^\)]+)\s*(.*)", - line) + r"[\s\*]*(\d+)\s*" + r"(Remote target|Thread (\d+)\s*\(Name: ([^\)]+))" + r"\s*(.*)", line) if m: threads.append(Thread(*m.groups())) - if not threads: - threads.append(Thread('1', '1', 'Default', '???')) + assert threads + #>>>if not threads: + #>>> threads.append(Thread('1', '1', 'Default', '???')) return threads def thread(self, thread): return self.command("thread %s" % thread.id) + def where(self): + return self.command("where 1") + +class PrivateState(object): + def __init__(self, gdb): + self.gdb = gdb + + def __enter__(self): + self.gdb.push_state() + + def __exit__(self, _type, _value, _traceback): + self.gdb.pop_state() + def run_all_tests(module, target, parsed): if not os.path.exists(parsed.logs): os.makedirs(parsed.logs) @@ -469,7 +563,7 @@ def run_tests(parsed, target, 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), + print "[%s] Starting > %s" % (name, log_name) instance = definition(target, hart) sys.stdout.flush() log_fd.write("Test: %s\n" % name) @@ -478,12 +572,12 @@ def run_tests(parsed, target, todo): real_stdout = sys.stdout sys.stdout = log_fd try: - result = instance.run() + result = instance.run(real_stdout) log_fd.write("Result: %s\n" % result) finally: sys.stdout = real_stdout log_fd.write("Time elapsed: %.2fs\n" % (time.time() - start)) - print "%s in %.2fs" % (result, time.time() - start) + print "[%s] %s in %.2fs" % (name, result, time.time() - start) if result not in good_results and parsed.print_failures: sys.stdout.write(open(log_name).read()) sys.stdout.flush() @@ -589,7 +683,7 @@ class BaseTest(object): def postMortem(self): pass - def run(self): + def run(self, real_stdout): """ If compile_args is set, compile a program and set self.binary. @@ -608,6 +702,8 @@ class BaseTest(object): try: self.classSetup() + real_stdout.write("[%s] Temporary logs: %s\n" % ( + type(self).__name__, ", ".join(self.logs))) self.setup() result = self.test() # pylint: disable=no-member except TestNotApplicable: @@ -622,7 +718,12 @@ class BaseTest(object): print e.message header("Traceback") traceback.print_exc(file=sys.stdout) - self.postMortem() + try: + self.postMortem() + except Exception as e: # pylint: disable=broad-except + header("postMortem Exception") + print e + traceback.print_exc(file=sys.stdout) return result finally: @@ -645,25 +746,22 @@ class GdbTest(BaseTest): BaseTest.classSetup(self) if gdb_cmd: - self.gdb = Gdb(gdb_cmd) + self.gdb = Gdb(self.server.gdb_ports, gdb_cmd, binary=self.binary) else: - self.gdb = Gdb() + self.gdb = Gdb(self.server.gdb_ports, binary=self.binary) - self.logs.append(self.gdb.logname) + self.logs += self.gdb.lognames() - if self.binary: - self.gdb.command("file %s" % self.binary) if self.target: - 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) - self.gdb.select_hart(self.hart) + self.gdb.global_command("set arch riscv:rv%d" % self.hart.xlen) + self.gdb.global_command("set remotetimeout %d" % + self.target.timeout_sec) for cmd in self.target.gdb_setup: self.gdb.command(cmd) + self.gdb.select_hart(self.hart) + # FIXME: OpenOCD doesn't handle PRIV now #self.gdb.p("$priv=3") -- cgit v1.1 From 76029e5a96545c6cc97bce17b69f99dcb51c5f6c Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Fri, 29 Sep 2017 15:41:30 -0700 Subject: Make ExamineTarget multi-core aware. Now on multi-core targets it only runs once, wasting less time. --- debug/testlib.py | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/debug/testlib.py b/debug/testlib.py index 996c188..94694a0 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -447,14 +447,16 @@ def run_all_tests(module, target, parsed): gdb_cmd = parsed.gdb todo = [] + examine_added = False 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)) + elif not examine_added: + todo.append(("ExamineTarget", ExamineTarget, None)) + examine_added = True for name in dir(module): definition = getattr(module, name) @@ -699,23 +701,26 @@ class GdbSingleHartTest(GdbTest): class ExamineTarget(GdbTest): def test(self): - self.hart.misa = self.gdb.p("$misa") - - txt = "RV" - if (self.hart.misa >> 30) == 1: - txt += "32" - elif (self.hart.misa >> 62) == 2: - txt += "64" - elif (self.hart.misa >> 126) == 3: - txt += "128" - else: - raise TestFailed("Couldn't determine XLEN from $misa (0x%x)" % - self.hart.misa) + for hart in self.target.harts: + self.gdb.select_hart(hart) + + hart.misa = self.gdb.p("$misa") + + txt = "RV" + if (hart.misa >> 30) == 1: + txt += "32" + elif (hart.misa >> 62) == 2: + txt += "64" + elif (hart.misa >> 126) == 3: + txt += "128" + else: + raise TestFailed("Couldn't determine XLEN from $misa (0x%x)" % + self.hart.misa) - for i in range(26): - if self.hart.misa & (1< Date: Sat, 30 Sep 2017 14:48:15 -0700 Subject: Resurrect priv tests. --- debug/gdbserver.py | 103 ++++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/debug/gdbserver.py b/debug/gdbserver.py index 924f42a..d2c4fe9 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -12,7 +12,7 @@ import targets import testlib from testlib import assertEqual, assertNotEqual, assertIn, assertNotIn from testlib import assertGreater, assertRegexpMatches, assertLess -from testlib import GdbTest, GdbSingleHartTest, TestFailed +from testlib import GdbTest, GdbSingleHartTest, TestFailed, assertTrue MSTATUS_UIE = 0x00000001 MSTATUS_SIE = 0x00000002 @@ -774,7 +774,6 @@ class DownloadTest(GdbTest): assertEqual(self.gdb.p("status"), self.crc) os.unlink(self.download_c.name) -# FIXME: PRIV isn't implemented in the current OpenOCD #class MprvTest(GdbTest): # compile_args = ("programs/mprv.S", ) # def setup(self): @@ -787,56 +786,56 @@ class DownloadTest(GdbTest): # self.gdb.interrupt() # output = self.gdb.command("p/x *(int*)(((char*)&data)-0x80000000)") # assertIn("0xbead", output) -# -#class PrivTest(GdbTest): -# compile_args = ("programs/priv.S", ) -# def setup(self): -# # pylint: disable=attribute-defined-outside-init -# self.gdb.load() -# -# misa = self.hart.misa -# self.supported = set() -# if misa & (1<<20): -# self.supported.add(0) -# if misa & (1<<18): -# self.supported.add(1) -# if misa & (1<<7): -# self.supported.add(2) -# self.supported.add(3) -# -#class PrivRw(PrivTest): -# def test(self): -# """Test reading/writing priv.""" -# for privilege in range(4): -# self.gdb.p("$priv=%d" % privilege) -# self.gdb.stepi() -# actual = self.gdb.p("$priv") -# assertIn(actual, self.supported) -# if privilege in self.supported: -# assertEqual(actual, privilege) -# -#class PrivChange(PrivTest): -# def test(self): -# """Test that the core's privilege level actually changes.""" -# -# if 0 not in self.supported: -# return 'not_applicable' -# -# self.gdb.b("main") -# self.gdb.c() -# -# # Machine mode -# self.gdb.p("$priv=3") -# main_address = self.gdb.p("$pc") -# self.gdb.stepi() -# assertEqual("%x" % self.gdb.p("$pc"), "%x" % (main_address+4)) -# -# # User mode -# self.gdb.p("$priv=0") -# self.gdb.stepi() -# # Should have taken an exception, so be nowhere near main. -# pc = self.gdb.p("$pc") -# assertTrue(pc < main_address or pc > main_address + 0x100) + +class PrivTest(GdbTest): + compile_args = ("programs/priv.S", ) + def setup(self): + # pylint: disable=attribute-defined-outside-init + self.gdb.load() + + misa = self.hart.misa + self.supported = set() + if misa & (1<<20): + self.supported.add(0) + if misa & (1<<18): + self.supported.add(1) + if misa & (1<<7): + self.supported.add(2) + self.supported.add(3) + +class PrivRw(PrivTest): + def test(self): + """Test reading/writing priv.""" + for privilege in range(4): + self.gdb.p("$priv=%d" % privilege) + self.gdb.stepi() + actual = self.gdb.p("$priv") + assertIn(actual, self.supported) + if privilege in self.supported: + assertEqual(actual, privilege) + +class PrivChange(PrivTest): + def test(self): + """Test that the core's privilege level actually changes.""" + + if 0 not in self.supported: + return 'not_applicable' + + self.gdb.b("main") + self.gdb.c() + + # Machine mode + self.gdb.p("$priv=3") + main_address = self.gdb.p("$pc") + self.gdb.stepi() + assertEqual("%x" % self.gdb.p("$pc"), "%x" % (main_address+4)) + + # User mode + self.gdb.p("$priv=0") + self.gdb.stepi() + # Should have taken an exception, so be nowhere near main. + pc = self.gdb.p("$pc") + assertTrue(pc < main_address or pc > main_address + 0x100) parsed = None def main(): -- cgit v1.1