13 def ihex_line(address
, record_type
, data
):
14 assert len(data
) < 128
15 line
= ":%02X%04X%02X" % (len(data
), address
, record_type
)
17 check
+= address
% 256
23 line
+= "%02X" % value
24 line
+= "%02X\n" % ((256-check
)%256)
28 assert line
.startswith(":")
30 data_len
= int(line
[:2], 16)
31 address
= int(line
[2:6], 16)
32 record_type
= int(line
[6:8], 16)
34 for i
in range(data_len
):
35 data
+= "%c" % int(line
[8+2*i
:10+2*i
], 16)
36 return record_type
, address
, data
38 class DeleteServer(unittest
.TestCase
):
42 class MemoryTest(DeleteServer
):
44 self
.server
= target
.server()
45 self
.gdb
= testlib
.Gdb()
46 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
48 def access_test(self
, size
, data_type
):
49 a
= 0x86753095555aaaa & ((1<<(size
*8))-1)
50 b
= 0xdeadbeef12345678 & ((1<<(size
*8))-1)
51 self
.gdb
.p("*((%s*)0x%x) = 0x%x" % (data_type
, target
.ram
, a
))
52 self
.gdb
.p("*((%s*)0x%x) = 0x%x" % (data_type
, target
.ram
+ size
, b
))
53 self
.assertEqual(self
.gdb
.p("*((%s*)0x%x)" % (data_type
, target
.ram
)), a
)
54 self
.assertEqual(self
.gdb
.p("*((%s*)0x%x)" % (data_type
, target
.ram
+ size
)), b
)
57 self
.access_test(1, 'char')
60 self
.access_test(2, 'short')
63 self
.access_test(4, 'long')
66 self
.access_test(8, 'long long')
71 fd
= file("write.ihex", "w")
73 for i
in range(length
/ line_length
):
74 line_data
= "".join(["%c" % random
.randrange(256) for _
in range(line_length
)])
76 fd
.write(ihex_line(i
* line_length
, 0, line_data
))
79 self
.gdb
.command("restore write.ihex 0x%x" % target
.ram
)
80 for offset
in range(0, length
, 19*4) + [length
-4]:
81 value
= self
.gdb
.p("*((long*)0x%x)" % (target
.ram
+ offset
))
82 written
= ord(data
[offset
]) | \
83 (ord(data
[offset
+1]) << 8) | \
84 (ord(data
[offset
+2]) << 16) | \
85 (ord(data
[offset
+3]) << 24)
86 self
.assertEqual(value
, written
)
88 self
.gdb
.command("dump ihex memory read.ihex 0x%x 0x%x" % (target
.ram
,
90 for line
in file("read.ihex"):
91 record_type
, address
, line_data
= ihex_parse(line
)
92 if (record_type
== 0):
93 self
.assertEqual(line_data
, data
[address
:address
+len(line_data
)])
95 class InstantHaltTest(DeleteServer
):
97 self
.server
= target
.server()
98 self
.gdb
= testlib
.Gdb()
99 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
101 def test_instant_halt(self
):
102 self
.assertEqual(0x1000, self
.gdb
.p("$pc"))
103 # For some reason instret resets to 0.
104 self
.assertLess(self
.gdb
.p("$instret"), 8)
106 self
.assertNotEqual(0x1000, self
.gdb
.p("$pc"))
108 def test_change_pc(self
):
109 """Change the PC right as we come out of reset."""
111 self
.gdb
.command("p *((int*) 0x%x)=0x13" % target
.ram
)
112 self
.gdb
.command("p *((int*) 0x%x)=0x13" % (target
.ram
+ 4))
113 self
.gdb
.command("p *((int*) 0x%x)=0x13" % (target
.ram
+ 8))
114 self
.gdb
.p("$pc=0x%x" % target
.ram
)
116 self
.assertEqual((target
.ram
+ 4), self
.gdb
.p("$pc"))
118 self
.assertEqual((target
.ram
+ 4), self
.gdb
.p("$pc"))
120 class DebugTest(DeleteServer
):
122 self
.binary
= target
.compile("programs/debug.c", "programs/checksum.c")
123 self
.server
= target
.server()
124 self
.gdb
= testlib
.Gdb()
125 self
.gdb
.command("file %s" % self
.binary
)
126 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
131 output
= self
.gdb
.c()
132 self
.assertIn("Breakpoint", output
)
133 self
.assertIn("_exit", output
)
134 self
.assertEqual(self
.gdb
.p("status"), 0xc86455d4)
136 def test_turbostep(self
):
137 """Single step a bunch of times."""
138 self
.gdb
.command("p i=0");
142 pc
= self
.gdb
.command("p $pc")
143 self
.assertNotEqual(last_pc
, pc
)
149 def test_breakpoint(self
):
151 # The breakpoint should be hit exactly 2 times.
153 output
= self
.gdb
.c()
154 self
.assertIn("Breakpoint ", output
)
157 def test_registers(self
):
158 # Get to a point in the code where some registers have actually been
163 # Try both forms to test gdb.
164 for cmd
in ("info all-registers", "info registers all"):
165 output
= self
.gdb
.command(cmd
)
166 self
.assertNotIn("Could not", output
)
167 for reg
in ('zero', 'ra', 'sp', 'gp', 'tp'):
168 self
.assertIn(reg
, output
)
171 # mcpuid is one of the few registers that should have the high bit set
173 # Leave this commented out until gdb and spike agree on the encoding of
174 # mcpuid (which is going to be renamed to misa in any case).
175 #self.assertRegexpMatches(output, ".*mcpuid *0x80")
178 # The instret register should always be changing.
181 # instret = self.gdb.p("$instret")
182 # self.assertNotEqual(instret, last_instret)
183 # last_instret = instret
188 def test_interrupt(self
):
189 """Sending gdb ^C while the program is running should cause it to halt."""
190 self
.gdb
.b("main:start")
192 self
.gdb
.command("p i=123");
193 self
.gdb
.c(wait
=False)
195 output
= self
.gdb
.interrupt()
196 assert "main" in output
197 self
.assertGreater(self
.gdb
.p("j"), 10)
201 class RegsTest(DeleteServer
):
203 self
.binary
= target
.compile("programs/regs.S")
204 self
.server
= target
.server()
205 self
.gdb
= testlib
.Gdb()
206 self
.gdb
.command("file %s" % self
.binary
)
207 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
210 self
.gdb
.b("handle_trap")
213 def test_write_gprs(self
):
214 # Note a0 is missing from this list since it's used to hold the
216 regs
= ("ra", "sp", "gp", "tp", "t0", "t1", "t2", "fp", "s1",
217 "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", "s4",
218 "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5",
221 self
.gdb
.p("$pc=write_regs")
222 for i
, r
in enumerate(regs
):
223 self
.gdb
.command("p $%s=%d" % (r
, (0xdeadbeef<<i
)+17))
224 self
.gdb
.command("p $a0=data")
225 self
.gdb
.command("b all_done")
226 output
= self
.gdb
.c()
227 self
.assertIn("Breakpoint ", output
)
229 # Just to get this data in the log.
230 self
.gdb
.command("x/30gx data")
231 self
.gdb
.command("info registers")
232 for n
in range(len(regs
)):
233 self
.assertEqual(self
.gdb
.x("data+%d" % (8*n
), 'g'),
236 def test_write_csrs(self
):
237 # As much a test of gdb as of the simulator.
238 self
.gdb
.p("$mscratch=0")
240 self
.assertEqual(self
.gdb
.p("$mscratch"), 0)
241 self
.gdb
.p("$mscratch=123")
243 self
.assertEqual(self
.gdb
.p("$mscratch"), 123)
245 self
.gdb
.command("p $pc=write_regs")
246 self
.gdb
.command("p $a0=data")
247 self
.gdb
.command("b all_done")
248 self
.gdb
.command("c")
250 self
.assertEqual(123, self
.gdb
.p("$mscratch"))
251 self
.assertEqual(123, self
.gdb
.p("$x1"))
252 self
.assertEqual(123, self
.gdb
.p("$csr832"))
254 class DownloadTest(DeleteServer
):
257 fd
= file("download.c", "w")
258 fd
.write("#include <stdint.h>\n")
259 fd
.write("unsigned int crc32a(uint8_t *message, unsigned int size);\n")
260 fd
.write("uint32_t length = %d;\n" % length
)
261 fd
.write("uint8_t d[%d] = {\n" % length
)
263 for i
in range(length
/ 16):
264 fd
.write(" /* 0x%04x */ " % (i
* 16));
266 value
= random
.randrange(1<<8)
267 fd
.write("%d, " % value
)
268 self
.crc
= binascii
.crc32("%c" % value
, self
.crc
)
271 fd
.write("uint8_t *data = &d[0];\n");
272 fd
.write("uint32_t main() { return crc32a(data, length); }\n")
278 self
.binary
= target
.compile("download.c", "programs/checksum.c")
279 self
.server
= target
.server()
280 self
.gdb
= testlib
.Gdb()
281 self
.gdb
.command("file %s" % self
.binary
)
282 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
284 def test_download(self
):
285 output
= self
.gdb
.load()
286 self
.gdb
.command("b _exit")
288 self
.assertEqual(self
.gdb
.p("status"), self
.crc
)
290 class MprvTest(DeleteServer
):
292 self
.binary
= target
.compile("programs/mprv.S")
293 self
.server
= target
.server()
294 self
.gdb
= testlib
.Gdb()
295 self
.gdb
.command("file %s" % self
.binary
)
296 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
300 """Test that the debugger can access memory when MPRV is set."""
301 self
.gdb
.c(wait
=False)
303 output
= self
.gdb
.command("p/x *(int*)(((char*)&data)-0x80000000)")
304 self
.assertIn("0xbead", output
)
306 class Target(object):
308 raise NotImplementedError
310 def compile(self
, *sources
):
311 return testlib
.compile(sources
+
312 ("programs/entry.S", "programs/init.c",
314 "-T", "targets/%s/link.lds" % self
.name
,
316 "-mcmodel=medany"), xlen
=self
.xlen
)
318 class SpikeTarget(Target
):
324 return testlib
.Spike(parsed
.cmd
, halted
=True)
326 class MicroSemiTarget(Target
):
332 return testlib
.Openocd(cmd
=parsed
.cmd
,
333 config
="targets/%s/openocd.cfg" % self
.name
)
341 parser
= argparse
.ArgumentParser(
343 Example command line from the real world:
344 Run all RegsTest cases against a MicroSemi m2gl_m2s board, with custom openocd command:
345 ./gdbserver.py --m2gl_m2s --cmd "$HOME/SiFive/openocd/src/openocd -s $HOME/SiFive/openocd/tcl -d" -- -vf RegsTest
347 group
= parser
.add_mutually_exclusive_group(required
=True)
349 group
.add_argument("--%s" % t
.name
, action
="store_const", const
=t
,
351 parser
.add_argument("--cmd",
352 help="The command to use to start the debug server.")
353 parser
.add_argument("unittest", nargs
="*")
355 parsed
= parser
.parse_args()
358 target
= parsed
.target()
359 unittest
.main(argv
=[sys
.argv
[0]] + parsed
.unittest
)
361 # TROUBLESHOOTING TIPS
362 # If a particular test fails, run just that one test, eg.:
363 # ./tests/gdbserver.py MprvTest.test_mprv
364 # Then inspect gdb.log and spike.log to see what happened in more detail.
366 if __name__
== '__main__':