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)
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."""
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)
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):
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 <stdint.h>\n")
fd.write("uint32_t length = %d;\n" % length)
fd.write("uint8_t d[%d] = {\n" % length)
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 = [
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()
}
return reverse(~crc);
}
-
-extern uint8_t *data;
-extern uint32_t length;
-
-uint32_t main()
-{
- /* Compute a simple checksum. */
- return crc32a(data, length);
-}
#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
-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<length; x++) {
- printf("%c", c);
+ while (*buf) {
+ if ((*buf >= '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;
}
;
}
-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());
+}
+#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
--- /dev/null
+#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
--- /dev/null
+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 = .;
+}
+
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:
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:
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")
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))
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