RISCV_SIM ?= spike
XLEN ?= 64
+OPENOCD_INSTALL ?= $(abspath .)/openocd-install
+OPENOCD_VERSION = d8683119c0a1aa88320c7a6d4f0d6f63a5f976c5
+
+OPENOCD_DIR = $(OPENOCD_INSTALL)_$(OPENOCD_VERSION)/
+
+$(OPENOCD_DIR)/bin/openocd:
+ rm -rf riscv-openocd
+ git clone http://github.com/riscv/riscv-openocd.git
+ cd riscv-openocd ; \
+ git checkout $(OPENOCD_VERSION) ; \
+ ./bootstrap ; \
+ ./configure --enable-remote_bitbang --prefix=$(OPENOCD_INSTALL)_$(OPENOCD_VERSION) --disable-werror; \
+ make ; \
+ make install
+
+install_openocd: $(OPENOCD_DIR)/bin/openocd
+
src_dir ?= .
GDBSERVER_PY = $(src_dir)/gdbserver.py
pylint --rcfile=pylint.rc *.py
%.log:
- $(GDBSERVER_PY) --isolate --$(subst .log,,$@) --cmd $(RISCV_SIM) \
+ $(GDBSERVER_PY) --isolate --$(subst .log,,$@) --server_cmd $(OPENOCD_DIR)/bin/openocd \
> $@ 2>&1 || (sed s/^/$@:\ / $@ && false)
clean:
===========
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.
+perform an end-to-end test, communicating with gdb and OpenOCD.
+If a simulator or hardware passes all these tests, then you can be pretty
+confident that the actual debug interface is functioning correctly.
Targets
=======
-------------------------------------
`./gdbserver.py --freedom-e300`
+`./gdbserver.py --hifive1`
+
32-bit rocket-chip core in Simulation
-------------------------------------
`./gdbserver.py --freedom-e300-sim`
-
Debug Tips
==========
import sys
import tempfile
import time
+import os
import targets
import testlib
self.gdb.command("p *((int*) 0x%x)=0x13" % self.target.ram)
self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 4))
self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 8))
+ self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 12))
+ self.gdb.command("p *((int*) 0x%x)=0x13" % (self.target.ram + 16))
self.gdb.p("$pc=0x%x" % self.target.ram)
class SimpleS0Test(SimpleRegisterTest):
class SimpleF18Test(SimpleRegisterTest):
def check_reg(self, name):
+ self.gdb.p_raw("$mstatus=$mstatus | 0x00006000")
+ self.gdb.stepi()
a = random.random()
b = random.random()
self.gdb.p_raw("$%s=%f" % (name, a))
def setup(self):
# pylint: disable=attribute-defined-outside-init
length = min(2**20, self.target.ram_size - 2048)
- download_c = tempfile.NamedTemporaryFile(prefix="download_",
- suffix=".c")
- download_c.write("#include <stdint.h>\n")
- download_c.write(
+ self.download_c = tempfile.NamedTemporaryFile(prefix="download_",
+ suffix=".c", delete=False)
+ self.download_c.write("#include <stdint.h>\n")
+ self.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.download_c.write("uint32_t length = %d;\n" % length)
+ self.download_c.write("uint8_t d[%d] = {\n" % length)
self.crc = 0
+ assert length % 16 == 0
for i in range(length / 16):
- download_c.write(" /* 0x%04x */ " % (i * 16))
+ self.download_c.write(" /* 0x%04x */ " % (i * 16))
for _ in range(16):
value = random.randrange(1<<8)
- download_c.write("%d, " % value)
+ self.download_c.write("0x%02x, " % 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()
+ self.download_c.write("\n")
+ self.download_c.write("};\n")
+ self.download_c.write("uint8_t *data = &d[0];\n")
+ self.download_c.write(
+ "uint32_t main() { return crc32a(data, length); }\n")
+ self.download_c.flush()
if self.crc < 0:
self.crc += 2**32
- self.binary = self.target.compile(download_c.name,
+ self.binary = self.target.compile(self.download_c.name,
"programs/checksum.c")
self.gdb.command("file %s" % self.binary)
def test(self):
self.gdb.load()
self.gdb.command("b _exit")
- self.gdb.c()
+ self.gdb.c(timeout=60)
assertEqual(self.gdb.p("status"), self.crc)
+ os.unlink(self.download_c.name)
class MprvTest(GdbTest):
compile_args = ("programs/mprv.S", )
epilog="""
Example command line from the real world:
Run all RegsTest cases against a physical FPGA, with custom openocd command:
- ./gdbserver.py --freedom-e300 --cmd "$HOME/SiFive/openocd/src/openocd -s $HOME/SiFive/openocd/tcl -d" Simple
+ ./gdbserver.py --freedom-e300 --server_cmd "$HOME/SiFive/openocd/src/openocd -s $HOME/SiFive/openocd/tcl -d" Simple
""")
targets.add_target_options(parser)
global parsed # pylint: disable=global-statement
parsed = parser.parse_args()
- target = parsed.target(parsed.cmd, parsed.run, parsed.isolate)
+ target = parsed.target(parsed.server_cmd, parsed.sim_cmd, parsed.isolate)
if parsed.xlen:
target.xlen = parsed.xlen
la t0, trap_entry
csrw mtvec, t0
csrwi mstatus, 0
+
+ // make sure these registers exist by seeing if either S or U bits
+ // are set before attempting to zero them out.
+ csrr t1, misa
+ addi t2, x0, 1
+ slli t2, t2, 20 // U_EXTENSION
+ and t2, t1, t2
+ bne x0, t2, 1f
+ addi t2, x0, 1
+ slli t2, t2, 18 // S_EXTENSION
+ and t2, t1, t2
+ bne x0, t2, 1f
+ j 2f
+1:
csrwi mideleg, 0
csrwi medeleg, 0
+2:
csrwi mie, 0
# initialize global pointer
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+
+int main()
+{
+
+ volatile int forever = 1;
+ while (forever);
+
+ return 1;
+}
# update mstatus
csrr t1, CSR_MSTATUS
#if XLEN == 32
- li t0, (MSTATUS_MPRV | (VM_SV32 << 24))
+ li t0, (MSTATUS_MPRV | (SPTBR_MODE_SV32 << 24))
#else
- li t0, (MSTATUS_MPRV | (VM_SV39 << 24))
+ li t0, (MSTATUS_MPRV | (SPTBR_MODE_SV39 << 24))
#endif
#li t0, ((VM_SV39 << 24))
or t1, t0, t1
use_fpu = False
misa = None
- def __init__(self, cmd, run, isolate):
- self.cmd = cmd
- self.run = run
+ def __init__(self, server_cmd, sim_cmd, isolate):
+ self.server_cmd = server_cmd
+ self.sim_cmd = sim_cmd
self.isolate = isolate
def target(self):
def server(self):
"""Start the debug server that gdb connects to, eg. OpenOCD."""
if self.openocd_config:
- return testlib.Openocd(cmd=self.cmd, config=self.openocd_config)
+ return testlib.Openocd(server_cmd=self.server_cmd, config=self.openocd_config)
else:
raise NotImplementedError
ram_size = 5 * 1024 * 1024
instruction_hardware_breakpoint_count = 4
reset_vector = 0x1000
+ openocd_config = "targets/%s/openocd.cfg" % directory
class Spike64Target(SpikeTarget):
name = "spike64"
xlen = 64
use_fpu = True
- def server(self):
- return testlib.Spike(self.cmd, halted=True)
+ def target(self):
+ return testlib.Spike(self.sim_cmd)
class Spike32Target(SpikeTarget):
name = "spike32"
xlen = 32
- def server(self):
- return testlib.Spike(self.cmd, halted=True, xlen=32)
+ def target(self):
+ return testlib.Spike(self.sim_cmd, xlen=32)
class FreedomE300Target(Target):
name = "freedom-e300"
class FreedomE300SimTarget(Target):
name = "freedom-e300-sim"
xlen = 32
- timeout_sec = 240
+ timeout_sec = 6000
ram = 0x80000000
ram_size = 256 * 1024 * 1024
instruction_hardware_breakpoint_count = 2
openocd_config = "targets/%s/openocd.cfg" % name
def target(self):
- return testlib.VcsSim(simv=self.run, debug=False)
+ return testlib.VcsSim(simv=self.sim_cmd, debug=False)
class FreedomU500Target(Target):
name = "freedom-u500"
class FreedomU500SimTarget(Target):
name = "freedom-u500-sim"
xlen = 64
- timeout_sec = 240
+ timeout_sec = 6000
ram = 0x80000000
ram_size = 256 * 1024 * 1024
instruction_hardware_breakpoint_count = 2
openocd_config = "targets/%s/openocd.cfg" % name
def target(self):
- return testlib.VcsSim(simv=self.run, debug=False)
+ return testlib.VcsSim(simv=self.sim_cmd, debug=False)
targets = [
Spike32Target,
for t in targets:
group.add_argument("--%s" % t.name, action="store_const", const=t,
dest="target")
- parser.add_argument("--run",
+ parser.add_argument("--sim_cmd",
help="The command to use to start the actual target (e.g. "
"simulation)")
- parser.add_argument("--cmd",
- help="The command to use to start the debug server.")
+ parser.add_argument("--server_cmd",
+ help="The command to use to start the debug server (e.g. OpenOCD)")
xlen_group = parser.add_mutually_exclusive_group()
xlen_group.add_argument("--32", action="store_const", const=32, dest="xlen",
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
-$_TARGETNAME configure -work-area-phys 0x80000000 -work-area-size 8096 -work-area-backup 1
+$_TARGETNAME configure -work-area-phys 0x80000000 -work-area-size 8096 -work-area-backup 1 -rtos riscv
flash bank my_first_flash fespi 0x20000000 0 0 0 $_TARGETNAME
init
source [find interface/jtag_vpi.cfg]
jtag_vpi_set_port $::env(JTAG_VPI_PORT)
+#jtag_vpi_set_port 34448
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913
set _TARGETNAME $_CHIPNAME.cpu
-target create $_TARGETNAME riscv -chain-position $_TARGETNAME
+target create $_TARGETNAME riscv -chain-position $_TARGETNAME -rtos riscv
init
-
halt
+echo "OK GO NOW"
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913
set _TARGETNAME $_CHIPNAME.cpu
-target create $_TARGETNAME riscv -chain-position $_TARGETNAME
+target create $_TARGETNAME riscv -chain-position $_TARGETNAME -rtos riscv
gdb_report_data_abort enable
adapter_khz 10000
source [find interface/jtag_vpi.cfg]
-jtag_vpi_set_port $::env(JTAG_VPI_PORT)
+#jtag_vpi_set_port $::env(JTAG_VPI_PORT)
+jtag_vpi_set_port 46401
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913
set _TARGETNAME $_CHIPNAME.cpu
-target create $_TARGETNAME riscv -chain-position $_TARGETNAME
+target create $_TARGETNAME riscv -chain-position $_TARGETNAME -rtos riscv
init
halt
+echo "OK GO NOW"
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913
set _TARGETNAME $_CHIPNAME.cpu
-target create $_TARGETNAME riscv -chain-position $_TARGETNAME
+target create $_TARGETNAME riscv -chain-position $_TARGETNAME -rtos riscv
init
--- /dev/null
+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
+halt
+
+echo "Ready for Remote Connections"
\ No newline at end of file
return None
def compile(args, xlen=32): # pylint: disable=redefined-builtin
- cc = os.path.expandvars("$RISCV/bin/riscv%d-unknown-elf-gcc" % xlen)
+ cc = os.path.expandvars("$RISCV/bin/riscv64-unknown-elf-gcc")
cmd = [cc, "-g"]
+ if (xlen == 32):
+ cmd.append("-march=rv32imac")
+ cmd.append("-mabi=ilp32")
+ else:
+ cmd.append("-march=rv64imac")
+ cmd.append("-mabi=lp64")
for arg in args:
found = find_file(arg)
if found:
else:
cmd.append(arg)
process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
+ stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
if process.returncode:
print
class Spike(object):
logname = "spike.log"
- def __init__(self, cmd, binary=None, halted=False, with_gdb=True,
+ def __init__(self, sim_cmd, binary=None, halted=False, with_jtag_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)
+ if sim_cmd:
+ cmd = shlex.split(sim_cmd)
else:
- cmd = ["spike"]
+ spike = os.path.expandvars("$RISCV/bin/spike")
+ cmd = [spike]
if xlen == 32:
cmd += ["--isa", "RV32"]
if halted:
cmd.append('-H')
- if with_gdb:
- self.port = unused_port()
- cmd += ['--gdb-port', str(self.port)]
+ if with_jtag_gdb:
+ cmd += ['--rbb-port', '0']
+ os.environ['REMOTE_BITBANG_HOST'] = 'localhost'
cmd.append("-m32")
- cmd.append('pk')
+ cmd.append('programs/infinite_loop')
if binary:
cmd.append(binary)
logfile = open(self.logname, "w")
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"
+
def __del__(self):
try:
self.process.kill()
return self.process.wait(*args, **kwargs)
class VcsSim(object):
- def __init__(self, simv=None, debug=False):
- if simv:
+ def __init__(self, sim_cmd=None, debug=False):
+ if sim_cmd:
cmd = shlex.split(simv)
else:
cmd = ["simv"]
class Openocd(object):
logname = "openocd.log"
- def __init__(self, cmd=None, config=None, debug=False):
- if cmd:
- cmd = shlex.split(cmd)
+ def __init__(self, server_cmd=None, config=None, debug=False):
+ if server_cmd:
+ cmd = shlex.split(server_cmd)
else:
- cmd = ["openocd"]
- if config:
- cmd += ["-f", find_file(config)]
- if debug:
- cmd.append("-d")
-
+ openocd = os.path.expandvars("$RISCV/bin/riscv-openocd")
+ cmd = [openocd]
+ if (debug):
+ cmd.append("-d")
+
# This command needs to come before any config scripts on the command
# line, since they are executed in order.
- cmd[1:1] = [
+ cmd += [
# Tell OpenOCD to bind gdb to an unused, ephemeral port.
"--command",
"gdb_port 0",
"telnet_port disabled",
]
+ if config:
+ f = find_file(config)
+ if f is None:
+ print("Unable to read file " + config)
+ exit(1)
+
+ cmd += ["-f", f]
+ if debug:
+ cmd.append("-d")
+
logfile = open(Openocd.logname, "w")
logfile.write("+ %s\n" % " ".join(cmd))
logfile.flush()
messaged = False
while True:
log = open(Openocd.logname).read()
- if "Examined RISCV core" in log:
+ if "Ready for Remote Connections" in log:
break
if not self.process.poll() is None:
raise Exception(
elif matches:
[match] = matches
return int(match.group('port'))
- time.sleep(0.1)
+ time.sleep(1)
raise Exception("Timed out waiting for gdb server to obtain port.")
def __del__(self):
"""Wait for prompt."""
self.child.expect(r"\(gdb\)")
- def command(self, command, timeout=-1):
+ def command(self, command, timeout=6000):
self.child.sendline(command)
self.child.expect("\n", timeout=timeout)
self.child.expect(r"\(gdb\)", timeout=timeout)
return self.child.before.strip()
- def c(self, wait=True):
+ def c(self, wait=True, timeout=-1):
if wait:
- output = self.command("c")
+ output = self.command("c", timeout=timeout)
assert "Continuing" in output
return output
else:
def interrupt(self):
self.child.send("\003")
- self.child.expect(r"\(gdb\)", timeout=60)
+ self.child.expect(r"\(gdb\)", timeout=6000)
return self.child.before.strip()
def x(self, address, size='w'):
return output
def load(self):
- output = self.command("load", timeout=60)
+ output = self.command("load", timeout=6000)
assert "failed" not in output
assert "Transfer rate" in output