avrdudes / avarice

AVaRICE is a program for interfacing the Atmel JTAG ICE to GDB to allow users to debug their embedded AVR target.
GNU General Public License v2.0
35 stars 11 forks source link

[DebugWire/GDB] Cannot execute command while target is running #87

Open SerialDestructor opened 2 years ago

SerialDestructor commented 2 years ago

I'm trying to get AVaRICE working with PlatformIO and CLion for debugging (using debugwire). It seems pretty straight forward, but I can't get it working.

The debugger initializes and shows a (suspiciously empty) hex file. From there, it just freezes. I can't add any breakpoints, pause or resume don't work. The error from the GDB server is "Cannot execute command while target is running".

I get similar results when I directly access the GDB server, so I suspect it's not related to the IDE I'm using.

Also, is it nessecary to flash the firmware before using AVaRICE? I can't find a way to disable the DWEN fuse on macOS, as with the GDB server open the USB port is occupied and I can't launch avrdude to do so.

Sorry for creating a bug report for this, I couldn't find any other ways to get in touch.

I might add that debugging is working fine in Microchip Studio - obviously. But I would like to use an open source toolchain as it's not available on macOS and MPLabX doesn't fit my needs.

dl8dtl commented 2 years ago

(Btw., there is a "Discussions" section as well.)

First of all, you need AVRDUDE to the very least to program the DWEN fuse. Usually, you'd also want to use it for uploading flash images, albeit once debugWIRE is enabled and the GDB server works correctly, you are also supposed to be able to reprogram the target using the load command in GDB. I always found ISP mode to be somewhat faster and more reliable though.

debugWIRE is a bit of a strange beast: it's not a genuine debugging mode (as JTAG or UPDI is), but rather a monitor ROM. (You can see that still in one spot in the docs, where it says Reset with MonCon disabled.) That primarily means that you only can do those things in the debugger the CPU can do – altering fuses explicitly does not belong to that feature set. Likewise, there are no data breakpoints ("watchpoint" in GDB terms), and code breakpoints are handled by the ICE reflashing the respective flash page with a BREAK instruction in place of the original instruction. This causes quite a bit of flash wearout, but it's nevertheless surprisingly fast.

The bad thing with that is, until you enable the DWEN fuse, you don't know whether your entire debugWIRE setup is even working at all. At the moment you are setting it, the next power cycle causes the fuse to be evaluated, and the /RESET pin turned into a debugWIRE communication pin. Note that debugWIRE, being a one-wire bus, imposes quite some constraints on the circuitry connected to /RESET: there must not be too much capacitance on it, so keep your cabling short (just 10 … 20 cm, including the ICE cable), and by all means, no additional capacitors must be on the pin. I found that it sometimes helps to have an additional 4.7 or 10 kΩ pullup attached.

In order to return from debugWIRE mode, the ICE has to issue the mentioned Reset with MonCon disabled instruction. The next reset (but no power-on!) then temporarily returns the /RESET pin to its original function, so normal ISP can proceed. This state is maintained until the next power cycle, when the fuses are re-evaluated. If DWEN is still enabled, debugWIRE will continue.

The downside is that such a setup cannot be operated without the option to occasionally power-cycle the target, for obvious reasons.

