instbasedir := $(DESTDIR)$(prefix)
bmarkdir := $(abs_top_src_dir)/benchmarks
isa_src_dir := $(abs_top_src_dir)/isa
+debug_src_dir := $(abs_top_src_dir)/debug
-all: benchmarks isa
+all: benchmarks isa debug
install: all
install -d $(instbasedir)/share/riscv-tests/isa
mkdir -p isa
$(MAKE) -C isa -f $(isa_src_dir)/Makefile src_dir=$(isa_src_dir) XLEN=$(XLEN)
+debug:
+ mkdir -p debug
+ $(MAKE) -C debug -f $(debug_src_dir)/Makefile src_dir=$(debug_src_dir) XLEN=$(XLEN)
+
clean:
$(MAKE) -C isa -f $(isa_src_dir)/Makefile src_dir=$(isa_src_dir) clean
$(MAKE) -C benchmarks -f $(bmarkdir)/Makefile src_dir=$(bmarkdir) clean
+ $(MAKE) -C debug -f $(bmarkdir)/Makefile src_dir=$(debug_src_dir) clean
.PHONY: benchmarks isa clean
--- /dev/null
+RISCV_SIM ?= spike
+XLEN ?= 64
+
+src_dir ?= .
+GDBSERVER_PY = $(src_dir)/gdbserver.py
+
+default: spike$(XLEN).log
+
+all: spike32.log spike64.log
+
+spike32.log:
+ $(GDBSERVER_PY) --isolate --spike32 --cmd $(RISCV_SIM) -- -v > $@ 2>&1
+
+spike64.log:
+ $(GDBSERVER_PY) --isolate --spike --cmd $(RISCV_SIM) -- -v > $@ 2>&1
+
+clean:
+ rm -f *.log
--- /dev/null
+Debug Tests
+===========
+
+Debugging requires many system components to all work together. The tests here
+perform an end-to-end test, communicating only with gdb. If a simulator or
+hardware passes all these tests, then you can be pretty confident that the
+actual debug interface is functioning correctly.
+
+Targets
+=======
+
+64-bit Spike
+------------
+
+`./gdbserver.py --spike --cmd $RISCV/bin/spike`
+
+32-bit Spike
+------------
+
+`./gdbserver.py --spike32 --cmd $RISCV/bin/spike`
+
+32-bit SiFive Core on Microsemi FPGA board
+------------------------------------------
+
+`./gdbserver.py --m2gl_m2s`
+
+Debug Tips
+==========
+
+You can run just a single test by specifying <class>.<function> on the command
+line, eg: `./gdbserver.py --spike --cmd $RISCV/bin/spike
+SimpleRegisterTest.test_s0`.
+Once that test has failed, you can look at gdb.log and (in this case) spike.log
+to get an idea of what might have gone wrong.
+
+You can see what spike is doing by add `-l` to the spike command, eg.:
+`./gdbserver.py --spike32 --cmd "$RISCV/bin/spike -l"
+DebugTest.test_breakpoint`. (Then look at spike.log.)
--- /dev/null
+#!/usr/bin/python
+
+import os
+import sys
+import argparse
+import testlib
+import unittest
+import tempfile
+import time
+import random
+import binascii
+
+MSTATUS_UIE = 0x00000001
+MSTATUS_SIE = 0x00000002
+MSTATUS_HIE = 0x00000004
+MSTATUS_MIE = 0x00000008
+MSTATUS_UPIE = 0x00000010
+MSTATUS_SPIE = 0x00000020
+MSTATUS_HPIE = 0x00000040
+MSTATUS_MPIE = 0x00000080
+MSTATUS_SPP = 0x00000100
+MSTATUS_HPP = 0x00000600
+MSTATUS_MPP = 0x00001800
+MSTATUS_FS = 0x00006000
+MSTATUS_XS = 0x00018000
+MSTATUS_MPRV = 0x00020000
+MSTATUS_PUM = 0x00040000
+MSTATUS_MXR = 0x00080000
+MSTATUS_VM = 0x1F000000
+MSTATUS32_SD = 0x80000000
+MSTATUS64_SD = 0x8000000000000000
+
+def ihex_line(address, record_type, data):
+ assert len(data) < 128
+ line = ":%02X%04X%02X" % (len(data), address, record_type)
+ check = len(data)
+ check += address % 256
+ check += address >> 8
+ check += record_type
+ for char in data:
+ value = ord(char)
+ check += value
+ line += "%02X" % value
+ line += "%02X\n" % ((256-check)%256)
+ return line
+
+def ihex_parse(line):
+ assert line.startswith(":")
+ line = line[1:]
+ data_len = int(line[:2], 16)
+ address = int(line[2:6], 16)
+ record_type = int(line[6:8], 16)
+ data = ""
+ for i in range(data_len):
+ data += "%c" % int(line[8+2*i:10+2*i], 16)
+ return record_type, address, data
+
+class DeleteServer(unittest.TestCase):
+ def tearDown(self):
+ del self.server
+
+class SimpleRegisterTest(DeleteServer):
+ def setUp(self):
+ self.server = target.server()
+ self.gdb = testlib.Gdb()
+ # For now gdb has to be told what the architecture is when it's not
+ # given an ELF file.
+ self.gdb.command("set arch riscv:rv%d" % target.xlen)
+
+ self.gdb.command("target extended-remote localhost:%d" % self.server.port)
+
+ # 0x13 is nop
+ self.gdb.command("p *((int*) 0x%x)=0x13" % target.ram)
+ self.gdb.command("p *((int*) 0x%x)=0x13" % (target.ram + 4))
+ self.gdb.command("p *((int*) 0x%x)=0x13" % (target.ram + 8))
+ self.gdb.p("$pc=0x%x" % target.ram)
+
+ def check_reg(self, name):
+ a = random.randrange(1<<target.xlen)
+ b = random.randrange(1<<target.xlen)
+ self.gdb.p("$%s=0x%x" % (name, a))
+ self.gdb.stepi()
+ self.assertEqual(self.gdb.p("$%s" % name), a)
+ self.gdb.p("$%s=0x%x" % (name, b))
+ self.gdb.stepi()
+ self.assertEqual(self.gdb.p("$%s" % name), b)
+
+ def test_s0(self):
+ # S0 is saved/restored in DSCRATCH
+ self.check_reg("s0")
+
+ def test_s1(self):
+ # S1 is saved/restored in Debug RAM
+ self.check_reg("s1")
+
+ def test_t0(self):
+ # T0 is not saved/restored at all
+ self.check_reg("t2")
+
+ def test_t2(self):
+ # T2 is not saved/restored at all
+ self.check_reg("t2")
+
+class SimpleMemoryTest(DeleteServer):
+ def setUp(self):
+ self.server = target.server()
+ self.gdb = testlib.Gdb()
+ self.gdb.command("set arch riscv:rv%d" % target.xlen)
+ self.gdb.command("target extended-remote localhost:%d" % self.server.port)
+
+ def access_test(self, size, data_type):
+ self.assertEqual(self.gdb.p("sizeof(%s)" % data_type),
+ size)
+ a = 0x86753095555aaaa & ((1<<(size*8))-1)
+ b = 0xdeadbeef12345678 & ((1<<(size*8))-1)
+ self.gdb.p("*((%s*)0x%x) = 0x%x" % (data_type, target.ram, a))
+ self.gdb.p("*((%s*)0x%x) = 0x%x" % (data_type, target.ram + size, b))
+ self.assertEqual(self.gdb.p("*((%s*)0x%x)" % (data_type, target.ram)), a)
+ self.assertEqual(self.gdb.p("*((%s*)0x%x)" % (data_type, target.ram + size)), b)
+
+ def test_8(self):
+ self.access_test(1, 'char')
+
+ def test_16(self):
+ self.access_test(2, 'short')
+
+ def test_32(self):
+ self.access_test(4, 'int')
+
+ def test_64(self):
+ self.access_test(8, 'long long')
+
+ def test_block(self):
+ length = 1024
+ line_length = 16
+ a = tempfile.NamedTemporaryFile(suffix=".ihex")
+ data = ""
+ for i in range(length / line_length):
+ line_data = "".join(["%c" % random.randrange(256) for _ in range(line_length)])
+ data += line_data
+ a.write(ihex_line(i * line_length, 0, line_data))
+ a.flush()
+
+ self.gdb.command("restore %s 0x%x" % (a.name, target.ram))
+ for offset in range(0, length, 19*4) + [length-4]:
+ value = self.gdb.p("*((int*)0x%x)" % (target.ram + offset))
+ written = ord(data[offset]) | \
+ (ord(data[offset+1]) << 8) | \
+ (ord(data[offset+2]) << 16) | \
+ (ord(data[offset+3]) << 24)
+ self.assertEqual(value, written)
+
+ b = tempfile.NamedTemporaryFile(suffix=".ihex")
+ self.gdb.command("dump ihex memory %s 0x%x 0x%x" % (b.name, target.ram,
+ target.ram + length))
+ for line in b:
+ record_type, address, line_data = ihex_parse(line)
+ if (record_type == 0):
+ self.assertEqual(line_data, data[address:address+len(line_data)])
+
+class InstantHaltTest(DeleteServer):
+ def setUp(self):
+ self.server = target.server()
+ self.gdb = testlib.Gdb()
+ self.gdb.command("set arch riscv:rv%d" % target.xlen)
+ self.gdb.command("target extended-remote localhost:%d" % self.server.port)
+
+ def test_instant_halt(self):
+ self.assertEqual(target.reset_vector, self.gdb.p("$pc"))
+ # mcycle and minstret have no defined reset value.
+ mstatus = self.gdb.p("$mstatus")
+ self.assertEqual(mstatus & (MSTATUS_MIE | MSTATUS_MPRV |
+ MSTATUS_VM), 0)
+
+ def test_change_pc(self):
+ """Change the PC right as we come out of reset."""
+ # 0x13 is nop
+ self.gdb.command("p *((int*) 0x%x)=0x13" % target.ram)
+ self.gdb.command("p *((int*) 0x%x)=0x13" % (target.ram + 4))
+ self.gdb.command("p *((int*) 0x%x)=0x13" % (target.ram + 8))
+ self.gdb.p("$pc=0x%x" % target.ram)
+ self.gdb.stepi()
+ self.assertEqual((target.ram + 4), self.gdb.p("$pc"))
+ self.gdb.stepi()
+ self.assertEqual((target.ram + 8), self.gdb.p("$pc"))
+
+class DebugTest(DeleteServer):
+ def setUp(self):
+ # 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.
+ self.binary = target.compile("programs/debug.c", "programs/checksum.c",
+ "programs/tiny-malloc.c", "-DDEFINE_MALLOC", "-DDEFINE_FREE")
+ 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.gdb.b("_exit")
+
+ def exit(self, expected_result = 0xc86455d4):
+ output = self.gdb.c()
+ self.assertIn("Breakpoint", output)
+ self.assertIn("_exit", output)
+ self.assertEqual(self.gdb.p("status"), expected_result)
+
+ def test_function_call(self):
+ self.gdb.b("main:start")
+ self.gdb.c()
+ text = "Howdy, Earth!"
+ gdb_length = self.gdb.p('strlen("%s")' % text)
+ self.assertEqual(gdb_length, len(text))
+ self.exit()
+
+ def test_change_string(self):
+ text = "This little piggy went to the market."
+ self.gdb.b("main:start")
+ self.gdb.c()
+ self.gdb.p('fox = "%s"' % text)
+ self.exit(0x43b497b8)
+
+ def test_turbostep(self):
+ """Single step a bunch of times."""
+ self.gdb.command("p i=0");
+ last_pc = None
+ advances = 0
+ jumps = 0
+ for _ in range(100):
+ self.gdb.stepi()
+ pc = self.gdb.p("$pc")
+ self.assertNotEqual(last_pc, pc)
+ if (last_pc and pc > last_pc and pc - last_pc <= 4):
+ advances += 1
+ else:
+ jumps += 1
+ last_pc = pc
+ # Some basic sanity that we're not running between breakpoints or
+ # something.
+ self.assertGreater(jumps, 10)
+ self.assertGreater(advances, 50)
+
+ def test_exit(self):
+ self.exit()
+
+ def test_symbols(self):
+ self.gdb.b("main")
+ self.gdb.b("rot13")
+ output = self.gdb.c()
+ self.assertIn(", main ", output)
+ output = self.gdb.c()
+ self.assertIn(", rot13 ", output)
+
+ def test_breakpoint(self):
+ self.gdb.b("rot13")
+ # The breakpoint should be hit exactly 2 times.
+ for i in range(2):
+ output = self.gdb.c()
+ self.gdb.p("$pc")
+ self.assertIn("Breakpoint ", output)
+ #TODO self.assertIn("rot13 ", output)
+ self.exit()
+
+ def test_hwbp_1(self):
+ if target.instruction_hardware_breakpoint_count < 1:
+ return
+
+ self.gdb.hbreak("rot13")
+ # The breakpoint should be hit exactly 2 times.
+ for i in range(2):
+ output = self.gdb.c()
+ self.gdb.p("$pc")
+ self.assertIn("Breakpoint ", output)
+ #TODO self.assertIn("rot13 ", output)
+ self.exit()
+
+ def test_hwbp_2(self):
+ if target.instruction_hardware_breakpoint_count < 2:
+ return
+
+ self.gdb.hbreak("main")
+ self.gdb.hbreak("rot13")
+ # We should hit 3 breakpoints.
+ for i in range(3):
+ output = self.gdb.c()
+ self.gdb.p("$pc")
+ self.assertIn("Breakpoint ", output)
+ #TODO self.assertIn("rot13 ", output)
+ self.exit()
+
+ def test_too_many_hwbp(self):
+ for i in range(30):
+ self.gdb.hbreak("*rot13 + %d" % (i * 4))
+
+ output = self.gdb.c()
+ self.assertIn("Cannot insert hardware breakpoint", output)
+ # Clean up, otherwise the hardware breakpoints stay set and future
+ # tests may fail.
+ self.gdb.command("D")
+
+ def test_registers(self):
+ # Get to a point in the code where some registers have actually been
+ # used.
+ self.gdb.b("rot13")
+ self.gdb.c()
+ self.gdb.c()
+ # Try both forms to test gdb.
+ for cmd in ("info all-registers", "info registers all"):
+ output = self.gdb.command(cmd)
+ self.assertNotIn("Could not", output)
+ for reg in ('zero', 'ra', 'sp', 'gp', 'tp'):
+ self.assertIn(reg, output)
+
+ #TODO
+ # mcpuid is one of the few registers that should have the high bit set
+ # (for rv64).
+ # Leave this commented out until gdb and spike agree on the encoding of
+ # mcpuid (which is going to be renamed to misa in any case).
+ #self.assertRegexpMatches(output, ".*mcpuid *0x80")
+
+ #TODO:
+ # The instret register should always be changing.
+ #last_instret = None
+ #for _ in range(5):
+ # instret = self.gdb.p("$instret")
+ # self.assertNotEqual(instret, last_instret)
+ # last_instret = instret
+ # self.gdb.stepi()
+
+ self.exit()
+
+ def test_interrupt(self):
+ """Sending gdb ^C while the program is running should cause it to halt."""
+ self.gdb.b("main:start")
+ self.gdb.c()
+ self.gdb.p("i=123");
+ self.gdb.c(wait=False)
+ time.sleep(0.1)
+ output = self.gdb.interrupt()
+ #TODO: assert "main" in output
+ self.assertGreater(self.gdb.p("j"), 10)
+ self.gdb.p("i=0");
+ self.exit()
+
+class StepTest(DeleteServer):
+ def setUp(self):
+ self.binary = target.compile("programs/step.S")
+ 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.gdb.b("main")
+ self.gdb.c()
+
+ def test_step(self):
+ main = self.gdb.p("$pc")
+ for expected in (4, 8, 0xc, 0x10, 0x18, 0x1c, 0x28, 0x20, 0x2c, 0x2c):
+ self.gdb.stepi()
+ pc = self.gdb.p("$pc")
+ self.assertEqual("%x" % pc, "%x" % (expected + main))
+
+class RegsTest(DeleteServer):
+ def setUp(self):
+ self.binary = target.compile("programs/regs.S")
+ 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.gdb.b("main")
+ self.gdb.b("handle_trap")
+ self.gdb.c()
+
+ def test_write_gprs(self):
+ regs = [("x%d" % n) for n in range(2, 32)]
+
+ self.gdb.p("$pc=write_regs")
+ for i, r in enumerate(regs):
+ self.gdb.command("p $%s=%d" % (r, (0xdeadbeef<<i)+17))
+ self.gdb.command("p $x1=data")
+ self.gdb.command("b all_done")
+ output = self.gdb.c()
+ self.assertIn("Breakpoint ", output)
+
+ # Just to get this data in the log.
+ self.gdb.command("x/30gx data")
+ self.gdb.command("info registers")
+ for n in range(len(regs)):
+ self.assertEqual(self.gdb.x("data+%d" % (8*n), 'g'),
+ ((0xdeadbeef<<n)+17) & ((1<<target.xlen)-1))
+
+ def test_write_csrs(self):
+ # As much a test of gdb as of the simulator.
+ self.gdb.p("$mscratch=0")
+ self.gdb.stepi()
+ self.assertEqual(self.gdb.p("$mscratch"), 0)
+ self.gdb.p("$mscratch=123")
+ self.gdb.stepi()
+ self.assertEqual(self.gdb.p("$mscratch"), 123)
+
+ self.gdb.command("p $pc=write_regs")
+ self.gdb.command("p $a0=data")
+ self.gdb.command("b all_done")
+ self.gdb.command("c")
+
+ self.assertEqual(123, self.gdb.p("$mscratch"))
+ self.assertEqual(123, self.gdb.p("$x1"))
+ self.assertEqual(123, self.gdb.p("$csr832"))
+
+class DownloadTest(DeleteServer):
+ def setUp(self):
+ length = min(2**20, target.ram_size - 2048)
+ download_c = tempfile.NamedTemporaryFile(prefix="download_", suffix=".c")
+ download_c.write("#include <stdint.h>\n")
+ download_c.write("unsigned int crc32a(uint8_t *message, unsigned int size);\n")
+ download_c.write("uint32_t length = %d;\n" % length)
+ download_c.write("uint8_t d[%d] = {\n" % length)
+ self.crc = 0
+ for i in range(length / 16):
+ download_c.write(" /* 0x%04x */ " % (i * 16));
+ for _ in range(16):
+ value = random.randrange(1<<8)
+ download_c.write("%d, " % value)
+ self.crc = binascii.crc32("%c" % value, self.crc)
+ download_c.write("\n");
+ download_c.write("};\n");
+ download_c.write("uint8_t *data = &d[0];\n");
+ download_c.write("uint32_t main() { return crc32a(data, length); }\n")
+ download_c.flush()
+
+ if self.crc < 0:
+ self.crc += 2**32
+
+ self.binary = target.compile(download_c.name, "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)
+
+ def test_download(self):
+ output = self.gdb.load()
+ self.gdb.command("b _exit")
+ self.gdb.c()
+ self.assertEqual(self.gdb.p("status"), self.crc)
+
+class MprvTest(DeleteServer):
+ def setUp(self):
+ self.binary = target.compile("programs/mprv.S")
+ 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()
+
+ def test_mprv(self):
+ """Test that the debugger can access memory when MPRV is set."""
+ self.gdb.c(wait=False)
+ time.sleep(0.5)
+ self.gdb.interrupt()
+ output = self.gdb.command("p/x *(int*)(((char*)&data)-0x80000000)")
+ self.assertIn("0xbead", output)
+
+class Target(object):
+ directory = None
+
+ def server(self):
+ raise NotImplementedError
+
+ def compile(self, *sources):
+ binary_name = "%s_%s" % (
+ self.name,
+ os.path.basename(os.path.splitext(sources[0])[0]))
+ if parsed.isolate:
+ self.temporary_binary = tempfile.NamedTemporaryFile(
+ prefix=binary_name + "_")
+ binary_name = self.temporary_binary.name
+ testlib.compile(sources +
+ ("programs/entry.S", "programs/init.c",
+ "-I", "../env",
+ "-T", "targets/%s/link.lds" % (self.directory or self.name),
+ "-nostartfiles",
+ "-mcmodel=medany",
+ "-o", binary_name),
+ xlen=self.xlen)
+ return binary_name
+
+class Spike64Target(Target):
+ name = "spike"
+ xlen = 64
+ ram = 0x80010000
+ ram_size = 5 * 1024 * 1024
+ instruction_hardware_breakpoint_count = 0
+ reset_vector = 0x1000
+
+ def server(self):
+ return testlib.Spike(parsed.cmd, halted=True)
+
+class Spike32Target(Target):
+ name = "spike32"
+ directory = "spike"
+ xlen = 32
+ ram = 0x80010000
+ ram_size = 5 * 1024 * 1024
+ instruction_hardware_breakpoint_count = 0
+ reset_vector = 0x1000
+
+ def server(self):
+ return testlib.Spike(parsed.cmd, halted=True, xlen=32)
+
+class MicroSemiTarget(Target):
+ name = "m2gl_m2s"
+ xlen = 32
+ ram = 0x80000000
+ ram_size = 16 * 1024
+ instruction_hardware_breakpoint_count = 2
+
+ def server(self):
+ return testlib.Openocd(cmd=parsed.cmd,
+ config="targets/%s/openocd.cfg" % self.name)
+
+targets = [
+ Spike32Target,
+ Spike64Target,
+ MicroSemiTarget
+ ]
+
+def main():
+ parser = argparse.ArgumentParser(
+ epilog="""
+ Example command line from the real world:
+ Run all RegsTest cases against a MicroSemi m2gl_m2s board, with custom openocd command:
+ ./gdbserver.py --m2gl_m2s --cmd "$HOME/SiFive/openocd/src/openocd -s $HOME/SiFive/openocd/tcl -d" -- -vf RegsTest
+ """)
+ group = parser.add_mutually_exclusive_group(required=True)
+ for t in targets:
+ group.add_argument("--%s" % t.name, action="store_const", const=t,
+ dest="target")
+ parser.add_argument("--cmd",
+ help="The command to use to start the debug server.")
+ parser.add_argument("--isolate", action="store_true",
+ help="Try to run in such a way that multiple instances can run at "
+ "the same time. This may make it harder to debug a failure if it "
+ "does occur.")
+ parser.add_argument("unittest", nargs="*")
+ global parsed
+ parsed = parser.parse_args()
+
+ global target
+ target = parsed.target()
+ unittest.main(argv=[sys.argv[0]] + parsed.unittest)
+
+# TROUBLESHOOTING TIPS
+# If a particular test fails, run just that one test, eg.:
+# ./tests/gdbserver.py MprvTest.test_mprv
+# Then inspect gdb.log and spike.log to see what happened in more detail.
+
+if __name__ == '__main__':
+ sys.exit(main())
--- /dev/null
+#include <stdint.h>
+
+// 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);
+}
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+unsigned int crc32a(uint8_t *message, unsigned int size);
+
+void rot13(char *buf)
+{
+ 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++;
+ }
+}
+
+size_t strlen(const char *buf)
+{
+ int len = 0;
+ while (buf[len])
+ len++;
+ return len;
+}
+
+extern void *__malloc_freelist;
+
+int main()
+{
+ __malloc_freelist = 0;
+
+ volatile int i = 0;
+ int j = 0;
+ char *fox = "The quick brown fox jumps of the lazy dog.";
+ unsigned int checksum = 0;
+
+start:
+ while (i)
+ j++;
+
+ rot13(fox);
+ checksum ^= crc32a(fox, strlen(fox));
+ rot13(fox);
+ checksum ^= crc32a(fox, strlen(fox));
+
+ return checksum;
+}
--- /dev/null
+#ifndef ENTRY_S
+#define ENTRY_S
+
+#include "encoding.h"
+
+#define STACK_SIZE 512
+
+#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
+
+ // Fill the stack with data so we can see if it was overrun.
+ .align 4
+stack_bottom:
+ .fill STACK_SIZE/4, 4, 0x22446688
+stack_top:
+#endif
--- /dev/null
+int main(void);
+
+void handle_trap(unsigned int mcause, unsigned int mepc, unsigned int sp)
+{
+ while (1)
+ ;
+}
+
+void _exit(int status)
+{
+ // 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());
+}
--- /dev/null
+#include "../../env/encoding.h"
+#define PGSHIFT 12
+
+ .global main
+
+ .section .text
+main:
+ # Set up a page table entry that maps 0x0... to 0x8...
+ la t0, page_table
+ srli t0, t0, PGSHIFT
+ csrw CSR_SPTBR, t0
+
+ # update mstatus
+ csrr t1, CSR_MSTATUS
+#ifdef __riscv32
+ li t0, (MSTATUS_MPRV | (VM_SV32 << 24))
+#else
+ li t0, (MSTATUS_MPRV | (VM_SV39 << 24))
+#endif
+ #li t0, ((VM_SV39 << 24))
+ or t1, t0, t1
+ csrw CSR_MSTATUS, t1
+
+ la t0, (loop - 0x80000000)
+ csrw CSR_MEPC, t0
+
+ # Exit supervisor mode, entering user mode at loop.
+ mret
+
+loop:
+ la t0, data
+ lw t1, 0(t0)
+ j loop
+
+ .section .data
+data:
+ .word 0xbead
+
+ .balign 0x1000
+page_table:
+#ifdef __riscv32
+ .word ((0x80000000 >> 2) | PTE_V | PTE_R | PTE_W | PTE_X | PTE_G | PTE_U)
+#else
+ .word ((0x80000000 >> 2) | PTE_V | PTE_R | PTE_W | PTE_X | PTE_G | PTE_U)
+ .word 0
+#endif
--- /dev/null
+#ifdef __riscv64
+# define LREG ld
+# define SREG sd
+# define REGBYTES 8
+#else
+# define LREG lw
+# define SREG sw
+# define REGBYTES 4
+#endif
+
+#include "../../env/encoding.h"
+
+ .global main
+main:
+ nop
+ j main
+
+write_regs:
+ SREG x2, 0(x1)
+ SREG x3, 8(x1)
+ SREG x4, 16(x1)
+ SREG x5, 24(x1)
+ SREG x6, 32(x1)
+ SREG x7, 40(x1)
+ SREG x8, 48(x1)
+ SREG x9, 56(x1)
+ SREG x10, 64(x1)
+ SREG x11, 72(x1)
+ SREG x12, 80(x1)
+ SREG x13, 88(x1)
+ SREG x14, 96(x1)
+ SREG x15, 104(x1)
+ SREG x16, 112(x1)
+ SREG x17, 120(x1)
+ SREG x18, 128(x1)
+ SREG x19, 136(x1)
+ SREG x20, 144(x1)
+ SREG x21, 152(x1)
+ SREG x22, 160(x1)
+ SREG x23, 168(x1)
+ SREG x24, 176(x1)
+ SREG x25, 184(x1)
+ SREG x26, 192(x1)
+ SREG x27, 200(x1)
+ SREG x28, 208(x1)
+ SREG x29, 216(x1)
+ SREG x30, 224(x1)
+ SREG x31, 232(x1)
+
+ csrr x1, CSR_MSCRATCH
+
+all_done:
+ j all_done
+
+ .balign 16
+data:
+ .fill 64, 8, 0
--- /dev/null
+ .global _start
+
+_start:
+ la sp, stack_end
+ jal main
+done:
+ j done
+
+ .data
+stack:
+ .fill 4096, 1, 0
+stack_end:
--- /dev/null
+// Test stepping over a variety of instructions.
+
+ .global main
+
+main:
+ la t0, trap_entry // 0, 4
+ csrw mtvec, t0 // 0x8
+
+ li t0, 5 // 0xc
+ beq zero, zero, one // 0x10
+ nop // 0x14
+one:
+ beq zero, t0, one // 0x18
+ jal two // 0x1c
+
+three:
+ .word 0 // 0x20
+ nop // 0x24
+
+two:
+ ret // 0x28
+
+trap_entry:
+ j trap_entry // 0x2c
--- /dev/null
+// https://github.com/32bitmicro/newlib-nano-1.0/blob/master/newlib/libc/machine/xstormy16/tiny-malloc.c
+
+/* A replacement malloc with:
+ - Much reduced code size;
+ - Smaller RAM footprint;
+ - The ability to handle downward-growing heaps;
+ but
+ - Slower;
+ - Probably higher memory fragmentation;
+ - Doesn't support threads (but, if it did support threads,
+ it wouldn't need a global lock, only a compare-and-swap instruction);
+ - Assumes the maximum alignment required is the alignment of a pointer;
+ - Assumes that memory is already there and doesn't need to be allocated.
+
+* Synopsis of public routines
+
+ malloc(size_t n);
+ Return a pointer to a newly allocated chunk of at least n bytes, or null
+ if no space is available.
+ free(void* p);
+ Release the chunk of memory pointed to by p, or no effect if p is null.
+ realloc(void* p, size_t n);
+ Return a pointer to a chunk of size n that contains the same data
+ as does chunk p up to the minimum of (n, p's size) bytes, or null
+ if no space is available. The returned pointer may or may not be
+ the same as p. If p is null, equivalent to malloc. Unless the
+ #define REALLOC_ZERO_BYTES_FREES below is set, realloc with a
+ size argument of zero (re)allocates a minimum-sized chunk.
+ memalign(size_t alignment, size_t n);
+ Return a pointer to a newly allocated chunk of n bytes, aligned
+ in accord with the alignment argument, which must be a power of
+ two. Will fail if 'alignment' is too large.
+ calloc(size_t unit, size_t quantity);
+ Returns a pointer to quantity * unit bytes, with all locations
+ set to zero.
+ cfree(void* p);
+ Equivalent to free(p).
+ malloc_trim(size_t pad);
+ Release all but pad bytes of freed top-most memory back
+ to the system. Return 1 if successful, else 0.
+ malloc_usable_size(void* p);
+ Report the number usable allocated bytes associated with allocated
+ chunk p. This may or may not report more bytes than were requested,
+ due to alignment and minimum size constraints.
+ malloc_stats();
+ Prints brief summary statistics on stderr.
+ mallinfo()
+ Returns (by copy) a struct containing various summary statistics.
+ mallopt(int parameter_number, int parameter_value)
+ Changes one of the tunable parameters described below. Returns
+ 1 if successful in changing the parameter, else 0. Actually, returns 0
+ always, as no parameter can be changed.
+*/
+
+#ifdef __xstormy16__
+#define MALLOC_DIRECTION -1
+#endif
+
+#ifndef MALLOC_DIRECTION
+#define MALLOC_DIRECTION 1
+#endif
+
+#include <stddef.h>
+
+void* malloc(size_t);
+void free(void*);
+void* realloc(void*, size_t);
+void* memalign(size_t, size_t);
+void* valloc(size_t);
+void* pvalloc(size_t);
+void* calloc(size_t, size_t);
+void cfree(void*);
+int malloc_trim(size_t);
+size_t malloc_usable_size(void*);
+void malloc_stats(void);
+int mallopt(int, int);
+struct mallinfo mallinfo(void);
+
+typedef struct freelist_entry {
+ size_t size;
+ struct freelist_entry *next;
+} *fle;
+
+extern void * __malloc_end;
+extern fle __malloc_freelist;
+
+/* Return the number of bytes that need to be added to X to make it
+ aligned to an ALIGN boundary. ALIGN must be a power of 2. */
+#define M_ALIGN(x, align) (-(size_t)(x) & ((align) - 1))
+
+/* Return the number of bytes that need to be subtracted from X to make it
+ aligned to an ALIGN boundary. ALIGN must be a power of 2. */
+#define M_ALIGN_SUB(x, align) ((size_t)(x) & ((align) - 1))
+
+extern char *__malloc_start;
+
+/* This is the minimum gap allowed between __malloc_end and the top of
+ the stack. This is only checked for when __malloc_end is
+ decreased; if instead the stack grows into the heap, silent data
+ corruption will result. */
+#define MALLOC_MINIMUM_GAP 32
+
+#ifdef __xstormy16__
+register void * stack_pointer asm ("r15");
+#define MALLOC_LIMIT stack_pointer
+#else
+#define MALLOC_LIMIT __builtin_frame_address (0)
+#endif
+
+#if MALLOC_DIRECTION < 0
+#define CAN_ALLOC_P(required) \
+ (((size_t) __malloc_end - (size_t)MALLOC_LIMIT \
+ - MALLOC_MINIMUM_GAP) >= (required))
+#else
+#define CAN_ALLOC_P(required) \
+ (((size_t)MALLOC_LIMIT - (size_t) __malloc_end \
+ - MALLOC_MINIMUM_GAP) >= (required))
+#endif
+
+/* real_size is the size we actually have to allocate, allowing for
+ overhead and alignment. */
+#define REAL_SIZE(sz) \
+ ((sz) < sizeof (struct freelist_entry) - sizeof (size_t) \
+ ? sizeof (struct freelist_entry) \
+ : sz + sizeof (size_t) + M_ALIGN(sz, sizeof (size_t)))
+
+#ifdef DEFINE_MALLOC
+
+void * __malloc_end = &__malloc_start;
+fle __malloc_freelist;
+
+void *
+malloc (size_t sz)
+{
+ fle *nextfree;
+ fle block;
+
+ /* real_size is the size we actually have to allocate, allowing for
+ overhead and alignment. */
+ size_t real_size = REAL_SIZE (sz);
+
+ /* Look for the first block on the freelist that is large enough. */
+ for (nextfree = &__malloc_freelist;
+ *nextfree;
+ nextfree = &(*nextfree)->next)
+ {
+ block = *nextfree;
+
+ if (block->size >= real_size)
+ {
+ /* If the block found is just the right size, remove it from
+ the free list. Otherwise, split it. */
+ if (block->size < real_size + sizeof (struct freelist_entry))
+ {
+ *nextfree = block->next;
+ return (void *)&block->next;
+ }
+ else
+ {
+ size_t newsize = block->size - real_size;
+ fle newnext = block->next;
+ *nextfree = (fle)((size_t)block + real_size);
+ (*nextfree)->size = newsize;
+ (*nextfree)->next = newnext;
+ goto done;
+ }
+ }
+
+ /* If this is the last block on the freelist, and it was too small,
+ enlarge it. */
+ if (! block->next
+ && __malloc_end == (void *)((size_t)block + block->size))
+ {
+ size_t moresize = real_size - block->size;
+ if (! CAN_ALLOC_P (moresize))
+ return NULL;
+
+ *nextfree = NULL;
+ if (MALLOC_DIRECTION < 0)
+ {
+ block = __malloc_end = (void *)((size_t)block - moresize);
+ }
+ else
+ {
+ __malloc_end = (void *)((size_t)block + real_size);
+ }
+
+ goto done;
+ }
+ }
+
+ /* No free space at the end of the free list. Allocate new space
+ and use that. */
+
+ if (! CAN_ALLOC_P (real_size))
+ return NULL;
+
+ if (MALLOC_DIRECTION > 0)
+ {
+ block = __malloc_end;
+ __malloc_end = (void *)((size_t)__malloc_end + real_size);
+ }
+ else
+ {
+ block = __malloc_end = (void *)((size_t)__malloc_end - real_size);
+ }
+ done:
+ block->size = real_size;
+ return (void *)&block->next;
+}
+
+#endif
+
+#ifdef DEFINE_FREE
+
+void
+free (void *block_p)
+{
+ fle *nextfree;
+ fle block = (fle)((size_t) block_p - offsetof (struct freelist_entry, next));
+
+ if (block_p == NULL)
+ return;
+
+ /* Look on the freelist to see if there's a free block just before
+ or just after this block. */
+ for (nextfree = &__malloc_freelist;
+ *nextfree;
+ nextfree = &(*nextfree)->next)
+ {
+ fle thisblock = *nextfree;
+ if ((size_t)thisblock + thisblock->size == (size_t) block)
+ {
+ thisblock->size += block->size;
+ if (MALLOC_DIRECTION > 0
+ && thisblock->next
+ && (size_t) block + block->size == (size_t) thisblock->next)
+ {
+ thisblock->size += thisblock->next->size;
+ thisblock->next = thisblock->next->next;
+ }
+ return;
+ }
+ else if ((size_t) thisblock == (size_t) block + block->size)
+ {
+ if (MALLOC_DIRECTION < 0
+ && thisblock->next
+ && (size_t) block == ((size_t) thisblock->next
+ + thisblock->next->size))
+ {
+ *nextfree = thisblock->next;
+ thisblock->next->size += block->size + thisblock->size;
+ }
+ else
+ {
+ block->size += thisblock->size;
+ block->next = thisblock->next;
+ *nextfree = block;
+ }
+ return;
+ }
+ else if ((MALLOC_DIRECTION > 0
+ && (size_t) thisblock > (size_t) block)
+ || (MALLOC_DIRECTION < 0
+ && (size_t) thisblock < (size_t) block))
+ break;
+ }
+
+ block->next = *nextfree;
+ *nextfree = block;
+ return;
+}
+#endif
+
+#ifdef DEFINE_REALLOC
+void *
+realloc (void *block_p, size_t sz)
+{
+ fle block = (fle)((size_t) block_p - offsetof (struct freelist_entry, next));
+ size_t real_size = REAL_SIZE (sz);
+ size_t old_real_size;
+
+ if (block_p == NULL)
+ return malloc (sz);
+
+ old_real_size = block->size;
+
+ /* Perhaps we need to allocate more space. */
+ if (old_real_size < real_size)
+ {
+ void *result;
+ size_t old_size = old_real_size - sizeof (size_t);
+
+ /* Need to allocate, copy, and free. */
+ result = malloc (sz);
+ if (result == NULL)
+ return NULL;
+ memcpy (result, block_p, old_size < sz ? old_size : sz);
+ free (block_p);
+ return result;
+ }
+ /* Perhaps we can free some space. */
+ if (old_real_size - real_size >= sizeof (struct freelist_entry))
+ {
+ fle newblock = (fle)((size_t)block + real_size);
+ block->size = real_size;
+ newblock->size = old_real_size - real_size;
+ free (&newblock->next);
+ }
+ return block_p;
+}
+#endif
+
+#ifdef DEFINE_CALLOC
+void *
+calloc (size_t n, size_t elem_size)
+{
+ void *result;
+ size_t sz = n * elem_size;
+ result = malloc (sz);
+ if (result != NULL)
+ memset (result, 0, sz);
+ return result;
+}
+#endif
+
+#ifdef DEFINE_CFREE
+void
+cfree (void *p)
+{
+ free (p);
+}
+#endif
+
+#ifdef DEFINE_MEMALIGN
+void *
+memalign (size_t align, size_t sz)
+{
+ fle *nextfree;
+ fle block;
+
+ /* real_size is the size we actually have to allocate, allowing for
+ overhead and alignment. */
+ size_t real_size = REAL_SIZE (sz);
+
+ /* Some sanity checking on 'align'. */
+ if ((align & (align - 1)) != 0
+ || align <= 0)
+ return NULL;
+
+ /* Look for the first block on the freelist that is large enough. */
+ /* One tricky part is this: We want the result to be a valid pointer
+ to free. That means that there has to be room for a size_t
+ before the block. If there's additional space before the block,
+ it should go on the freelist, or it'll be lost---we could add it
+ to the size of the block before it in memory, but finding the
+ previous block is expensive. */
+ for (nextfree = &__malloc_freelist;
+ ;
+ nextfree = &(*nextfree)->next)
+ {
+ size_t before_size;
+ size_t old_size;
+
+ /* If we've run out of free blocks, allocate more space. */
+ if (! *nextfree)
+ {
+ old_size = real_size;
+ if (MALLOC_DIRECTION < 0)
+ {
+ old_size += M_ALIGN_SUB (((size_t)__malloc_end
+ - old_size + sizeof (size_t)),
+ align);
+ if (! CAN_ALLOC_P (old_size))
+ return NULL;
+ block = __malloc_end = (void *)((size_t)__malloc_end - old_size);
+ }
+ else
+ {
+ block = __malloc_end;
+ old_size += M_ALIGN ((size_t)__malloc_end + sizeof (size_t),
+ align);
+ if (! CAN_ALLOC_P (old_size))
+ return NULL;
+ __malloc_end = (void *)((size_t)__malloc_end + old_size);
+ }
+ *nextfree = block;
+ block->size = old_size;
+ block->next = NULL;
+ }
+ else
+ {
+ block = *nextfree;
+ old_size = block->size;
+ }
+
+
+ before_size = M_ALIGN (&block->next, align);
+ if (before_size != 0)
+ before_size = sizeof (*block) + M_ALIGN (&(block+1)->next, align);
+
+ /* If this is the last block on the freelist, and it is too small,
+ enlarge it. */
+ if (! block->next
+ && old_size < real_size + before_size
+ && __malloc_end == (void *)((size_t)block + block->size))
+ {
+ if (MALLOC_DIRECTION < 0)
+ {
+ size_t moresize = real_size - block->size;
+ moresize += M_ALIGN_SUB ((size_t)&block->next - moresize, align);
+ if (! CAN_ALLOC_P (moresize))
+ return NULL;
+ block = __malloc_end = (void *)((size_t)block - moresize);
+ block->next = NULL;
+ block->size = old_size = old_size + moresize;
+ before_size = 0;
+ }
+ else
+ {
+ if (! CAN_ALLOC_P (before_size + real_size - block->size))
+ return NULL;
+ __malloc_end = (void *)((size_t)block + before_size + real_size);
+ block->size = old_size = before_size + real_size;
+ }
+
+ /* Two out of the four cases below will now be possible; which
+ two depends on MALLOC_DIRECTION. */
+ }
+
+ if (old_size >= real_size + before_size)
+ {
+ /* This block will do. If there needs to be space before it,
+ split the block. */
+ if (before_size != 0)
+ {
+ fle old_block = block;
+
+ old_block->size = before_size;
+ block = (fle)((size_t)block + before_size);
+
+ /* If there's no space after the block, we're now nearly
+ done; just make a note of the size required.
+ Otherwise, we need to create a new free space block. */
+ if (old_size - before_size
+ <= real_size + sizeof (struct freelist_entry))
+ {
+ block->size = old_size - before_size;
+ return (void *)&block->next;
+ }
+ else
+ {
+ fle new_block;
+ new_block = (fle)((size_t)block + real_size);
+ new_block->size = old_size - before_size - real_size;
+ if (MALLOC_DIRECTION > 0)
+ {
+ new_block->next = old_block->next;
+ old_block->next = new_block;
+ }
+ else
+ {
+ new_block->next = old_block;
+ *nextfree = new_block;
+ }
+ goto done;
+ }
+ }
+ else
+ {
+ /* If the block found is just the right size, remove it from
+ the free list. Otherwise, split it. */
+ if (old_size <= real_size + sizeof (struct freelist_entry))
+ {
+ *nextfree = block->next;
+ return (void *)&block->next;
+ }
+ else
+ {
+ size_t newsize = old_size - real_size;
+ fle newnext = block->next;
+ *nextfree = (fle)((size_t)block + real_size);
+ (*nextfree)->size = newsize;
+ (*nextfree)->next = newnext;
+ goto done;
+ }
+ }
+ }
+ }
+
+ done:
+ block->size = real_size;
+ return (void *)&block->next;
+}
+#endif
+
+#ifdef DEFINE_VALLOC
+void *
+valloc (size_t sz)
+{
+ return memalign (128, sz);
+}
+#endif
+#ifdef DEFINE_PVALLOC
+void *
+pvalloc (size_t sz)
+{
+ return memalign (128, sz + M_ALIGN (sz, 128));
+}
+#endif
+
+#ifdef DEFINE_MALLINFO
+#include "malloc.h"
+
+struct mallinfo
+mallinfo (void)
+{
+ struct mallinfo r;
+ fle fr;
+ size_t free_size;
+ size_t total_size;
+ size_t free_blocks;
+
+ memset (&r, 0, sizeof (r));
+
+ free_size = 0;
+ free_blocks = 0;
+ for (fr = __malloc_freelist; fr; fr = fr->next)
+ {
+ free_size += fr->size;
+ free_blocks++;
+ if (! fr->next)
+ {
+ int atend;
+ if (MALLOC_DIRECTION > 0)
+ atend = (void *)((size_t)fr + fr->size) == __malloc_end;
+ else
+ atend = (void *)fr == __malloc_end;
+ if (atend)
+ r.keepcost = fr->size;
+ }
+ }
+
+ if (MALLOC_DIRECTION > 0)
+ total_size = (char *)__malloc_end - (char *)&__malloc_start;
+ else
+ total_size = (char *)&__malloc_start - (char *)__malloc_end;
+
+#ifdef DEBUG
+ /* Fixme: should walk through all the in-use blocks and see if
+ they're valid. */
+#endif
+
+ r.arena = total_size;
+ r.fordblks = free_size;
+ r.uordblks = total_size - free_size;
+ r.ordblks = free_blocks;
+ return r;
+}
+#endif
+
+#ifdef DEFINE_MALLOC_STATS
+#include "malloc.h"
+#include <stdio.h>
+
+void
+malloc_stats(void)
+{
+ struct mallinfo i;
+ FILE *fp;
+
+ fp = stderr;
+ i = mallinfo();
+ fprintf (fp, "malloc has reserved %u bytes between %p and %p\n",
+ i.arena, &__malloc_start, __malloc_end);
+ fprintf (fp, "there are %u bytes free in %u chunks\n",
+ i.fordblks, i.ordblks);
+ fprintf (fp, "of which %u bytes are at the end of the reserved space\n",
+ i.keepcost);
+ fprintf (fp, "and %u bytes are in use.\n", i.uordblks);
+}
+#endif
+
+#ifdef DEFINE_MALLOC_USABLE_SIZE
+size_t
+malloc_usable_size (void *block_p)
+{
+ fle block = (fle)((size_t) block_p - offsetof (struct freelist_entry, next));
+ return block->size - sizeof (size_t);
+}
+#endif
+
+#ifdef DEFINE_MALLOPT
+int
+mallopt (int n, int v)
+{
+ (void)n; (void)v;
+ return 0;
+}
+#endif
--- /dev/null
+OUTPUT_ARCH( "riscv" )
+
+SECTIONS
+{
+ . = 0x80000000;
+ .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) }
+
+ __malloc_start = .;
+ . = . + 512;
+
+ /* End of uninitalized data segement */
+ _end = .;
+}
--- /dev/null
+adapter_khz 10000
+
+source [find interface/ftdi/olimex-arm-usb-tiny-h.cfg]
+
+set _CHIPNAME riscv
+jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME riscv -chain-position $_TARGETNAME
+
+#reset_config trst_and_srst separate
+# Stupid long so I can see the LEDs
+#adapter_nsrst_delay 2000
+#jtag_ntrst_delay 1000
+#
+init
+#reset
+
+halt
--- /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) }
+
+ __malloc_start = .;
+ . = . + 512;
+
+ /* End of uninitalized data segement */
+ _end = .;
+}
--- /dev/null
+import os.path
+import pexpect
+import shlex
+import subprocess
+import tempfile
+import testlib
+import unittest
+
+# Note that gdb comes with its own testsuite. I was unable to figure out how to
+# run that testsuite against the spike simulator.
+
+def find_file(path):
+ for directory in (os.getcwd(), os.path.dirname(testlib.__file__)):
+ fullpath = os.path.join(directory, path)
+ if os.path.exists(fullpath):
+ return fullpath
+ return None
+
+def compile(args, xlen=32):
+ cc = os.path.expandvars("$RISCV/bin/riscv%d-unknown-elf-gcc" % xlen)
+ cmd = [cc, "-g"]
+ for arg in args:
+ found = find_file(arg)
+ if found:
+ cmd.append(found)
+ else:
+ cmd.append(arg)
+ cmd = " ".join(cmd)
+ result = os.system(cmd)
+ assert result == 0, "%r failed" % cmd
+
+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):
+ def __init__(self, cmd, binary=None, halted=False, with_gdb=True, timeout=None,
+ xlen=64):
+ """Launch spike. Return tuple of its process and the port it's running on."""
+ if cmd:
+ cmd = shlex.split(cmd)
+ else:
+ cmd = ["spike"]
+ if (xlen == 32):
+ cmd += ["--isa", "RV32"]
+
+ if timeout:
+ cmd = ["timeout", str(timeout)] + cmd
+
+ 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")
+ logfile.write("+ %s\n" % " ".join(cmd))
+ logfile.flush()
+ 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 Openocd(object):
+ def __init__(self, cmd=None, config=None, debug=False):
+ if cmd:
+ cmd = shlex.split(cmd)
+ else:
+ cmd = ["openocd"]
+ if config:
+ cmd += ["-f", find_file(config)]
+ if debug:
+ cmd.append("-d")
+ logfile = open("openocd.log", "w")
+ logfile.write("+ %s\n" % " ".join(cmd))
+ self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=logfile,
+ stderr=logfile)
+ # TODO: Pick a random port
+ self.port = 3333
+
+ def __del__(self):
+ try:
+ self.process.kill()
+ self.process.wait()
+ except OSError:
+ pass
+
+class Gdb(object):
+ def __init__(self,
+ path=os.path.expandvars("$RISCV/bin/riscv64-unknown-elf-gdb")):
+ self.child = pexpect.spawn(path)
+ self.child.logfile = file("gdb.log", "w")
+ self.child.logfile.write("+ %s\n" % path)
+ 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")
+
+ def wait(self):
+ """Wait for prompt."""
+ self.child.expect("\(gdb\)")
+
+ def command(self, command, timeout=-1):
+ self.child.sendline(command)
+ self.child.expect("\n", timeout=timeout)
+ self.child.expect("\(gdb\)", timeout=timeout)
+ return self.child.before.strip()
+
+ def c(self, wait=True):
+ if wait:
+ 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))
+ value = int(output.split(':')[1].strip(), 0)
+ return value
+
+ def p(self, obj):
+ output = self.command("p/x %s" % obj)
+ value = int(output.split('=')[-1].strip(), 0)
+ return value
+
+ def stepi(self):
+ output = self.command("stepi")
+ assert "Cannot" not in output
+ return output
+
+ def load(self):
+ output = self.command("load", timeout=60)
+ assert "failed" not in output
+ assert "Transfer rate" in output
+
+ def b(self, location):
+ output = self.command("b %s" % location)
+ assert "not defined" not in output
+ assert "Breakpoint" in output
+ return output
+
+ def hbreak(self, location):
+ output = self.command("hbreak %s" % location)
+ assert "not defined" not in output
+ assert "Hardware assisted breakpoint" in output
+ return output