From 6990284b8eab8d4e4f57f82ac8918913c5c63e97 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Tue, 7 Jun 2016 16:59:26 -0700 Subject: [PATCH] Made some progress towards working with spike. I'm writing all the tests so they should just work on real hardware, too. --- debug/gdbserver.py | 94 ++++++++++++++++--------- debug/programs/checksum.c | 9 --- debug/programs/debug.c | 47 +++++++++---- debug/programs/init.c | 14 +++- debug/programs/regs.S | 70 +++++++++++-------- debug/targets/spike/entry.S | 131 +++++++++++++++++++++++++++++++++++ debug/targets/spike/link.lds | 35 ++++++++++ debug/testlib.py | 35 +++++++--- 8 files changed, 338 insertions(+), 97 deletions(-) create mode 100755 debug/targets/spike/entry.S create mode 100755 debug/targets/spike/link.lds diff --git a/debug/gdbserver.py b/debug/gdbserver.py index 8b7b562..88c5657 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -14,9 +14,21 @@ class DeleteServer(unittest.TestCase): def tearDown(self): del self.server +class MemoryTest(DeleteServer): + def setUp(self): + self.server = target.server() + self.gdb = testlib.Gdb() + self.gdb.command("target extended-remote localhost:%d" % self.server.port) + + def test_32(self): + self.gdb.p("*((int*)0x%x) = 0x8675309" % target.ram) + self.gdb.p("*((int*)0x%x) = 0xdeadbeef" % (target.ram + 4)) + self.assertEqual(self.gdb.p("*((int*)0x%x)" % target.ram), 0x8675309) + self.assertEqual(self.gdb.p("*((int*)0x%x)" % (target.ram + 4)), 0xdeadbeef) + class InstantHaltTest(DeleteServer): def setUp(self): - self.binary = target.compile("debug.c") + self.binary = target.compile("programs/debug.c") self.server = target.server(self.binary, halted=True) self.gdb = testlib.Gdb() self.gdb.command("file %s" % self.binary) @@ -43,11 +55,19 @@ class InstantHaltTest(DeleteServer): class DebugTest(DeleteServer): def setUp(self): - self.binary = target.compile("debug.c") - self.server = target.server(self.binary, halted=False) + self.binary = target.compile("programs/debug.c", "programs/checksum.c") + self.server = target.server() self.gdb = testlib.Gdb() self.gdb.command("file %s" % self.binary) self.gdb.command("target extended-remote localhost:%d" % self.server.port) + self.gdb.load(self.binary) + self.gdb.b("_exit") + + def exit(self): + output = self.gdb.c() + self.assertIn("Breakpoint", output) + self.assertIn("_exit", output) + self.assertEqual(self.gdb.p("status"), 0xc86455d4) def test_turbostep(self): """Single step a bunch of times.""" @@ -60,26 +80,19 @@ class DebugTest(DeleteServer): last_pc = pc def test_exit(self): - self.gdb.command("p i=0"); - output = self.gdb.command("c") - self.assertIn("Continuing", output) - self.assertIn("Remote connection closed", output) + self.exit() def test_breakpoint(self): - self.gdb.command("p i=0"); - self.gdb.command("b print_row") - # The breakpoint should be hit exactly 10 times. - for i in range(10): - output = self.gdb.command("c") - self.assertIn("Continuing", output) - self.assertIn("length=%d" % i, output) - self.assertIn("Breakpoint 1", output) - output = self.gdb.command("c") - self.assertIn("Continuing", output) - self.assertIn("Remote connection closed", output) + self.gdb.b("rot13") + # The breakpoint should be hit exactly 2 times. + for i in range(2): + output = self.gdb.c() + self.assertIn("Breakpoint ", output) + self.exit() def test_registers(self): - self.gdb.command("p i=0"); + self.gdb.b("rot13") + self.gdb.c() # Try both forms to test gdb. for cmd in ("info all-registers", "info registers all"): output = self.gdb.command(cmd) @@ -100,19 +113,20 @@ class DebugTest(DeleteServer): last_instret = instret self.gdb.command("stepi") + self.exit() + def test_interrupt(self): """Sending gdb ^C while the program is running should cause it to halt.""" - self.gdb.c(wait=False) - time.sleep(0.1) - self.gdb.interrupt() + self.gdb.b("main:start") + self.gdb.c() self.gdb.command("p i=123"); self.gdb.c(wait=False) time.sleep(0.1) - self.gdb.interrupt() - self.gdb.command("p i=0"); - output = self.gdb.c() - self.assertIn("Continuing", output) - self.assertIn("Remote connection closed", output) + output = self.gdb.interrupt() + assert "main" in output + self.assertGreater(self.gdb.p("j"), 10) + self.gdb.p("i=0"); + self.exit() class RegsTest(DeleteServer): def setUp(self): @@ -169,6 +183,14 @@ class DownloadTest(DeleteServer): def setUp(self): length = 2**20 fd = file("data.c", "w") +# extern uint8_t *data; +# extern uint32_t length; +# +# uint32_t main() +#{ +# /* Compute a simple checksum. */ +# return crc32a(data, length); +#} fd.write("#include \n") fd.write("uint32_t length = %d;\n" % length) fd.write("uint8_t d[%d] = {\n" % length) @@ -225,20 +247,28 @@ class Target(object): raise NotImplementedError def compile(self, *sources): - return testlib.compile(*(sources + + return testlib.compile(sources + ("targets/%s/entry.S" % self.name, "programs/init.c", "-I", "../env", "-T", "targets/%s/link.lds" % self.name, - "-nostartfiles"))) + "-nostartfiles", + "-mcmodel=medany"), xlen=self.xlen) class SpikeTarget(Target): name = "spike" + xlen = 64 + ram = 0x80010000 + + def server(self): + return testlib.Spike(parsed.cmd, halted=True) class MicroSemiTarget(Target): name = "m2gl_m2s" + xlen = 32 + ram = 0x80000000 def server(self): - return testlib.Openocd(cmd=parsed.openocd, + return testlib.Openocd(cmd=parsed.cmd, config="targets/%s/openocd.cfg" % self.name) targets = [ @@ -252,8 +282,8 @@ def main(): for t in targets: group.add_argument("--%s" % t.name, action="store_const", const=t, dest="target") - parser.add_argument("--openocd", help="The OpenOCD command to use.", - default="openocd") + parser.add_argument("--cmd", + help="The command to use to start the debug server.") parser.add_argument("unittest", nargs="*") global parsed parsed = parser.parse_args() diff --git a/debug/programs/checksum.c b/debug/programs/checksum.c index 36152fc..e076f8a 100644 --- a/debug/programs/checksum.c +++ b/debug/programs/checksum.c @@ -36,12 +36,3 @@ unsigned int crc32a(uint8_t *message, unsigned int size) { } return reverse(~crc); } - -extern uint8_t *data; -extern uint32_t length; - -uint32_t main() -{ - /* Compute a simple checksum. */ - return crc32a(data, length); -} diff --git a/debug/programs/debug.c b/debug/programs/debug.c index 2cad88f..afca484 100644 --- a/debug/programs/debug.c +++ b/debug/programs/debug.c @@ -1,27 +1,46 @@ #include +#include +#include -char c = 'x'; +unsigned int crc32a(uint8_t *message, unsigned int size); -void print_row(int length) +void rot13(char *buf) { - for (int x=0; x= 'a' && *buf <= 'm') || + (*buf >= 'A' && *buf <= 'M')) { + *buf += 13; + } else if ((*buf >= 'n' && *buf <= 'z') || + (*buf >= 'N' && *buf <= 'Z')) { + *buf -= 13; + } + buf++; } - printf("\n"); +} + +size_t strlen(const char *buf) +{ + int len = 0; + while (buf[len]) + len++; + return len; } int main() { - volatile int i = 42; - const char *text = "constant\n"; - int threshold = 7; + volatile int i = 0; + int j = 0; + char *fox = "The quick brown fox jumps of the lazy dog."; + unsigned int checksum = 0; - // Wait for the debugger to get us out of this loop. +start: while (i) - ; + j++; - printf("%s", text); - for (int y=0; y < 10; y++) { - print_row(y); - } + rot13(fox); + checksum ^= crc32a(fox, strlen(fox)); + rot13(fox); + checksum ^= crc32a(fox, strlen(fox)); + + return checksum; } diff --git a/debug/programs/init.c b/debug/programs/init.c index 074bc21..a2b41b0 100644 --- a/debug/programs/init.c +++ b/debug/programs/init.c @@ -6,9 +6,19 @@ void handle_trap(unsigned int mcause, unsigned int mepc, unsigned int sp) ; } -void _init() +void _exit(int status) { - main(); + // Make sure gcc doesn't inline _exit, so we can actually set a breakpoint + // on it. + volatile int i = 42; + while (i) + ; + // _exit isn't supposed to return. while (1) ; } + +void _init() +{ + _exit(main()); +} diff --git a/debug/programs/regs.S b/debug/programs/regs.S index e6456e1..54b3961 100644 --- a/debug/programs/regs.S +++ b/debug/programs/regs.S @@ -1,38 +1,48 @@ +#ifdef __riscv64 +# define LREG ld +# define SREG sd +# define REGBYTES 8 +#else +# define LREG lw +# define SREG sw +# define REGBYTES 4 +#endif + .global main main: j main write_regs: - sd x1, 0(a0) - sd x2, 8(a0) - sd x3, 16(a0) - sd x4, 24(a0) - sd x5, 32(a0) - sd x6, 40(a0) - sd x7, 48(a0) - sd x8, 56(a0) - sd x9, 64(a0) - sd x11, 72(a0) - sd x12, 80(a0) - sd x13, 88(a0) - sd x14, 96(a0) - sd x15, 104(a0) - sd x16, 112(a0) - sd x17, 120(a0) - sd x18, 128(a0) - sd x19, 136(a0) - sd x20, 144(a0) - sd x21, 152(a0) - sd x22, 160(a0) - sd x23, 168(a0) - sd x24, 176(a0) - sd x25, 184(a0) - sd x26, 192(a0) - sd x27, 200(a0) - sd x28, 208(a0) - sd x29, 216(a0) - sd x30, 224(a0) - sd x31, 232(a0) + SREG x1, 0(a0) + SREG x2, 8(a0) + SREG x3, 16(a0) + SREG x4, 24(a0) + SREG x5, 32(a0) + SREG x6, 40(a0) + SREG x7, 48(a0) + SREG x8, 56(a0) + SREG x9, 64(a0) + SREG x11, 72(a0) + SREG x12, 80(a0) + SREG x13, 88(a0) + SREG x14, 96(a0) + SREG x15, 104(a0) + SREG x16, 112(a0) + SREG x17, 120(a0) + SREG x18, 128(a0) + SREG x19, 136(a0) + SREG x20, 144(a0) + SREG x21, 152(a0) + SREG x22, 160(a0) + SREG x23, 168(a0) + SREG x24, 176(a0) + SREG x25, 184(a0) + SREG x26, 192(a0) + SREG x27, 200(a0) + SREG x28, 208(a0) + SREG x29, 216(a0) + SREG x30, 224(a0) + SREG x31, 232(a0) csrr x1, 1 # fflags diff --git a/debug/targets/spike/entry.S b/debug/targets/spike/entry.S new file mode 100755 index 0000000..ff49cf6 --- /dev/null +++ b/debug/targets/spike/entry.S @@ -0,0 +1,131 @@ +#ifndef ENTRY_S +#define ENTRY_S + +#include "encoding.h" + +#define STACK_SIZE ((1 << 12) - 128) + +#ifdef __riscv64 +# define LREG ld +# define SREG sd +# define REGBYTES 8 +#else +# define LREG lw +# define SREG sw +# define REGBYTES 4 +#endif + + .section .text.entry + .globl _start +_start: + j handle_reset + +nmi_vector: + j nmi_vector + +trap_vector: + j trap_entry + +handle_reset: + la t0, trap_entry + csrw mtvec, t0 + csrwi mstatus, 0 + csrwi mideleg, 0 + csrwi medeleg, 0 + csrwi mie, 0 + + # initialize global pointer + la gp, _gp + + # initialize stack pointer + la sp, stack_top + + # perform the rest of initialization in C + j _init + +trap_entry: + addi sp, sp, -32*REGBYTES + + SREG x1, 1*REGBYTES(sp) + SREG x2, 2*REGBYTES(sp) + SREG x3, 3*REGBYTES(sp) + SREG x4, 4*REGBYTES(sp) + SREG x5, 5*REGBYTES(sp) + SREG x6, 6*REGBYTES(sp) + SREG x7, 7*REGBYTES(sp) + SREG x8, 8*REGBYTES(sp) + SREG x9, 9*REGBYTES(sp) + SREG x10, 10*REGBYTES(sp) + SREG x11, 11*REGBYTES(sp) + SREG x12, 12*REGBYTES(sp) + SREG x13, 13*REGBYTES(sp) + SREG x14, 14*REGBYTES(sp) + SREG x15, 15*REGBYTES(sp) + SREG x16, 16*REGBYTES(sp) + SREG x17, 17*REGBYTES(sp) + SREG x18, 18*REGBYTES(sp) + SREG x19, 19*REGBYTES(sp) + SREG x20, 20*REGBYTES(sp) + SREG x21, 21*REGBYTES(sp) + SREG x22, 22*REGBYTES(sp) + SREG x23, 23*REGBYTES(sp) + SREG x24, 24*REGBYTES(sp) + SREG x25, 25*REGBYTES(sp) + SREG x26, 26*REGBYTES(sp) + SREG x27, 27*REGBYTES(sp) + SREG x28, 28*REGBYTES(sp) + SREG x29, 29*REGBYTES(sp) + SREG x30, 30*REGBYTES(sp) + SREG x31, 31*REGBYTES(sp) + + csrr a0, mcause + csrr a1, mepc + mv a2, sp + jal handle_trap + csrw mepc, a0 + + # Remain in M-mode after mret + li t0, MSTATUS_MPP + csrs mstatus, t0 + + LREG x1, 1*REGBYTES(sp) + LREG x2, 2*REGBYTES(sp) + LREG x3, 3*REGBYTES(sp) + LREG x4, 4*REGBYTES(sp) + LREG x5, 5*REGBYTES(sp) + LREG x6, 6*REGBYTES(sp) + LREG x7, 7*REGBYTES(sp) + LREG x8, 8*REGBYTES(sp) + LREG x9, 9*REGBYTES(sp) + LREG x10, 10*REGBYTES(sp) + LREG x11, 11*REGBYTES(sp) + LREG x12, 12*REGBYTES(sp) + LREG x13, 13*REGBYTES(sp) + LREG x14, 14*REGBYTES(sp) + LREG x15, 15*REGBYTES(sp) + LREG x16, 16*REGBYTES(sp) + LREG x17, 17*REGBYTES(sp) + LREG x18, 18*REGBYTES(sp) + LREG x19, 19*REGBYTES(sp) + LREG x20, 20*REGBYTES(sp) + LREG x21, 21*REGBYTES(sp) + LREG x22, 22*REGBYTES(sp) + LREG x23, 23*REGBYTES(sp) + LREG x24, 24*REGBYTES(sp) + LREG x25, 25*REGBYTES(sp) + LREG x26, 26*REGBYTES(sp) + LREG x27, 27*REGBYTES(sp) + LREG x28, 28*REGBYTES(sp) + LREG x29, 29*REGBYTES(sp) + LREG x30, 30*REGBYTES(sp) + LREG x31, 31*REGBYTES(sp) + + addi sp, sp, 32*REGBYTES + mret + + .bss + .align 4 +stack_bottom: + .skip STACK_SIZE +stack_top: +#endif diff --git a/debug/targets/spike/link.lds b/debug/targets/spike/link.lds new file mode 100755 index 0000000..029a332 --- /dev/null +++ b/debug/targets/spike/link.lds @@ -0,0 +1,35 @@ +OUTPUT_ARCH( "riscv" ) + +SECTIONS +{ + /* Leave some space for pk's data structures, which includes tohost/fromhost + * which are special addresses we ought to leave alone. */ + . = 0x80010000; + .text : + { + *(.text.entry) + *(.text) + } + + /* data segment */ + .data : { *(.data) } + + .sdata : { + _gp = . + 0x800; + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) + *(.srodata*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + } + + /* bss segment */ + .sbss : { + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + } + .bss : { *(.bss) } + + /* End of uninitalized data segement */ + _end = .; + _heap_end = .; +} + diff --git a/debug/testlib.py b/debug/testlib.py index 0902983..d8e485a 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -16,11 +16,11 @@ def find_file(path): return fullpath return None -def compile(*args): +def compile(args, xlen=32): """Compile a single .c file into a binary.""" dst = os.path.splitext(args[0])[0] - cc = os.path.expandvars("$RISCV/bin/riscv64-unknown-elf-gcc") - cmd = [cc, "-g", "-O", "-o", dst] + cc = os.path.expandvars("$RISCV/bin/riscv%d-unknown-elf-gcc" % xlen) + cmd = [cc, "-g", "-o", dst] for arg in args: found = find_file(arg) if found: @@ -42,13 +42,16 @@ def unused_port(): return port class Spike(object): - def __init__(self, binary, halted=False, with_gdb=True, timeout=None): + def __init__(self, cmd, binary=None, halted=False, with_gdb=True, timeout=None): """Launch spike. Return tuple of its process and the port it's running on.""" - cmd = [] + if cmd: + cmd = shlex.split(cmd) + else: + cmd = ["spike"] + if timeout: - cmd += ["timeout", str(timeout)] + cmd = ["timeout", str(timeout)] + cmd - cmd += [find_file("spike")] if halted: cmd.append('-H') if with_gdb: @@ -117,7 +120,9 @@ class Gdb(object): def c(self, wait=True): if wait: - return self.command("c") + output = self.command("c") + assert "Continuing" in output + return output else: self.child.sendline("c") self.child.expect("Continuing") @@ -125,6 +130,7 @@ class Gdb(object): def interrupt(self): self.child.send("\003"); self.child.expect("\(gdb\)") + return self.child.before.strip() def x(self, address, size='w'): output = self.command("x/%s %s" % (size, address)) @@ -132,9 +138,18 @@ class Gdb(object): return value def p(self, obj): - output = self.command("p %s" % obj) - value = int(output.split('=')[-1].strip()) + output = self.command("p/x %s" % obj) + value = int(output.split('=')[-1].strip(), 0) return value def stepi(self): return self.command("stepi") + + def load(self, binary): + return self.command("load %s" % binary) + + def b(self, location): + output = self.command("b %s" % location) + assert "not defined" not in output + assert "Breakpoint" in output + return output -- 2.30.2