AVRDUDE handles all that by starting in normal ISP mode. When it fails to enter ISP mode on a target that has debugWIRE capabilities, it next attempts to issue the respective "return from debugWIRE" command, and restarts the ISP initialization. If debugWIRE did work, this ISP session can then be used to upload new flash contents, and/or to alter fuses. (Unlike Atmel/Microchip Studio, which then always turns off DWEN, AVRDUDE doesn't implicitly do that for you. If you want to turn off DWEN, you have to issue the respective -U hfuse:w:xxx command for that – but you are not required to, if you intend to remain in debugWIRE mode.)

I hope that made the entire picture a bit clearer … if anyone feels like it, we could start a set of "recipes" or such in Wiki to record that as hints for the users.

SerialDestructor commented 2 years ago

Thank you for the awesome in-depth writeup. But this only answers the question:

Also, is it nessecary to flash the firmware before using AVaRICE? I can't find a way to disable the DWEN fuse on macOS, as with the GDB server open the USB port is occupied and I can't launch avrdude to do so.

I still have no clue what causes the "Cannot execute command while target is running". Do you have any clue? The parameters I'm using are:

./avarice --edbg --debugwire :4242

Seems pretty standard...

dl8dtl commented 2 years ago

What you are showing is only the AVaRICE side (the GDB server). How does your AVR-GDB side look like?

SerialDestructor commented 2 years ago

What exactly do you want to see?

dl8dtl commented 2 years ago

What arre yoiur AVR-GDB commands used?

Here is a full transaction. First, AVRDUDE and AVaRICE side:

$ avrdude -c xplainedmini -p atmega168pb -U hfuse:w:0x9f:m

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.04s

avrdude: Device signature = 0x1e9415 (probably m168pb)
avrdude: reading input file "0x9f"
avrdude: writing hfuse (1 bytes):

Writing | ################################################## | 100% 0.03s

avrdude: 1 bytes of hfuse written
avrdude: verifying hfuse memory against 0x9f:
avrdude: load data hfuse data from input file 0x9f:
avrdude: input file 0x9f contains 1 bytes
avrdude: reading on-chip hfuse data:

Reading | ################################################## | 100% 0.01s

avrdude: verifying ...
avrdude: 1 bytes of hfuse verified

avrdude: safemode: Fuses OK (E:FF, H:9F, L:E0)

avrdude done.  Thank you.

$ avarice --debugwire --edbg --part atmega168p :4242
AVaRICE version 2.14svn20200906, Mar 19 2021 22:27:18

JTAG config starting.
Found a device, serial number: ATML2222050200007574
Reported device ID: 0x9415
Configured for device ID: 0x940B atmega168p -- FORCED with atmega168p
JTAG config complete.
Preparing the target device for On Chip Debugging.
Waiting for connection on port 4242.

(At that point, it just waits for GDB to connect. That's the part quoted below.)

Connection opened by host 127.0.0.1, port 61965.
gdb exited.
$ avrdude -c xplainedmini -p atmega168pb -U hfuse:w:0xdf:m

avrdude: stk500v2_command(): command failed
avrdude: Target prepared for ISP, signed off.
avrdude: Now retrying without power-cycling the target.
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.05s

avrdude: Device signature = 0x1e9415 (probably m168pb)
avrdude: reading input file "0xdf"
avrdude: writing hfuse (1 bytes):

Writing | ################################################## | 100% 0.03s

avrdude: 1 bytes of hfuse written
avrdude: verifying hfuse memory against 0xdf:
avrdude: load data hfuse data from input file 0xdf:
avrdude: input file 0xdf contains 1 bytes
avrdude: reading on-chip hfuse data:

Reading | ################################################## | 100% 0.01s

avrdude: verifying ...
avrdude: 1 bytes of hfuse verified

avrdude: safemode: Fuses OK (E:FF, H:DF, L:E0)

avrdude done.  Thank you.

(I have to force an ATmega168P because AVaRICE currently doesn't know about the ATmega168PB used.)

Here is the GDB end:

$ avr-gdb demo.elf
GNU gdb (GDB) 7.3.1
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=amd64-portbld-freebsd10.0 --target=avr".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /tmp/demo.elf...done.
(gdb) target extended-remote :4242
Remote debugging using :4242
0x00000000 in __vectors ()
(gdb) disp/i $pc
1: x/i $pc
=> 0x0 <__vectors>:     jmp     0x68    ;  0x68 <__trampolines_start>
(gdb) s
Single stepping until exit from function __vectors,
which has no line number information.
0x00000068 in __trampolines_start ()
1: x/i $pc
=> 0x68 <__trampolines_start>:  eor     r1, r1
(gdb) 
Single stepping until exit from function __trampolines_start,
which has no line number information.
__do_clear_bss () at ../../.././libgcc/config/avr/lib1funcs.S:2437
2437    ../../.././libgcc/config/avr/lib1funcs.S: No such file or directory.
        in ../../.././libgcc/config/avr/lib1funcs.S
1: x/i $pc
=> 0x74 <__do_clear_bss>:       ldi     r18, 0x01       ; 1
(gdb) detach
Detached from remote process.
(gdb)
SerialDestructor commented 2 years ago

Your instructions work, as I expected.

I had one more go at it, and this time captured the traffic between GDB and the server.

This is the traffic when PlatformIO is involved:

https://pastebin.com/k47erNvv (and the gdb log) https://pastebin.com/96LJdJ8B

I tried to add a breakpoint here, but it still got me the "Cannot execute command while target is running" error.

And this is the traffic when PlatformIO is not involved - so AVaRICE directly to CLion:

https://pastebin.com/CHSgVa9u (and the gdb log) https://pastebin.com/GwRxNwCf

When I tried to add a breakpoint in the latter scenario, it replied with Program received signal SIGINT, Interrupt. 0x00000000 in __vectors ()

Reading this traffic is beyond my knowledge, but maybe you might spot something...

dl8dtl commented 2 years ago

Well, I don't really have an idea what PIO is all doing here. (I've been using PIO only on Cortex-M devices in the past.) I suggest asking in a PIO forum what they are using to attach to a remote AVR-GDB.

SerialDestructor commented 2 years ago

What I also found is that debugging seems to work (it breaks correctly on the breakpoints), but there is no feedback in the IDE. For example, when I place a breakpoint in a very simple LED blink program (on delay), it stops. However, the IDE doesn't report anything about it. Clearing the breakpoint gets the execution going again.

Could it be that the GDB client of CLion tries to use some GDB options the gdb server of AVaRICE doesn't support?

Remember, this is not via PIO, but CLion directly launching AVaRICE and attaching to it. (See also the last log link in my previous post, no PIO involved there)

dl8dtl commented 2 years ago

Could it be that the GDB client of CLion tries to use some GDB options the gdb server of AVaRICE doesn't support?

Sure, if they try to directly forward GDB server commands ("monitor" command in GDB), this can happen. These commands are always specific to the actual GDB server implementation. For example, to trigger an MCU reset, with AVaRICE you say (inside GDB) "monitor reset", while in OpenOCD (e.g for Cortex-M) you say "monitor reset halt".

Can you upload a full minimal project? Sorry, I don't have the slightest idea about what CLion does.

dl8dtl commented 2 years ago

One point a friend just remarked: are you sure the AVaRICE versions used by Platform.IO and on your commandline are the same? After all, --edbg support is a relatively recent addition in terms of a release (2020-08-31, version 2.14).

Keep in mind that Platform.IO tends to prefer its own locally installed tools over the system-wide tool installations.

SerialDestructor commented 2 years ago

PIO doesn't have AVaRICE by itself - it supports using a custom debugger. I cloned the repository and used compiled it.

I'm using CLion 2021.3.3.

This is the sourcecode I used - it's purely to test the debugger, nothing special:

main.cpp

#include <Arduino.h>

void setup() {
    // put your setup code here, to run once:
    int ledPin = 13;
    pinMode(ledPin, OUTPUT);
}

void switchLED(int pinId) {
    bool newState = !digitalRead(pinId);
    digitalWrite(pinId, newState);
}

void loop() {
    // put your main code here, to run repeatedly:
    int ledPin = 13;
    int oneHzDelay = 1000;
    int twoHzDelay = 500;
    int amountOneHz = 2;
    int amountTwoHz = 16;
    for(int i = 0; i < amountOneHz; i++) {
        switchLED(ledPin);
        delay(oneHzDelay);
    }
    for(int i = 0; i < amountTwoHz; i++) {
        delay(twoHzDelay);
        switchLED(ledPin);
    }
}

platformio.ini

[env:uno]
platform = atmelavr
board = uno
upload_protocol = atmelice_isp

framework = arduino
upload_flags = "-e"
board_upload.require_upload_port = false

build_type = debug
debug_server =
    /Users/wirepirate/sources/avarice/src/avarice
    --edbg
    --debugwire
    :4444
debug_tool = custom
debug_port = localhost:4444
debug_init_cmds =
    define pio_reset_run_target
    end
    define pio_reset_halt_target
    end
    target extended-remote $DEBUG_PORT
debug_load_cmds =
    file "$PROG_PATH"
    load

And these are the debugger settings I'm using:

If you test without AVaRICE without PIO, obviously make sure it does compile with debugging options.

Edit: I might add that if you use PIO, just select the 'Arduino Uno' option from the boards list - you can also directly use the CLion debugger when using PIO (see the attached screenshot)

dl8dtl commented 2 years ago

Well, someone else seems to have "stolen" the screenshot already … you could always attach it here if you think it's needed.

Well, I'm not sure, your pio_reset_halt_target is empty. You could try putting "monitor reset" into it. (For pio_reset_run, it would be "monitor reset", then "continue" in a second line.)

Where / how did you program the DWEN fuse? Somewhere outside, using a manual AVRDUDE command?

SerialDestructor commented 2 years ago

Yes, I was having trouble with those (GDB errored with 'pio_reset_run: no such command'). I will try those lines.

I programmed the fuses using AVRDUDE and I am sure DWEN is enabled correctly.

Edit: I tried including the 'monitor reset' commands and it made no difference, unfortunately.