1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
# This is a simple guide to using AVR-GDB with simavr
First of all, you can use the [Makefile](../Makefile) in the directory above.
But for the competeness sake, I'll list commands
## Running simulation
QEMU: I don't personally prefer it, but here is a breakdown:
```sh
qemu-system-avr -M arduino-uno -bios <image> -nographic -s -S
```
* `-M`: machine to use, shorthand for `--machine`. Self-explanatory.
* `-bios`: the thing that you would flash onto your arduino. Note that this is
not a hex file, this must be an elf file, the one you get out of `ld`.
* `-nographic`: this should be default to be honest
* `-s`: shorthand for `-gdb tcp::1234`. Enables GDB debugging
* `-S`: suspend execution and wait for debugger to connect.
This should be everything you need.
[More documentation] (https://www.qemu.org/docs/master/system/target-avr.html)
SIMAVR: This is the one I prefer
First of all, the `--help` is actually not as big so it's easy to guess
everything yourself, but fine, here is the command:
```sh
simavr -m atmega328p -g <image>
```
I think you can already see why I like it more. Breakdown of flags:
* `-m, --mcu` stands for "MicroController Unit". atmega328p is the processor arduino
uno uses
* `-g, --gdb`: wait for gdb to connect on `:1234`
* `-f, --freq`: optionally specify the clock frequency, arduino uno has 16MHz
You can also interrupt the process to kill it! Qemu does not let you do that.
## gdb
```sh
$ avr-gdb <image>
(gdb) target remote :1234
(gdb) c
```
First of all, load the image. Then connect to the simulation. Since this is not
a program in a traditional sense, it's already running and you have to
`continue` instead of `run`.
* to break at an address, use `b *0x69`. also, 0x69 is odd, so you will not land
there. Instructions are either 16 bit or 32 bit on AVR, so almost any even
address is a valid instruction.
* to print instructions at some address, use `x/16i 0x420`, replace 16 with
amount of instructions and 0x420 with an actual address. you can also use
`$pc`, which stands for program counter (current instruction) as an address.
Also, unlike on intel/arm64, you can safely write `x/4i $pc-4` and get a valid
disassembly.
* To print register values, use `print $r16`, replace `r16` with any register
you want. For formatted printing, see `help x` in gdb shell, it is often
useful to `print/x` in hexadecimal or `print/t` in binary.
* use `displ` command. You will probably want to track current instruction, 1 or
2 registers and some IO memory addresses. For me, a "must have" is
`displ/i $pc`, which displays an instruction to be executed each time the
execution avdances. really useful for stepping
* `si` command steps 1 assembly insstruction. you will find yourself using it a
lot
* To print IO registers, use `print *(unsigned char*)0xADD12E55`. NOTE that, if
you can use `in/out` instruction to access a memory location, gdb can't, it
will always use `sts/lds`. So don't forget `0x20` to all IO locations before
`0x3F`. For example, if you want to track `PORTB`, you'd often write
`displ/t *(unsigned char)0x25`, even though documentation says the `PORTB` is
at `0x05`.
* To restart the program without restarting the simulation, you can write
`set $pc = 0`. This will trick a processor into thinking that next instruction
is at `0x00`, which is a reset vector. Note that state of your registers does
not reset. The set command it useful in many places, for example you want a
loop to execute for a little longer - you can set the counter register to 0.
* you can safely put breakpoints at interrupts, at any labels you defined,
anywhere.
* I coudn't get `compile code` gdb instruction to work (to execute assembly
on-the-fly), because it can't find the avr C compiler. Maybe this is a
distribution misconfiguration. Didn't bother setting up stuff in a debian VM.
This is it! Hope this was helpful.
P.S.: Internet has a lof of `avr-gdb` guides, and even more "just `gdb`" guides,
which are as relevant. Search them up and have fun debugging!
JAC, 2025
|