Merge pull request #69 from riscv/multicore
[riscv-tests.git] / debug / gdbserver.py
1 #!/usr/bin/env python
2
3 import argparse
4 import binascii
5 import random
6 import sys
7 import tempfile
8 import time
9 import os
10
11 import targets
12 import testlib
13 from testlib import assertEqual, assertNotEqual, assertIn, assertNotIn
14 from testlib import assertGreater, assertRegexpMatches, assertLess
15 from testlib import GdbTest, GdbSingleHartTest, TestFailed
16
17 MSTATUS_UIE = 0x00000001
18 MSTATUS_SIE = 0x00000002
19 MSTATUS_HIE = 0x00000004
20 MSTATUS_MIE = 0x00000008
21 MSTATUS_UPIE = 0x00000010
22 MSTATUS_SPIE = 0x00000020
23 MSTATUS_HPIE = 0x00000040
24 MSTATUS_MPIE = 0x00000080
25 MSTATUS_SPP = 0x00000100
26 MSTATUS_HPP = 0x00000600
27 MSTATUS_MPP = 0x00001800
28 MSTATUS_FS = 0x00006000
29 MSTATUS_XS = 0x00018000
30 MSTATUS_MPRV = 0x00020000
31 MSTATUS_PUM = 0x00040000
32 MSTATUS_MXR = 0x00080000
33 MSTATUS_VM = 0x1F000000
34 MSTATUS32_SD = 0x80000000
35 MSTATUS64_SD = 0x8000000000000000
36
37 # pylint: disable=abstract-method
38
39 def ihex_line(address, record_type, data):
40 assert len(data) < 128
41 line = ":%02X%04X%02X" % (len(data), address, record_type)
42 check = len(data)
43 check += address % 256
44 check += address >> 8
45 check += record_type
46 for char in data:
47 value = ord(char)
48 check += value
49 line += "%02X" % value
50 line += "%02X\n" % ((256-check)%256)
51 return line
52
53 def ihex_parse(line):
54 assert line.startswith(":")
55 line = line[1:]
56 data_len = int(line[:2], 16)
57 address = int(line[2:6], 16)
58 record_type = int(line[6:8], 16)
59 data = ""
60 for i in range(data_len):
61 data += "%c" % int(line[8+2*i:10+2*i], 16)
62 return record_type, address, data
63
64 def readable_binary_string(s):
65 return "".join("%02x" % ord(c) for c in s)
66
67 class SimpleRegisterTest(GdbTest):
68 def check_reg(self, name):
69 a = random.randrange(1<<self.hart.xlen)
70 b = random.randrange(1<<self.hart.xlen)
71 self.gdb.p("$%s=0x%x" % (name, a))
72 self.gdb.stepi()
73 assertEqual(self.gdb.p("$%s" % name), a)
74 self.gdb.p("$%s=0x%x" % (name, b))
75 self.gdb.stepi()
76 assertEqual(self.gdb.p("$%s" % name), b)
77
78 def setup(self):
79 # 0x13 is nop
80 self.gdb.command("p *((int*) 0x%x)=0x13" % self.hart.ram)
81 self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 4))
82 self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 8))
83 self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 12))
84 self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 16))
85 self.gdb.p("$pc=0x%x" % self.hart.ram)
86
87 class SimpleS0Test(SimpleRegisterTest):
88 def test(self):
89 self.check_reg("s0")
90
91 class SimpleS1Test(SimpleRegisterTest):
92 def test(self):
93 self.check_reg("s1")
94
95 class SimpleT0Test(SimpleRegisterTest):
96 def test(self):
97 self.check_reg("t0")
98
99 class SimpleT1Test(SimpleRegisterTest):
100 def test(self):
101 self.check_reg("t1")
102
103 class SimpleF18Test(SimpleRegisterTest):
104 def check_reg(self, name):
105 self.gdb.p_raw("$mstatus=$mstatus | 0x00006000")
106 self.gdb.stepi()
107 a = random.random()
108 b = random.random()
109 self.gdb.p_raw("$%s=%f" % (name, a))
110 self.gdb.stepi()
111 assertLess(abs(float(self.gdb.p_raw("$%s" % name)) - a), .001)
112 self.gdb.p_raw("$%s=%f" % (name, b))
113 self.gdb.stepi()
114 assertLess(abs(float(self.gdb.p_raw("$%s" % name)) - b), .001)
115
116 def early_applicable(self):
117 return self.hart.extensionSupported('F')
118
119 def test(self):
120 self.check_reg("f18")
121
122 class SimpleMemoryTest(GdbTest):
123 def access_test(self, size, data_type):
124 assertEqual(self.gdb.p("sizeof(%s)" % data_type), size)
125 a = 0x86753095555aaaa & ((1<<(size*8))-1)
126 b = 0xdeadbeef12345678 & ((1<<(size*8))-1)
127 addrA = self.hart.ram
128 addrB = self.hart.ram + self.hart.ram_size - size
129 self.gdb.p("*((%s*)0x%x) = 0x%x" % (data_type, addrA, a))
130 self.gdb.p("*((%s*)0x%x) = 0x%x" % (data_type, addrB, b))
131 assertEqual(self.gdb.p("*((%s*)0x%x)" % (data_type, addrA)), a)
132 assertEqual(self.gdb.p("*((%s*)0x%x)" % (data_type, addrB)), b)
133
134 class MemTest8(SimpleMemoryTest):
135 def test(self):
136 self.access_test(1, 'char')
137
138 class MemTest16(SimpleMemoryTest):
139 def test(self):
140 self.access_test(2, 'short')
141
142 class MemTest32(SimpleMemoryTest):
143 def test(self):
144 self.access_test(4, 'int')
145
146 class MemTest64(SimpleMemoryTest):
147 def test(self):
148 self.access_test(8, 'long long')
149
150 # FIXME: I'm not passing back invalid addresses correctly in read/write memory.
151 #class MemTestReadInvalid(SimpleMemoryTest):
152 # def test(self):
153 # # This test relies on 'gdb_report_data_abort enable' being executed in
154 # # the openocd.cfg file.
155 # try:
156 # self.gdb.p("*((int*)0xdeadbeef)")
157 # assert False, "Read should have failed."
158 # except testlib.CannotAccess as e:
159 # assertEqual(e.address, 0xdeadbeef)
160 # self.gdb.p("*((int*)0x%x)" % self.hart.ram)
161 #
162 #class MemTestWriteInvalid(SimpleMemoryTest):
163 # def test(self):
164 # # This test relies on 'gdb_report_data_abort enable' being executed in
165 # # the openocd.cfg file.
166 # try:
167 # self.gdb.p("*((int*)0xdeadbeef)=8675309")
168 # assert False, "Write should have failed."
169 # except testlib.CannotAccess as e:
170 # assertEqual(e.address, 0xdeadbeef)
171 # self.gdb.p("*((int*)0x%x)=6874742" % self.hart.ram)
172
173 class MemTestBlock(GdbTest):
174 length = 1024
175 line_length = 16
176
177 def test(self):
178 a = tempfile.NamedTemporaryFile(suffix=".ihex")
179 data = ""
180 for i in range(self.length / self.line_length):
181 line_data = "".join(["%c" % random.randrange(256)
182 for _ in range(self.line_length)])
183 data += line_data
184 a.write(ihex_line(i * self.line_length, 0, line_data))
185 a.flush()
186
187 self.gdb.command("restore %s 0x%x" % (a.name, self.hart.ram))
188 for offset in range(0, self.length, 19*4) + [self.length-4]:
189 value = self.gdb.p("*((int*)0x%x)" % (self.hart.ram + offset))
190 written = ord(data[offset]) | \
191 (ord(data[offset+1]) << 8) | \
192 (ord(data[offset+2]) << 16) | \
193 (ord(data[offset+3]) << 24)
194 assertEqual(value, written)
195
196 b = tempfile.NamedTemporaryFile(suffix=".ihex")
197 self.gdb.command("dump ihex memory %s 0x%x 0x%x" % (b.name,
198 self.hart.ram, self.hart.ram + self.length))
199 for line in b:
200 record_type, address, line_data = ihex_parse(line)
201 if record_type == 0:
202 written_data = data[address:address+len(line_data)]
203 if line_data != written_data:
204 raise TestFailed(
205 "Data mismatch at 0x%x; wrote %s but read %s" % (
206 address, readable_binary_string(written_data),
207 readable_binary_string(line_data)))
208
209 class InstantHaltTest(GdbTest):
210 def test(self):
211 """Assert that reset is really resetting what it should."""
212 self.gdb.command("monitor reset halt")
213 self.gdb.command("flushregs")
214 threads = self.gdb.threads()
215 pcs = []
216 for t in threads:
217 self.gdb.thread(t)
218 pcs.append(self.gdb.p("$pc"))
219 for pc in pcs:
220 assertEqual(self.hart.reset_vector, pc)
221 # mcycle and minstret have no defined reset value.
222 mstatus = self.gdb.p("$mstatus")
223 assertEqual(mstatus & (MSTATUS_MIE | MSTATUS_MPRV |
224 MSTATUS_VM), 0)
225
226 class InstantChangePc(GdbTest):
227 def test(self):
228 """Change the PC right as we come out of reset."""
229 # 0x13 is nop
230 self.gdb.command("monitor reset halt")
231 self.gdb.command("flushregs")
232 self.gdb.command("p *((int*) 0x%x)=0x13" % self.hart.ram)
233 self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 4))
234 self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + 8))
235 self.gdb.p("$pc=0x%x" % self.hart.ram)
236 self.gdb.stepi()
237 assertEqual((self.hart.ram + 4), self.gdb.p("$pc"))
238 self.gdb.stepi()
239 assertEqual((self.hart.ram + 8), self.gdb.p("$pc"))
240
241 class DebugTest(GdbSingleHartTest):
242 # Include malloc so that gdb can make function calls. I suspect this malloc
243 # will silently blow through the memory set aside for it, so be careful.
244 compile_args = ("programs/debug.c", "programs/checksum.c",
245 "programs/tiny-malloc.c", "-DDEFINE_MALLOC", "-DDEFINE_FREE")
246
247 def setup(self):
248 self.gdb.load()
249 self.gdb.b("_exit")
250
251 def exit(self, expected_result=0xc86455d4):
252 output = self.gdb.c()
253 assertIn("Breakpoint", output)
254 assertIn("_exit", output)
255 assertEqual(self.gdb.p("status"), expected_result)
256
257 class DebugCompareSections(DebugTest):
258 def test(self):
259 output = self.gdb.command("compare-sections")
260 matched = 0
261 for line in output.splitlines():
262 if line.startswith("Section"):
263 assert line.endswith("matched.")
264 matched += 1
265 assertGreater(matched, 1)
266
267 class DebugFunctionCall(DebugTest):
268 def test(self):
269 self.gdb.b("main:start")
270 self.gdb.c()
271 assertEqual(self.gdb.p('fib(6)'), 8)
272 assertEqual(self.gdb.p('fib(7)'), 13)
273 self.exit()
274
275 class DebugChangeString(DebugTest):
276 def test(self):
277 text = "This little piggy went to the market."
278 self.gdb.b("main:start")
279 self.gdb.c()
280 self.gdb.p('fox = "%s"' % text)
281 self.exit(0x43b497b8)
282
283 class DebugTurbostep(DebugTest):
284 def test(self):
285 """Single step a bunch of times."""
286 self.gdb.b("main:start")
287 self.gdb.c()
288 self.gdb.command("p i=0")
289 last_pc = None
290 advances = 0
291 jumps = 0
292 for _ in range(10):
293 self.gdb.stepi()
294 pc = self.gdb.p("$pc")
295 assertNotEqual(last_pc, pc)
296 if last_pc and pc > last_pc and pc - last_pc <= 4:
297 advances += 1
298 else:
299 jumps += 1
300 last_pc = pc
301 # Some basic sanity that we're not running between breakpoints or
302 # something.
303 assertGreater(jumps, 1)
304 assertGreater(advances, 5)
305
306 class DebugExit(DebugTest):
307 def test(self):
308 self.exit()
309
310 class DebugSymbols(DebugTest):
311 def test(self):
312 self.gdb.b("main")
313 self.gdb.b("rot13")
314 output = self.gdb.c()
315 assertIn(", main ", output)
316 output = self.gdb.c()
317 assertIn(", rot13 ", output)
318
319 class DebugBreakpoint(DebugTest):
320 def test(self):
321 self.gdb.b("rot13")
322 # The breakpoint should be hit exactly 2 times.
323 for _ in range(2):
324 output = self.gdb.c()
325 self.gdb.p("$pc")
326 assertIn("Breakpoint ", output)
327 assertIn("rot13 ", output)
328 self.exit()
329
330 class Hwbp1(DebugTest):
331 def test(self):
332 if self.hart.instruction_hardware_breakpoint_count < 1:
333 return 'not_applicable'
334
335 if not self.hart.honors_tdata1_hmode:
336 # Run to main before setting the breakpoint, because startup code
337 # will otherwise clear the trigger that we set.
338 self.gdb.b("main")
339 self.gdb.c()
340
341 self.gdb.hbreak("rot13")
342 # The breakpoint should be hit exactly 2 times.
343 for _ in range(2):
344 output = self.gdb.c()
345 self.gdb.p("$pc")
346 assertRegexpMatches(output, r"[bB]reakpoint")
347 assertIn("rot13 ", output)
348 self.exit()
349
350 class Hwbp2(DebugTest):
351 def test(self):
352 if self.hart.instruction_hardware_breakpoint_count < 2:
353 return 'not_applicable'
354
355 self.gdb.hbreak("main")
356 self.gdb.hbreak("rot13")
357 # We should hit 3 breakpoints.
358 for expected in ("main", "rot13", "rot13"):
359 output = self.gdb.c()
360 self.gdb.p("$pc")
361 assertRegexpMatches(output, r"[bB]reakpoint")
362 assertIn("%s " % expected, output)
363 self.exit()
364
365 class TooManyHwbp(DebugTest):
366 def run(self):
367 for i in range(30):
368 self.gdb.hbreak("*rot13 + %d" % (i * 4))
369
370 output = self.gdb.c()
371 assertIn("Cannot insert hardware breakpoint", output)
372 # Clean up, otherwise the hardware breakpoints stay set and future
373 # tests may fail.
374 self.gdb.command("D")
375
376 class Registers(DebugTest):
377 def test(self):
378 # Get to a point in the code where some registers have actually been
379 # used.
380 self.gdb.b("rot13")
381 self.gdb.c()
382 self.gdb.c()
383 # Try both forms to test gdb.
384 for cmd in ("info all-registers", "info registers all"):
385 output = self.gdb.command(cmd)
386 for reg in ('zero', 'ra', 'sp', 'gp', 'tp'):
387 assertIn(reg, output)
388
389 #TODO
390 # mcpuid is one of the few registers that should have the high bit set
391 # (for rv64).
392 # Leave this commented out until gdb and spike agree on the encoding of
393 # mcpuid (which is going to be renamed to misa in any case).
394 #assertRegexpMatches(output, ".*mcpuid *0x80")
395
396 #TODO:
397 # The instret register should always be changing.
398 #last_instret = None
399 #for _ in range(5):
400 # instret = self.gdb.p("$instret")
401 # assertNotEqual(instret, last_instret)
402 # last_instret = instret
403 # self.gdb.stepi()
404
405 self.exit()
406
407 class UserInterrupt(DebugTest):
408 def test(self):
409 """Sending gdb ^C while the program is running should cause it to
410 halt."""
411 self.gdb.b("main:start")
412 self.gdb.c()
413 self.gdb.p("i=123")
414 self.gdb.c(wait=False)
415 time.sleep(2)
416 output = self.gdb.interrupt()
417 assert "main" in output
418 assertGreater(self.gdb.p("j"), 10)
419 self.gdb.p("i=0")
420 self.exit()
421
422 class MulticoreRegTest(GdbTest):
423 compile_args = ("programs/infinite_loop.S", "-DMULTICORE")
424
425 def early_applicable(self):
426 return len(self.target.harts) > 1
427
428 def setup(self):
429 self.gdb.load()
430 for hart in self.target.harts:
431 self.gdb.select_hart(hart)
432 self.gdb.p("$pc=_start")
433
434 def test(self):
435 # Run to main
436 self.gdb.b("main")
437 self.gdb.c()
438 for t in self.gdb.threads():
439 assertIn("main", t.frame)
440 self.gdb.command("delete breakpoints")
441
442 # Run through the entire loop.
443 self.gdb.b("main_end")
444 self.gdb.c()
445
446 hart_ids = []
447 for t in self.gdb.threads():
448 assertIn("main_end", t.frame)
449 # Check register values.
450 self.gdb.thread(t)
451 hart_id = self.gdb.p("$x1")
452 assertNotIn(hart_id, hart_ids)
453 hart_ids.append(hart_id)
454 for n in range(2, 32):
455 value = self.gdb.p("$x%d" % n)
456 assertEqual(value, hart_ids[-1] + n - 1)
457
458 # Confirmed that we read different register values for different harts.
459 # Write a new value to x1, and run through the add sequence again.
460
461 for hart in self.target.harts:
462 self.gdb.select_hart(hart)
463 self.gdb.p("$x1=0x%x" % (hart.index * 0x800))
464 self.gdb.p("$pc=main_post_csrr")
465 self.gdb.c()
466 for t in self.gdb.threads():
467 assertIn("main_end", t.frame)
468 for hart in self.target.harts:
469 # Check register values.
470 self.gdb.select_hart(hart)
471 for n in range(1, 32):
472 value = self.gdb.p("$x%d" % n)
473 assertEqual(value, hart.index * 0x800 + n - 1)
474
475 class MulticoreRunHaltStepiTest(GdbTest):
476 compile_args = ("programs/multicore.c", "-DMULTICORE")
477
478 def early_applicable(self):
479 return len(self.target.harts) > 1
480
481 def setup(self):
482 self.gdb.load()
483 for hart in self.target.harts:
484 self.gdb.select_hart(hart)
485 self.gdb.p("$pc=_start")
486
487 def test(self):
488 previous_hart_count = [0 for h in self.target.harts]
489 for _ in range(10):
490 self.gdb.c(wait=False)
491 time.sleep(1)
492 self.gdb.interrupt()
493 self.gdb.p("buf", fmt="")
494 hart_count = self.gdb.p("hart_count")
495 for i, h in enumerate(self.target.harts):
496 assertGreater(hart_count[i], previous_hart_count[i])
497 self.gdb.select_hart(h)
498 pc = self.gdb.p("$pc")
499 self.gdb.stepi()
500 stepped_pc = self.gdb.p("$pc")
501 assertNotEqual(pc, stepped_pc)
502
503 class StepTest(GdbTest):
504 compile_args = ("programs/step.S", )
505
506 def setup(self):
507 self.gdb.load()
508 self.gdb.b("main")
509 self.gdb.c()
510
511 def test(self):
512 main_address = self.gdb.p("$pc")
513 if self.hart.extensionSupported("c"):
514 sequence = (4, 8, 0xc, 0xe, 0x14, 0x18, 0x22, 0x1c, 0x24, 0x24)
515 else:
516 sequence = (4, 8, 0xc, 0x10, 0x18, 0x1c, 0x28, 0x20, 0x2c, 0x2c)
517 for expected in sequence:
518 self.gdb.stepi()
519 pc = self.gdb.p("$pc")
520 assertEqual("%x" % (pc - main_address), "%x" % expected)
521
522 class TriggerTest(GdbTest):
523 compile_args = ("programs/trigger.S", )
524 def setup(self):
525 self.gdb.load()
526 self.gdb.b("_exit")
527 self.gdb.b("main")
528 self.gdb.c()
529
530 def exit(self):
531 output = self.gdb.c()
532 assertIn("Breakpoint", output)
533 assertIn("_exit", output)
534
535 class TriggerExecuteInstant(TriggerTest):
536 """Test an execute breakpoint on the first instruction executed out of
537 debug mode."""
538 def test(self):
539 main_address = self.gdb.p("$pc")
540 self.gdb.command("hbreak *0x%x" % (main_address + 4))
541 self.gdb.c()
542 assertEqual(self.gdb.p("$pc"), main_address+4)
543
544 # FIXME: Triggers aren't quite working yet
545 #class TriggerLoadAddress(TriggerTest):
546 # def test(self):
547 # self.gdb.command("rwatch *((&data)+1)")
548 # output = self.gdb.c()
549 # assertIn("read_loop", output)
550 # assertEqual(self.gdb.p("$a0"),
551 # self.gdb.p("(&data)+1"))
552 # self.exit()
553
554 class TriggerLoadAddressInstant(TriggerTest):
555 """Test a load address breakpoint on the first instruction executed out of
556 debug mode."""
557 def test(self):
558 self.gdb.command("b just_before_read_loop")
559 self.gdb.c()
560 read_loop = self.gdb.p("&read_loop")
561 self.gdb.command("rwatch data")
562 self.gdb.c()
563 # Accept hitting the breakpoint before or after the load instruction.
564 assertIn(self.gdb.p("$pc"), [read_loop, read_loop + 4])
565 assertEqual(self.gdb.p("$a0"), self.gdb.p("&data"))
566
567 # FIXME: Triggers aren't quite working yet
568 #class TriggerStoreAddress(TriggerTest):
569 # def test(self):
570 # self.gdb.command("watch *((&data)+3)")
571 # output = self.gdb.c()
572 # assertIn("write_loop", output)
573 # assertEqual(self.gdb.p("$a0"),
574 # self.gdb.p("(&data)+3"))
575 # self.exit()
576
577 class TriggerStoreAddressInstant(TriggerTest):
578 def test(self):
579 """Test a store address breakpoint on the first instruction executed out
580 of debug mode."""
581 self.gdb.command("b just_before_write_loop")
582 self.gdb.c()
583 write_loop = self.gdb.p("&write_loop")
584 self.gdb.command("watch data")
585 self.gdb.c()
586 # Accept hitting the breakpoint before or after the store instruction.
587 assertIn(self.gdb.p("$pc"), [write_loop, write_loop + 4])
588 assertEqual(self.gdb.p("$a0"), self.gdb.p("&data"))
589
590 class TriggerDmode(TriggerTest):
591 def early_applicable(self):
592 return self.hart.honors_tdata1_hmode
593
594 def check_triggers(self, tdata1_lsbs, tdata2):
595 dmode = 1 << (self.hart.xlen-5)
596
597 triggers = []
598
599 if self.hart.xlen == 32:
600 xlen_type = 'int'
601 elif self.hart.xlen == 64:
602 xlen_type = 'long long'
603 else:
604 raise NotImplementedError
605
606 dmode_count = 0
607 i = 0
608 for i in range(16):
609 tdata1 = self.gdb.p("((%s *)&data)[%d]" % (xlen_type, 2*i))
610 if tdata1 == 0:
611 break
612 tdata2 = self.gdb.p("((%s *)&data)[%d]" % (xlen_type, 2*i+1))
613
614 if tdata1 & dmode:
615 dmode_count += 1
616 else:
617 assertEqual(tdata1 & 0xffff, tdata1_lsbs)
618 assertEqual(tdata2, tdata2)
619
620 assertGreater(i, 1)
621 assertEqual(dmode_count, 1)
622
623 return triggers
624
625 def test(self):
626 self.gdb.command("hbreak write_load_trigger")
627 self.gdb.b("clear_triggers")
628 self.gdb.p("$pc=write_store_trigger")
629 output = self.gdb.c()
630 assertIn("write_load_trigger", output)
631 self.check_triggers((1<<6) | (1<<1), 0xdeadbee0)
632 output = self.gdb.c()
633 assertIn("clear_triggers", output)
634 self.check_triggers((1<<6) | (1<<0), 0xfeedac00)
635
636 class RegsTest(GdbTest):
637 compile_args = ("programs/regs.S", )
638 def setup(self):
639 self.gdb.load()
640 self.gdb.b("main")
641 self.gdb.b("handle_trap")
642 self.gdb.c()
643
644 class WriteGprs(RegsTest):
645 def test(self):
646 regs = [("x%d" % n) for n in range(2, 32)]
647
648 self.gdb.p("$pc=write_regs")
649 for i, r in enumerate(regs):
650 self.gdb.p("$%s=%d" % (r, (0xdeadbeef<<i)+17))
651 self.gdb.p("$x1=data")
652 self.gdb.command("b all_done")
653 output = self.gdb.c()
654 assertIn("Breakpoint ", output)
655
656 # Just to get this data in the log.
657 self.gdb.command("x/30gx data")
658 self.gdb.command("info registers")
659 for n in range(len(regs)):
660 assertEqual(self.gdb.x("data+%d" % (8*n), 'g'),
661 ((0xdeadbeef<<n)+17) & ((1<<self.hart.xlen)-1))
662
663 class WriteCsrs(RegsTest):
664 def test(self):
665 # As much a test of gdb as of the simulator.
666 self.gdb.p("$mscratch=0")
667 self.gdb.stepi()
668 assertEqual(self.gdb.p("$mscratch"), 0)
669 self.gdb.p("$mscratch=123")
670 self.gdb.stepi()
671 assertEqual(self.gdb.p("$mscratch"), 123)
672
673 self.gdb.p("$pc=write_regs")
674 self.gdb.p("$x1=data")
675 self.gdb.command("b all_done")
676 self.gdb.command("c")
677
678 assertEqual(123, self.gdb.p("$mscratch"))
679 assertEqual(123, self.gdb.p("$x1"))
680 assertEqual(123, self.gdb.p("$csr832"))
681
682 class DownloadTest(GdbTest):
683 def setup(self):
684 # pylint: disable=attribute-defined-outside-init
685 length = min(2**10, self.hart.ram_size - 2048)
686 self.download_c = tempfile.NamedTemporaryFile(prefix="download_",
687 suffix=".c", delete=False)
688 self.download_c.write("#include <stdint.h>\n")
689 self.download_c.write(
690 "unsigned int crc32a(uint8_t *message, unsigned int size);\n")
691 self.download_c.write("uint32_t length = %d;\n" % length)
692 self.download_c.write("uint8_t d[%d] = {\n" % length)
693 self.crc = 0
694 assert length % 16 == 0
695 for i in range(length / 16):
696 self.download_c.write(" /* 0x%04x */ " % (i * 16))
697 for _ in range(16):
698 value = random.randrange(1<<8)
699 self.download_c.write("0x%02x, " % value)
700 self.crc = binascii.crc32("%c" % value, self.crc)
701 self.download_c.write("\n")
702 self.download_c.write("};\n")
703 self.download_c.write("uint8_t *data = &d[0];\n")
704 self.download_c.write(
705 "uint32_t main() { return crc32a(data, length); }\n")
706 self.download_c.flush()
707
708 if self.crc < 0:
709 self.crc += 2**32
710
711 self.binary = self.target.compile(self.hart, self.download_c.name,
712 "programs/checksum.c")
713 self.gdb.command("file %s" % self.binary)
714
715 def test(self):
716 self.gdb.load()
717 self.gdb.command("b _exit")
718 self.gdb.c(timeout=60)
719 assertEqual(self.gdb.p("status"), self.crc)
720 os.unlink(self.download_c.name)
721
722 # FIXME: PRIV isn't implemented in the current OpenOCD
723 #class MprvTest(GdbTest):
724 # compile_args = ("programs/mprv.S", )
725 # def setup(self):
726 # self.gdb.load()
727 #
728 # def test(self):
729 # """Test that the debugger can access memory when MPRV is set."""
730 # self.gdb.c(wait=False)
731 # time.sleep(0.5)
732 # self.gdb.interrupt()
733 # output = self.gdb.command("p/x *(int*)(((char*)&data)-0x80000000)")
734 # assertIn("0xbead", output)
735 #
736 #class PrivTest(GdbTest):
737 # compile_args = ("programs/priv.S", )
738 # def setup(self):
739 # # pylint: disable=attribute-defined-outside-init
740 # self.gdb.load()
741 #
742 # misa = self.hart.misa
743 # self.supported = set()
744 # if misa & (1<<20):
745 # self.supported.add(0)
746 # if misa & (1<<18):
747 # self.supported.add(1)
748 # if misa & (1<<7):
749 # self.supported.add(2)
750 # self.supported.add(3)
751 #
752 #class PrivRw(PrivTest):
753 # def test(self):
754 # """Test reading/writing priv."""
755 # for privilege in range(4):
756 # self.gdb.p("$priv=%d" % privilege)
757 # self.gdb.stepi()
758 # actual = self.gdb.p("$priv")
759 # assertIn(actual, self.supported)
760 # if privilege in self.supported:
761 # assertEqual(actual, privilege)
762 #
763 #class PrivChange(PrivTest):
764 # def test(self):
765 # """Test that the core's privilege level actually changes."""
766 #
767 # if 0 not in self.supported:
768 # return 'not_applicable'
769 #
770 # self.gdb.b("main")
771 # self.gdb.c()
772 #
773 # # Machine mode
774 # self.gdb.p("$priv=3")
775 # main_address = self.gdb.p("$pc")
776 # self.gdb.stepi()
777 # assertEqual("%x" % self.gdb.p("$pc"), "%x" % (main_address+4))
778 #
779 # # User mode
780 # self.gdb.p("$priv=0")
781 # self.gdb.stepi()
782 # # Should have taken an exception, so be nowhere near main.
783 # pc = self.gdb.p("$pc")
784 # assertTrue(pc < main_address or pc > main_address + 0x100)
785
786 parsed = None
787 def main():
788 parser = argparse.ArgumentParser(
789 description="Test that gdb can talk to a RISC-V target.",
790 epilog="""
791 Example command line from the real world:
792 Run all RegsTest cases against a physical FPGA, with custom openocd command:
793 ./gdbserver.py --freedom-e300 --server_cmd "$HOME/SiFive/openocd/src/openocd -s $HOME/SiFive/openocd/tcl -d" Simple
794 """)
795 targets.add_target_options(parser)
796
797 testlib.add_test_run_options(parser)
798
799 # TODO: remove global
800 global parsed # pylint: disable=global-statement
801 parsed = parser.parse_args()
802 target = targets.target(parsed)
803
804 if parsed.xlen:
805 target.xlen = parsed.xlen
806
807 module = sys.modules[__name__]
808
809 return testlib.run_all_tests(module, target, parsed)
810
811 # TROUBLESHOOTING TIPS
812 # If a particular test fails, run just that one test, eg.:
813 # ./gdbserver.py MprvTest.test_mprv
814 # Then inspect gdb.log and spike.log to see what happened in more detail.
815
816 if __name__ == '__main__':
817 sys.exit(main())