Skip to content

Commit cf5360f

Browse files
authored
Merge pull request #489 from riscv-software-src/power_dance
debug: Test OpenOCD behavior when harts become unavailable, using new spike mechanism
2 parents 4d2b318 + 90691df commit cf5360f

File tree

10 files changed

+196
-55
lines changed

10 files changed

+196
-55
lines changed

debug/gdbserver.py

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
from testlib import GdbTest, GdbSingleHartTest, TestFailed
2020
from testlib import TestNotApplicable, CompileError
2121
from testlib import UnknownThread
22-
from testlib import CouldNotReadRegisters
22+
from testlib import CouldNotReadRegisters, CommandException
23+
from testlib import ThreadTerminated
2324

2425
MSTATUS_UIE = 0x00000001
2526
MSTATUS_SIE = 0x00000002
@@ -1807,22 +1808,29 @@ def test(self):
18071808
output = self.gdb.c()
18081809
assertIn("_exit", output)
18091810

1810-
class CeaseMultiTest(GdbTest):
1811-
"""Test that we work correctly when a hart ceases to respond (e.g. because
1811+
class UnavailableMultiTest(GdbTest):
1812+
"""Test that we work correctly when a hart becomes unavailable (e.g. because
18121813
it's powered down)."""
18131814
compile_args = ("programs/counting_loop.c", "-DDEFINE_MALLOC",
18141815
"-DDEFINE_FREE")
18151816

18161817
def early_applicable(self):
1817-
return self.hart.support_cease and len(self.target.harts) > 1
1818+
return (self.hart.support_cease or
1819+
self.target.support_unavailable_control) \
1820+
and len(self.target.harts) > 1
18181821

18191822
def setup(self):
18201823
ProgramTest.setup(self)
1821-
self.parkOtherHarts("precease")
1824+
self.parkOtherHarts()
18221825

18231826
def test(self):
18241827
# Run all the way to the infinite loop in exit
1825-
self.gdb.c(wait=False)
1828+
self.gdb.c_all(wait=False)
1829+
# Other hart should have become unavailable.
1830+
if self.target.support_unavailable_control:
1831+
self.server.wait_until_running(self.target.harts)
1832+
self.server.command(
1833+
f"riscv dmi_write 0x1f 0x{(1<<self.hart.id)&0x3:x}")
18261834
self.gdb.expect(r"\S+ became unavailable.")
18271835
self.gdb.interrupt()
18281836

@@ -1834,7 +1842,7 @@ def test(self):
18341842
self.gdb.p("$misa")
18351843
assert False, \
18361844
"Shouldn't be able to access unavailable hart."
1837-
except UnknownThread:
1845+
except (UnknownThread, CommandException):
18381846
pass
18391847

18401848
# Check that the main hart can still be debugged.
@@ -1872,29 +1880,70 @@ def test(self):
18721880
except CouldNotReadRegisters:
18731881
pass
18741882

1875-
class CeaseRunTest(ProgramTest):
1883+
class UnavailableRunTest(ProgramTest):
18761884
"""Test that we work correctly when the hart we're debugging ceases to
18771885
respond."""
18781886
def early_applicable(self):
1879-
return self.hart.support_cease
1887+
return self.hart.support_cease or \
1888+
self.target.support_unavailable_control
18801889

18811890
def test(self):
18821891
self.gdb.b("main")
18831892
output = self.gdb.c()
18841893
assertIn("Breakpoint", output)
18851894
assertIn("main", output)
18861895

1887-
self.gdb.p("$pc=precease")
1896+
if self.target.support_unavailable_control:
1897+
self.gdb.p("$pc=loop_forever")
1898+
else:
1899+
self.gdb.p("$pc=cease")
18881900
self.gdb.c(wait=False)
1901+
if self.target.support_unavailable_control:
1902+
self.server.wait_until_running([self.hart])
1903+
self.server.command(
1904+
f"riscv dmi_write 0x1f 0x{(~(1<<self.hart.id))&0x3:x}")
18891905
self.gdb.expect(r"\S+ became unavailable.")
18901906
self.gdb.interrupt()
1907+
# gdb might automatically switch to the available hart.
1908+
try:
1909+
self.gdb.select_hart(self.hart)
1910+
except ThreadTerminated:
1911+
# GDB sees that the thread is gone. Count this as success.
1912+
return
18911913
try:
18921914
self.gdb.p("$pc")
18931915
assert False, ("Registers shouldn't be accessible when the hart is "
18941916
"unavailable.")
18951917
except CouldNotReadRegisters:
18961918
pass
18971919

1920+
class UnavailableCycleTest(ProgramTest):
1921+
"""Test that harts can be debugged after becoming temporarily
1922+
unavailable."""
1923+
def early_applicable(self):
1924+
return self.target.support_unavailable_control
1925+
1926+
def test(self):
1927+
self.gdb.b("main")
1928+
output = self.gdb.c()
1929+
assertIn("Breakpoint", output)
1930+
assertIn("main", output)
1931+
1932+
self.gdb.p("$pc=loop_forever")
1933+
self.gdb.c(wait=False)
1934+
self.server.wait_until_running([self.hart])
1935+
self.server.command(
1936+
f"riscv dmi_write 0x1f 0x{(~(1<<self.hart.id))&0x3:x}")
1937+
self.gdb.expect(r"\S+ became unavailable.")
1938+
1939+
# Now send a DMI command through OpenOCD to make the hart available
1940+
# again.
1941+
1942+
self.server.command("riscv dmi_write 0x1f 0x3")
1943+
self.gdb.expect(r"\S+ became available")
1944+
self.gdb.interrupt()
1945+
self.gdb.p("$pc")
1946+
18981947
class FreeRtosTest(GdbTest):
18991948
def early_applicable(self):
19001949
return self.target.freertos_binary
@@ -1989,7 +2038,6 @@ def setup(self):
19892038
self.gdb.b("handle_trap")
19902039

19912040
def test(self):
1992-
self.gdb.command(f"monitor targets {self.hart.id}")
19932041
# Set trigger on Load access fault
19942042
self.gdb.command("monitor riscv etrigger set m 0x20")
19952043
# Set fox to a null pointer so we'll get a load access exception later.
@@ -2009,7 +2057,6 @@ def setup(self):
20092057
DebugTest.setup(self)
20102058
self.gdb.b("main")
20112059
self.gdb.c()
2012-
self.gdb.command(f"monitor targets {self.hart.id}")
20132060

20142061
def test(self):
20152062
# Execute 2 instructions.
@@ -2039,7 +2086,6 @@ def setup(self):
20392086
self.gdb.load()
20402087

20412088
def test(self):
2042-
self.gdb.command(f"monitor targets {self.hart.id}")
20432089
output = self.gdb.command("monitor riscv itrigger set 0x80")
20442090
assertIn("Doesn't make sense", output)
20452091
output = self.gdb.command("monitor riscv itrigger set m 0")

debug/targets.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ class Target:
129129
# in https://github.com/FreeRTOS/FreeRTOS.
130130
freertos_binary = None
131131

132+
# Supports controlling hart availability through DMCUSTOM.
133+
support_unavailable_control = False
134+
132135
# Internal variables:
133136
directory = None
134137
temporary_files = []

debug/targets/RISC-V/spike32-2-hwthread.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class spike32_2(targets.Target):
1010
timeout_sec = 5
1111
implements_custom_test = True
1212
support_memory_sampling = False # not supported without sba
13+
support_unavailable_control = True
1314

1415
def create(self):
1516
return testlib.Spike(self, isa="RV32IMAFDV", support_hasel=True,

debug/targets/RISC-V/spike32-2.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class spike32_2(targets.Target):
99
openocd_config_path = "spike-2.cfg"
1010
timeout_sec = 30
1111
implements_custom_test = True
12+
support_unavailable_control = True
1213

1314
def create(self):
1415
return testlib.Spike(self, isa="RV32IMAFC", progbufsize=0, dmi_rti=4,

debug/targets/RISC-V/spike32.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class spike32(targets.Target):
1717
implements_custom_test = True
1818
support_memory_sampling = False # Needs SBA
1919
freertos_binary = "bin/RTOSDemo32.axf"
20+
support_unavailable_control = True
2021

2122
def create(self):
2223
# 64-bit FPRs on 32-bit target

debug/targets/RISC-V/spike64-2-hwthread.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class spike64_2(targets.Target):
1313
implements_custom_test = True
1414
support_hasel = False
1515
support_memory_sampling = False # Needs SBA
16+
support_unavailable_control = True
1617

1718
def create(self):
1819
return testlib.Spike(self, isa="RV64IMAFDV", abstract_rti=30,

debug/targets/RISC-V/spike64-2-rtos.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class spike64_2_rtos(targets.Target):
1313
test_semihosting = False
1414
support_manual_hwbp = False # not supported with `-rtos riscv`
1515
support_memory_sampling = False # not supported with `-rtos riscv`
16+
support_unavailable_control = True
1617

1718
def create(self):
1819
return testlib.Spike(self, abstract_rti=30, support_hasel=False,

debug/targets/RISC-V/spike64-2.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class spike64_2(targets.Target):
1010
timeout_sec = 5
1111
implements_custom_test = True
1212
support_memory_sampling = False # Needs SBA
13+
support_unavailable_control = True
1314

1415
def create(self):
1516
return testlib.Spike(self)

debug/targets/RISC-V/spike64.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class spike64(targets.Target):
1717
timeout_sec = 30
1818
implements_custom_test = True
1919
freertos_binary = "bin/RTOSDemo64.axf"
20+
support_unavailable_control = True
2021

2122
def create(self):
2223
# 32-bit FPRs only

0 commit comments

Comments
 (0)