From 9baa8a015cddc294d53d72ce57a8ca2f5c832c64 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Tue, 10 May 2016 10:03:28 -0700 Subject: [PATCH] Write test for downloading a mostly random program It passes, but it's slow. --- riscv/gdbserver.cc | 9 ++++-- tests/checksum.c | 47 +++++++++++++++++++++++++++++ tests/ebreak.py | 4 +-- tests/gdbserver.py | 72 +++++++++++++++++++++++++++++--------------- tests/mprv.S | 4 +-- tests/standalone.lds | 2 +- tests/start.S | 12 ++++++++ tests/testlib.py | 57 ++++++++++++++++++++--------------- 8 files changed, 151 insertions(+), 56 deletions(-) create mode 100644 tests/checksum.c create mode 100644 tests/start.S diff --git a/riscv/gdbserver.cc b/riscv/gdbserver.cc index 03df123..7c94615 100644 --- a/riscv/gdbserver.cc +++ b/riscv/gdbserver.cc @@ -729,6 +729,11 @@ class memory_write_op_t : public operation_t return false; } + if (gs.read_debug_ram(DEBUG_RAM_SIZE / 4 - 1)) { + fprintf(stderr, "Exception happened while writing to 0x%lx -> 0x%lx\n", + vaddr, paddr); + } + offset += access_size; if (offset >= length) { gs.send_packet("OK"); @@ -1509,8 +1514,8 @@ void gdbserver_t::handle_packet(const std::vector &packet) return handle_halt_reason(packet); case 'g': return handle_general_registers_read(packet); - case 'k': - return handle_kill(packet); +// case 'k': +// return handle_kill(packet); case 'm': return handle_memory_read(packet); // case 'M': diff --git a/tests/checksum.c b/tests/checksum.c new file mode 100644 index 0000000..36152fc --- /dev/null +++ b/tests/checksum.c @@ -0,0 +1,47 @@ +#include + +// CRC code from http://www.hackersdelight.org/hdcodetxt/crc.c.txt + +// Reverses (reflects) bits in a 32-bit word. +unsigned reverse(unsigned x) { + x = ((x & 0x55555555) << 1) | ((x >> 1) & 0x55555555); + x = ((x & 0x33333333) << 2) | ((x >> 2) & 0x33333333); + x = ((x & 0x0F0F0F0F) << 4) | ((x >> 4) & 0x0F0F0F0F); + x = (x << 24) | ((x & 0xFF00) << 8) | + ((x >> 8) & 0xFF00) | (x >> 24); + return x; +} + +// ----------------------------- crc32a -------------------------------- + +/* This is the basic CRC algorithm with no optimizations. It follows the +logic circuit as closely as possible. */ + +unsigned int crc32a(uint8_t *message, unsigned int size) { + int i, j; + unsigned int byte, crc; + + i = 0; + crc = 0xFFFFFFFF; + while (i < size) { + byte = message[i]; // Get next byte. + byte = reverse(byte); // 32-bit reversal. + for (j = 0; j <= 7; j++) { // Do eight times. + if ((int)(crc ^ byte) < 0) + crc = (crc << 1) ^ 0x04C11DB7; + else crc = crc << 1; + byte = byte << 1; // Ready next msg bit. + } + i = i + 1; + } + 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/tests/ebreak.py b/tests/ebreak.py index 4b41f7d..dd7e658 100755 --- a/tests/ebreak.py +++ b/tests/ebreak.py @@ -12,13 +12,13 @@ class EbreakTest(unittest.TestCase): def test_noport(self): """Make sure that we can run past ebreak when --gdb-port isn't used.""" - spike = testlib.spike(self.binary, with_gdb=False, timeout=10) + spike = testlib.Spike(self.binary, with_gdb=False, timeout=10) result = spike.wait() self.assertEqual(result, 0) def test_nogdb(self): """Make sure that we can run past ebreak when gdb isn't attached.""" - spike, port = testlib.spike(self.binary, timeout=10) + spike = testlib.Spike(self.binary, timeout=10) result = spike.wait() self.assertEqual(result, 0) diff --git a/tests/gdbserver.py b/tests/gdbserver.py index 6ca9af6..5be6574 100755 --- a/tests/gdbserver.py +++ b/tests/gdbserver.py @@ -5,18 +5,16 @@ import testlib import unittest import tempfile import time +import random +import binascii class InstantHaltTest(unittest.TestCase): def setUp(self): self.binary = testlib.compile("debug.c") - self.spike, self.port = testlib.spike(self.binary, halted=True) + self.spike = testlib.Spike(self.binary, halted=True) self.gdb = testlib.Gdb() self.gdb.command("file %s" % self.binary) - self.gdb.command("target extended-remote localhost:%d" % self.port) - - def tearDown(self): - self.spike.kill() - self.spike.wait() + self.gdb.command("target extended-remote localhost:%d" % self.spike.port) def test_instant_halt(self): self.assertEqual(0x1000, self.gdb.p("$pc")) @@ -40,14 +38,10 @@ class InstantHaltTest(unittest.TestCase): class DebugTest(unittest.TestCase): def setUp(self): self.binary = testlib.compile("debug.c") - self.spike, self.port = testlib.spike(self.binary, halted=False) + self.spike = testlib.Spike(self.binary, halted=False) self.gdb = testlib.Gdb() self.gdb.command("file %s" % self.binary) - self.gdb.command("target extended-remote localhost:%d" % self.port) - - def tearDown(self): - self.spike.kill() - self.spike.wait() + self.gdb.command("target extended-remote localhost:%d" % self.spike.port) def test_turbostep(self): """Single step a bunch of times.""" @@ -117,14 +111,10 @@ class DebugTest(unittest.TestCase): class RegsTest(unittest.TestCase): def setUp(self): self.binary = testlib.compile("regs.s") - self.spike, self.port = testlib.spike(self.binary, halted=False) + self.spike = testlib.Spike(self.binary, halted=False) self.gdb = testlib.Gdb() self.gdb.command("file %s" % self.binary) - self.gdb.command("target extended-remote localhost:%d" % self.port) - - def tearDown(self): - self.spike.kill() - self.spike.wait() + self.gdb.command("target extended-remote localhost:%d" % self.spike.port) def test_write_gprs(self): # Note a0 is missing from this list since it's used to hold the @@ -168,20 +158,54 @@ class RegsTest(unittest.TestCase): self.assertEqual(9, self.gdb.p("$x1")) self.assertEqual(9, self.gdb.p("$csr1")) +class DownloadTest(unittest.TestCase): + def setUp(self): + length = 2**16 + fd = file("data.c", "w") + fd.write("#include \n") + fd.write("uint32_t length = %d;\n" % length) + fd.write("uint8_t d[%d] = {\n" % length) + self.crc = 0 + for _ in range(length / 16): + fd.write(" "); + for _ in range(16): + value = random.randrange(1<<8) + fd.write("%d, " % value) + self.crc = binascii.crc32("%c" % value, self.crc) + fd.write("\n"); + fd.write("};\n"); + fd.write("uint8_t *data = &d[0];\n"); + fd.close() + + self.binary = testlib.compile("checksum.c", "data.c", "start.S", + "-mcmodel=medany", + "-T", "standalone.lds", + "-nostartfiles" + ) + self.spike = testlib.Spike(None, halted=True) + self.gdb = testlib.Gdb() + self.gdb.command("file %s" % self.binary) + self.gdb.command("target extended-remote localhost:%d" % self.spike.port) + + def test_download(self): + output = self.gdb.command("load") + self.assertNotIn("failed", output) + self.assertIn("Transfer rate", output) + self.gdb.command("b done") + self.gdb.c() + result = self.gdb.p("$a0") + self.assertEqual(self.crc, result) + class MprvTest(unittest.TestCase): def setUp(self): self.binary = testlib.compile("mprv.S", "-T", "standalone.lds", "-nostartfiles") - self.spike, self.port = testlib.spike(None, halted=True) + self.spike = testlib.Spike(None, halted=True) self.gdb = testlib.Gdb() self.gdb.command("file %s" % self.binary) - self.gdb.command("target extended-remote localhost:%d" % self.port) + self.gdb.command("target extended-remote localhost:%d" % self.spike.port) self.gdb.command("load") - def tearDown(self): - self.spike.kill() - self.spike.wait() - def test_mprv(self): """Test that the debugger can access memory when MPRV is set.""" self.gdb.c(wait=False) diff --git a/tests/mprv.S b/tests/mprv.S index 114918a..df346b3 100644 --- a/tests/mprv.S +++ b/tests/mprv.S @@ -1,10 +1,10 @@ #include "../riscv/encoding.h" #define PGSHIFT 12 - .global main + .global _start .section .text -main: +_start: # Set up a page table entry that maps 0x0... to 0x8... la t0, page_table srli t0, t0, PGSHIFT diff --git a/tests/standalone.lds b/tests/standalone.lds index 6705cfd..9b1bab1 100644 --- a/tests/standalone.lds +++ b/tests/standalone.lds @@ -1,6 +1,6 @@ OUTPUT_ARCH( "riscv" ) -ENTRY( main ) +ENTRY( _start ) SECTIONS { diff --git a/tests/start.S b/tests/start.S new file mode 100644 index 0000000..76c37bb --- /dev/null +++ b/tests/start.S @@ -0,0 +1,12 @@ + .global _start + +_start: + la sp, stack_end + jal main +done: + j done + + .data +stack: + .fill 4096, 1, 0 +stack_end: diff --git a/tests/testlib.py b/tests/testlib.py index 3cba8c7..d5e8d79 100644 --- a/tests/testlib.py +++ b/tests/testlib.py @@ -40,28 +40,35 @@ def unused_port(): s.close() return port -def spike(binary, halted=False, with_gdb=True, timeout=None): - """Launch spike. Return tuple of its process and the port it's running on.""" - cmd = [] - if timeout: - cmd += ["timeout", str(timeout)] - - cmd += [find_file("spike")] - if halted: - cmd.append('-H') - if with_gdb: - port = unused_port() - cmd += ['--gdb-port', str(port)] - cmd.append('pk') - if binary: - cmd.append(binary) - logfile = open("spike.log", "w") - process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=logfile, - stderr=logfile) - if with_gdb: - return process, port - else: - return process +class Spike(object): + def __init__(self, binary, halted=False, with_gdb=True, timeout=None): + """Launch spike. Return tuple of its process and the port it's running on.""" + cmd = [] + if timeout: + cmd += ["timeout", str(timeout)] + + cmd += [find_file("spike")] + if halted: + cmd.append('-H') + if with_gdb: + self.port = unused_port() + cmd += ['--gdb-port', str(self.port)] + cmd.append('pk') + if binary: + cmd.append(binary) + logfile = open("spike.log", "w") + self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=logfile, + stderr=logfile) + + def __del__(self): + try: + self.process.kill() + self.process.wait() + except OSError: + pass + + def wait(self, *args, **kwargs): + return self.process.wait(*args, **kwargs) class Gdb(object): def __init__(self): @@ -78,10 +85,10 @@ class Gdb(object): """Wait for prompt.""" self.child.expect("\(gdb\)") - def command(self, command): + def command(self, command, timeout=-1): self.child.sendline(command) - self.child.expect("\n") - self.child.expect("\(gdb\)") + self.child.expect("\n", timeout=timeout) + self.child.expect("\(gdb\)", timeout=timeout) return self.child.before.strip() def c(self, wait=True): -- 2.30.2