13 MSTATUS_UIE
= 0x00000001
14 MSTATUS_SIE
= 0x00000002
15 MSTATUS_HIE
= 0x00000004
16 MSTATUS_MIE
= 0x00000008
17 MSTATUS_UPIE
= 0x00000010
18 MSTATUS_SPIE
= 0x00000020
19 MSTATUS_HPIE
= 0x00000040
20 MSTATUS_MPIE
= 0x00000080
21 MSTATUS_SPP
= 0x00000100
22 MSTATUS_HPP
= 0x00000600
23 MSTATUS_MPP
= 0x00001800
24 MSTATUS_FS
= 0x00006000
25 MSTATUS_XS
= 0x00018000
26 MSTATUS_MPRV
= 0x00020000
27 MSTATUS_PUM
= 0x00040000
28 MSTATUS_MXR
= 0x00080000
29 MSTATUS_VM
= 0x1F000000
30 MSTATUS32_SD
= 0x80000000
31 MSTATUS64_SD
= 0x8000000000000000
33 def ihex_line(address
, record_type
, data
):
34 assert len(data
) < 128
35 line
= ":%02X%04X%02X" % (len(data
), address
, record_type
)
37 check
+= address
% 256
43 line
+= "%02X" % value
44 line
+= "%02X\n" % ((256-check
)%256)
48 assert line
.startswith(":")
50 data_len
= int(line
[:2], 16)
51 address
= int(line
[2:6], 16)
52 record_type
= int(line
[6:8], 16)
54 for i
in range(data_len
):
55 data
+= "%c" % int(line
[8+2*i
:10+2*i
], 16)
56 return record_type
, address
, data
58 class DeleteServer(unittest
.TestCase
):
62 class SimpleRegisterTest(DeleteServer
):
64 self
.server
= target
.server()
65 self
.gdb
= testlib
.Gdb()
66 # For now gdb has to be told what the architecture is when it's not
68 self
.gdb
.command("set arch riscv:rv%d" % target
.xlen
)
70 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
73 self
.gdb
.command("p *((int*) 0x%x)=0x13" % target
.ram
)
74 self
.gdb
.command("p *((int*) 0x%x)=0x13" % (target
.ram
+ 4))
75 self
.gdb
.command("p *((int*) 0x%x)=0x13" % (target
.ram
+ 8))
76 self
.gdb
.p("$pc=0x%x" % target
.ram
)
78 def check_reg(self
, name
):
79 a
= random
.randrange(1<<target
.xlen
)
80 b
= random
.randrange(1<<target
.xlen
)
81 self
.gdb
.p("$%s=0x%x" % (name
, a
))
83 self
.assertEqual(self
.gdb
.p("$%s" % name
), a
)
84 self
.gdb
.p("$%s=0x%x" % (name
, b
))
86 self
.assertEqual(self
.gdb
.p("$%s" % name
), b
)
89 # S0 is saved/restored in DSCRATCH
93 # S1 is saved/restored in Debug RAM
97 # T0 is not saved/restored at all
101 # T2 is not saved/restored at all
104 class SimpleMemoryTest(DeleteServer
):
106 self
.server
= target
.server()
107 self
.gdb
= testlib
.Gdb()
108 self
.gdb
.command("set arch riscv:rv%d" % target
.xlen
)
109 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
111 def access_test(self
, size
, data_type
):
112 self
.assertEqual(self
.gdb
.p("sizeof(%s)" % data_type
),
114 a
= 0x86753095555aaaa & ((1<<(size
*8))-1)
115 b
= 0xdeadbeef12345678 & ((1<<(size
*8))-1)
116 self
.gdb
.p("*((%s*)0x%x) = 0x%x" % (data_type
, target
.ram
, a
))
117 self
.gdb
.p("*((%s*)0x%x) = 0x%x" % (data_type
, target
.ram
+ size
, b
))
118 self
.assertEqual(self
.gdb
.p("*((%s*)0x%x)" % (data_type
, target
.ram
)), a
)
119 self
.assertEqual(self
.gdb
.p("*((%s*)0x%x)" % (data_type
, target
.ram
+ size
)), b
)
122 self
.access_test(1, 'char')
125 self
.access_test(2, 'short')
128 self
.access_test(4, 'int')
131 self
.access_test(8, 'long long')
133 def test_block(self
):
136 a
= tempfile
.NamedTemporaryFile(suffix
=".ihex")
138 for i
in range(length
/ line_length
):
139 line_data
= "".join(["%c" % random
.randrange(256) for _
in range(line_length
)])
141 a
.write(ihex_line(i
* line_length
, 0, line_data
))
144 self
.gdb
.command("restore %s 0x%x" % (a
.name
, target
.ram
))
145 for offset
in range(0, length
, 19*4) + [length
-4]:
146 value
= self
.gdb
.p("*((int*)0x%x)" % (target
.ram
+ offset
))
147 written
= ord(data
[offset
]) | \
148 (ord(data
[offset
+1]) << 8) | \
149 (ord(data
[offset
+2]) << 16) | \
150 (ord(data
[offset
+3]) << 24)
151 self
.assertEqual(value
, written
)
153 b
= tempfile
.NamedTemporaryFile(suffix
=".ihex")
154 self
.gdb
.command("dump ihex memory %s 0x%x 0x%x" % (b
.name
, target
.ram
,
155 target
.ram
+ length
))
157 record_type
, address
, line_data
= ihex_parse(line
)
158 if (record_type
== 0):
159 self
.assertEqual(line_data
, data
[address
:address
+len(line_data
)])
161 class InstantHaltTest(DeleteServer
):
163 self
.server
= target
.server()
164 self
.gdb
= testlib
.Gdb()
165 self
.gdb
.command("set arch riscv:rv%d" % target
.xlen
)
166 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
168 def test_instant_halt(self
):
169 self
.assertEqual(target
.reset_vector
, self
.gdb
.p("$pc"))
170 # mcycle and minstret have no defined reset value.
171 mstatus
= self
.gdb
.p("$mstatus")
172 self
.assertEqual(mstatus
& (MSTATUS_MIE | MSTATUS_MPRV |
175 def test_change_pc(self
):
176 """Change the PC right as we come out of reset."""
178 self
.gdb
.command("p *((int*) 0x%x)=0x13" % target
.ram
)
179 self
.gdb
.command("p *((int*) 0x%x)=0x13" % (target
.ram
+ 4))
180 self
.gdb
.command("p *((int*) 0x%x)=0x13" % (target
.ram
+ 8))
181 self
.gdb
.p("$pc=0x%x" % target
.ram
)
183 self
.assertEqual((target
.ram
+ 4), self
.gdb
.p("$pc"))
185 self
.assertEqual((target
.ram
+ 8), self
.gdb
.p("$pc"))
187 class DebugTest(DeleteServer
):
189 # Include malloc so that gdb can make function calls. I suspect this
190 # malloc will silently blow through the memory set aside for it, so be
192 self
.binary
= target
.compile("programs/debug.c", "programs/checksum.c",
193 "programs/tiny-malloc.c", "-DDEFINE_MALLOC", "-DDEFINE_FREE")
194 self
.server
= target
.server()
195 self
.gdb
= testlib
.Gdb()
196 self
.gdb
.command("file %s" % self
.binary
)
197 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
201 def exit(self
, expected_result
= 0xc86455d4):
202 output
= self
.gdb
.c()
203 self
.assertIn("Breakpoint", output
)
204 self
.assertIn("_exit", output
)
205 self
.assertEqual(self
.gdb
.p("status"), expected_result
)
207 def test_function_call(self
):
208 self
.gdb
.b("main:start")
210 text
= "Howdy, Earth!"
211 gdb_length
= self
.gdb
.p('strlen("%s")' % text
)
212 self
.assertEqual(gdb_length
, len(text
))
215 def test_change_string(self
):
216 text
= "This little piggy went to the market."
217 self
.gdb
.b("main:start")
219 self
.gdb
.p('fox = "%s"' % text
)
220 self
.exit(0x43b497b8)
222 def test_turbostep(self
):
223 """Single step a bunch of times."""
224 self
.gdb
.command("p i=0");
230 pc
= self
.gdb
.p("$pc")
231 self
.assertNotEqual(last_pc
, pc
)
232 if (last_pc
and pc
> last_pc
and pc
- last_pc
<= 4):
237 # Some basic sanity that we're not running between breakpoints or
239 self
.assertGreater(jumps
, 10)
240 self
.assertGreater(advances
, 50)
245 def test_symbols(self
):
248 output
= self
.gdb
.c()
249 self
.assertIn(", main ", output
)
250 output
= self
.gdb
.c()
251 self
.assertIn(", rot13 ", output
)
253 def test_breakpoint(self
):
255 # The breakpoint should be hit exactly 2 times.
257 output
= self
.gdb
.c()
259 self
.assertIn("Breakpoint ", output
)
260 #TODO self.assertIn("rot13 ", output)
263 def test_hwbp_1(self
):
264 if target
.instruction_hardware_breakpoint_count
< 1:
267 self
.gdb
.hbreak("rot13")
268 # The breakpoint should be hit exactly 2 times.
270 output
= self
.gdb
.c()
272 self
.assertIn("Breakpoint ", output
)
273 #TODO self.assertIn("rot13 ", output)
276 def test_hwbp_2(self
):
277 if target
.instruction_hardware_breakpoint_count
< 2:
280 self
.gdb
.hbreak("main")
281 self
.gdb
.hbreak("rot13")
282 # We should hit 3 breakpoints.
284 output
= self
.gdb
.c()
286 self
.assertIn("Breakpoint ", output
)
287 #TODO self.assertIn("rot13 ", output)
290 def test_too_many_hwbp(self
):
292 self
.gdb
.hbreak("*rot13 + %d" % (i
* 4))
294 output
= self
.gdb
.c()
295 self
.assertIn("Cannot insert hardware breakpoint", output
)
296 # Clean up, otherwise the hardware breakpoints stay set and future
298 self
.gdb
.command("D")
300 def test_registers(self
):
301 # Get to a point in the code where some registers have actually been
306 # Try both forms to test gdb.
307 for cmd
in ("info all-registers", "info registers all"):
308 output
= self
.gdb
.command(cmd
)
309 self
.assertNotIn("Could not", output
)
310 for reg
in ('zero', 'ra', 'sp', 'gp', 'tp'):
311 self
.assertIn(reg
, output
)
314 # mcpuid is one of the few registers that should have the high bit set
316 # Leave this commented out until gdb and spike agree on the encoding of
317 # mcpuid (which is going to be renamed to misa in any case).
318 #self.assertRegexpMatches(output, ".*mcpuid *0x80")
321 # The instret register should always be changing.
324 # instret = self.gdb.p("$instret")
325 # self.assertNotEqual(instret, last_instret)
326 # last_instret = instret
331 def test_interrupt(self
):
332 """Sending gdb ^C while the program is running should cause it to halt."""
333 self
.gdb
.b("main:start")
336 self
.gdb
.c(wait
=False)
338 output
= self
.gdb
.interrupt()
339 #TODO: assert "main" in output
340 self
.assertGreater(self
.gdb
.p("j"), 10)
344 class StepTest(DeleteServer
):
346 self
.binary
= target
.compile("programs/step.S")
347 self
.server
= target
.server()
348 self
.gdb
= testlib
.Gdb()
349 self
.gdb
.command("file %s" % self
.binary
)
350 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
356 main
= self
.gdb
.p("$pc")
357 for expected
in (4, 8, 0xc, 0x10, 0x18, 0x1c, 0x28, 0x20, 0x2c, 0x2c):
359 pc
= self
.gdb
.p("$pc")
360 self
.assertEqual("%x" % pc
, "%x" % (expected
+ main
))
362 class RegsTest(DeleteServer
):
364 self
.binary
= target
.compile("programs/regs.S")
365 self
.server
= target
.server()
366 self
.gdb
= testlib
.Gdb()
367 self
.gdb
.command("file %s" % self
.binary
)
368 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
371 self
.gdb
.b("handle_trap")
374 def test_write_gprs(self
):
375 regs
= [("x%d" % n
) for n
in range(2, 32)]
377 self
.gdb
.p("$pc=write_regs")
378 for i
, r
in enumerate(regs
):
379 self
.gdb
.command("p $%s=%d" % (r
, (0xdeadbeef<<i
)+17))
380 self
.gdb
.command("p $x1=data")
381 self
.gdb
.command("b all_done")
382 output
= self
.gdb
.c()
383 self
.assertIn("Breakpoint ", output
)
385 # Just to get this data in the log.
386 self
.gdb
.command("x/30gx data")
387 self
.gdb
.command("info registers")
388 for n
in range(len(regs
)):
389 self
.assertEqual(self
.gdb
.x("data+%d" % (8*n
), 'g'),
390 ((0xdeadbeef<<n
)+17) & ((1<<target
.xlen
)-1))
392 def test_write_csrs(self
):
393 # As much a test of gdb as of the simulator.
394 self
.gdb
.p("$mscratch=0")
396 self
.assertEqual(self
.gdb
.p("$mscratch"), 0)
397 self
.gdb
.p("$mscratch=123")
399 self
.assertEqual(self
.gdb
.p("$mscratch"), 123)
401 self
.gdb
.command("p $pc=write_regs")
402 self
.gdb
.command("p $a0=data")
403 self
.gdb
.command("b all_done")
404 self
.gdb
.command("c")
406 self
.assertEqual(123, self
.gdb
.p("$mscratch"))
407 self
.assertEqual(123, self
.gdb
.p("$x1"))
408 self
.assertEqual(123, self
.gdb
.p("$csr832"))
410 class DownloadTest(DeleteServer
):
412 length
= min(2**20, target
.ram_size
- 2048)
413 download_c
= tempfile
.NamedTemporaryFile(prefix
="download_", suffix
=".c")
414 download_c
.write("#include <stdint.h>\n")
415 download_c
.write("unsigned int crc32a(uint8_t *message, unsigned int size);\n")
416 download_c
.write("uint32_t length = %d;\n" % length
)
417 download_c
.write("uint8_t d[%d] = {\n" % length
)
419 for i
in range(length
/ 16):
420 download_c
.write(" /* 0x%04x */ " % (i
* 16));
422 value
= random
.randrange(1<<8)
423 download_c
.write("%d, " % value
)
424 self
.crc
= binascii
.crc32("%c" % value
, self
.crc
)
425 download_c
.write("\n");
426 download_c
.write("};\n");
427 download_c
.write("uint8_t *data = &d[0];\n");
428 download_c
.write("uint32_t main() { return crc32a(data, length); }\n")
434 self
.binary
= target
.compile(download_c
.name
, "programs/checksum.c")
435 self
.server
= target
.server()
436 self
.gdb
= testlib
.Gdb()
437 self
.gdb
.command("file %s" % self
.binary
)
438 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
440 def test_download(self
):
441 output
= self
.gdb
.load()
442 self
.gdb
.command("b _exit")
444 self
.assertEqual(self
.gdb
.p("status"), self
.crc
)
446 class MprvTest(DeleteServer
):
448 self
.binary
= target
.compile("programs/mprv.S")
449 self
.server
= target
.server()
450 self
.gdb
= testlib
.Gdb()
451 self
.gdb
.command("file %s" % self
.binary
)
452 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
456 """Test that the debugger can access memory when MPRV is set."""
457 self
.gdb
.c(wait
=False)
460 output
= self
.gdb
.command("p/x *(int*)(((char*)&data)-0x80000000)")
461 self
.assertIn("0xbead", output
)
463 class Target(object):
467 raise NotImplementedError
469 def compile(self
, *sources
):
470 binary_name
= "%s_%s" % (
472 os
.path
.basename(os
.path
.splitext(sources
[0])[0]))
474 self
.temporary_binary
= tempfile
.NamedTemporaryFile(
475 prefix
=binary_name
+ "_")
476 binary_name
= self
.temporary_binary
.name
477 testlib
.compile(sources
+
478 ("programs/entry.S", "programs/init.c",
480 "-T", "targets/%s/link.lds" % (self
.directory
or self
.name
),
487 class SpikeTarget(Target
):
490 ram_size
= 5 * 1024 * 1024
491 instruction_hardware_breakpoint_count
= 0
492 reset_vector
= 0x1000
494 class Spike64Target(SpikeTarget
):
499 return testlib
.Spike(parsed
.cmd
, halted
=True)
501 class Spike32Target(SpikeTarget
):
506 return testlib
.Spike(parsed
.cmd
, halted
=True, xlen
=32)
508 class FreedomE300Target(Target
):
509 name
= "freedom-e300"
513 instruction_hardware_breakpoint_count
= 2
516 return testlib
.Openocd(cmd
=parsed
.cmd
,
517 config
="targets/%s/openocd.cfg" % self
.name
)
526 parser
= argparse
.ArgumentParser(
528 Example command line from the real world:
529 Run all RegsTest cases against a MicroSemi m2gl_m2s board, with custom openocd command:
530 ./gdbserver.py --m2gl_m2s --cmd "$HOME/SiFive/openocd/src/openocd -s $HOME/SiFive/openocd/tcl -d" -- -vf RegsTest
532 group
= parser
.add_mutually_exclusive_group(required
=True)
534 group
.add_argument("--%s" % t
.name
, action
="store_const", const
=t
,
536 parser
.add_argument("--cmd",
537 help="The command to use to start the debug server.")
538 parser
.add_argument("--isolate", action
="store_true",
539 help="Try to run in such a way that multiple instances can run at "
540 "the same time. This may make it harder to debug a failure if it "
542 parser
.add_argument("unittest", nargs
="*")
544 parsed
= parser
.parse_args()
547 target
= parsed
.target()
548 unittest
.main(argv
=[sys
.argv
[0]] + parsed
.unittest
)
550 # TROUBLESHOOTING TIPS
551 # If a particular test fails, run just that one test, eg.:
552 # ./tests/gdbserver.py MprvTest.test_mprv
553 # Then inspect gdb.log and spike.log to see what happened in more detail.
555 if __name__
== '__main__':