dcwbrown / dwire-debug

Simple stand-alone debugger for AVR DebugWIRE chips connected directly to an FT232R/CH340/DigiSpark/LittleWire on Linux or Windows.
GNU General Public License v2.0
188 stars 31 forks source link

fuses? #20

Open plasticassius opened 7 years ago

plasticassius commented 7 years ago

Dave, this tool is already very useful. Do you think it's possible there's a way to use debug wire to change fuses? If so, after initially enabling debug wire, it would be possible to completely put away the whole rest of the suite of programmers (SPI, HVP, USB).

dcwbrown commented 7 years ago

I cannot see any way to do so, and all references I've found, official and comments in mailing lists, say it is not possible to do so.

DebugWire works by executing normal instructions. It can access exactly what the normal instruction set can access.

It would be quite easy for dwdebug to read the fuses and lock bits by using the RFLB flag in SPMCSR, but there's no way to write the fuses.

Only high voltage programming mode provides guaranteed write access to the fuses. SPI mode also works, but only if it is enabled in the fuses.

Sadly.

Unless - do you know of any way to write the fuses from running code?

-- Dave.

On 2017-02-18 23:02, plasticassius wrote:

Dave, this tool is already very useful. Do you think it's possible there's a way to use debug wire to change fuses? If so, after initially enabling debug wire, it would be possible to completely put away the whole rest of the suite of programmers (SPI, HVP, USB).

-- You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub [1], or mute the thread [2].

Links:

[1] https://github.com/dcwbrown/dwire-debug/issues/20 [2] https://github.com/notifications/unsubscribe-auth/ADChoLK_-KZq0PjXzvhZAez2jfUlz5FCks5rd3hogaJpZM4MFSeW

plasticassius commented 7 years ago

I just hoped you knew about some undocumented thing DebugWire could do, but I don't have any insight into this. I guess we still need to pull out the other programmers for fuses.

dcwbrown commented 7 years ago

I bought one of those very cheap attiny usb boards you mentioned earlier but I haven't played with it.

I wonder whether one could solder a stack of diodes and capacitors to make a charge pump and have enough pins to implement high voltage programming mode.

On 2017-02-19 14:29, plasticassius wrote:

I just hoped you knew about some undocumented thing DebugWire could do, but I don't have any insight into this. I guess we still need to pull out the other programmers for fuses.

-- You are receiving this because you commented. Reply to this email directly, view it on GitHub [1], or mute the thread [2].

Links:

[1] https://github.com/dcwbrown/dwire-debug/issues/20#issuecomment-280922602 [2] https://github.com/notifications/unsubscribe-auth/ADChoDHVShvs2pwm196tNrNEaxR-mY1Xks5reFHmgaJpZM4MFSeW

plasticassius commented 7 years ago

It's quite possible, but my opinion is that it's not worth the trouble when you can order something like this or this. It's just a matter of waiting the couple of weeks for the shipment.

I can send you code I've adapted from here for an attiny13 if you're interested.

dcwbrown commented 7 years ago

Yes, these little power converters are a modern marvel. That arduinodiy link looks good. I guess that's the simplest solution.

plasticassius commented 7 years ago

Here's my adaptation in case it comes in handy:

#if 0 // MAKEFILE{

MCU = attiny13
F_CPU = 9600000

#endif // MAKEFILE}

#include "arduino.hpp"
#include <util/delay.h>

// AVR High-voltage Serial Fuse Reprogrammer
// Adapted from code and design by Paul Willoughby 03/20/2010
// http://www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer/
// Fuse Calc:
//   http://www.engbedded.com/fusecalc/
//https://arduinodiy.wordpress.com/2015/05/16/high-voltage-programmingunbricking-for-attiny/

// definitions for the controller
#define VCC 2 // Target VCC
#define RST 4 // Output to level shifter
#define SCI 3 // Target Clock Input
#define SDO 1 // Target Data Output
#define SII 5 // Target Instruction Input
#define SDI 0 // Target Data Input

/*
_______
| NPN |
|BC337|
--|||--
  CBE

     R 1K
V+12--zz--o--->RST
          |
        | /C
       B|/
RST>----|\
        | >E
      Q   |
   BC337  |
         GND

   3D
___|___
| BSS |
| 138 |
-|---|-
 1G  2S

     R 2K2
V+12--zz--o--->RST
          |
      G |-- D
RST>--->|<-
  Q     |-| S
  BSS138  |
         GND

     R 1K  /
VCC>--zz--|<--<V+5

# Attiny25, Attiny45, Attiny85 & Attiny13
# 1 PB5 RST
# 2 PB3 SCI
# 3 PB4
# 4 GND
# 5 PB0 SDI
# 6 PB1 SII
# 7 PB2 SDO
# 8 VCC

# Attiny24, Attiny44, Attiny84
# 01 VCC
# 02 PB0 SCI
# 03 PB1
# 04 PB3 RST
# 05 PB2
# 06 PA7
# 07 PA6 SDI
# 08 PA5 SII
# 09 PA4 SDO
# 10 PA3
# 11 PA2 GND
# 12 PA1 GND
# 13 PA0 GND
# 14 GND

# HV____ X4____ X5___ nRF24L01+
#  1 GND 14 GND 4 GND 1 GND
#  2 VCC 01 VCC 8 VCC 2 VCC
#  3 GND 11 PA2       3 CE
#  4 GND 13 PA0       4 CS
#  5 SDO 09 PA4 7 PB2 5 SCK
#  6 SII 08 PA5 6 PB1 6 MOSI
#  7 SDI 07 PA6 5 PB0 7 MISO
#  8 SCI 02 PB0 2 PB3 8 IRQ
#  9 GND 12 PA1
# 10 RST 04 PB3 1 PB5

*/

#define HFUSE  0x747C
#define LFUSE  0x646C
#define EFUSE  0x666E

// Define ATTiny series signatures
#define ATTINY13   0x9007  // L: 0x6A, H: 0xFF             8 pin
#define ATTINY24   0x910B  // L: 0x62, H: 0xDF, E: 0xFF   14 pin
#define ATTINY25   0x9108  // L: 0x62, H: 0xDF, E: 0xFF    8 pin
#define ATTINY44   0x9207  // L: 0x62, H: 0xDF, E: 0xFF   14 pin
#define ATTINY45   0x9206  // L: 0x62, H: 0xDF, E: 0xFF    8 pin
#define ATTINY84   0x930C  // L: 0x62, H: 0xDF, E: 0xFF   14 pin
#define ATTINY85   0x930B  // L: 0x62, H: 0xDF, E: 0xFF    8 pin

byte shiftOut (byte val1, byte val2) {
  int inBits = 0;
  //Wait until SDO goes high
  while (!digitalRead(SDO)) wdt_reset();
  unsigned int dout = (unsigned int) val1 << 2;
  unsigned int iout = (unsigned int) val2 << 2;
  for (int ii = 10; ii >= 0; ii--)  {
    digitalWrite(SDI, !!(dout & (1 << ii)));
    digitalWrite(SII, !!(iout & (1 << ii)));
    inBits <<= 1; inBits |= digitalRead(SDO);
    digitalWrite(SCI, HIGH);
    digitalWrite(SCI, LOW);
  }
  return inBits >> 2;
}

void writeFuse (unsigned int fuse, byte val) {
  shiftOut(0x40, 0x4C);
  shiftOut( val, 0x2C);
  shiftOut(0x00, (byte) (fuse >> 8));
  shiftOut(0x00, (byte) fuse);
}

void readFuses () {
#if 0
  byte val;
        shiftOut(0x04, 0x4C);  // LFuse
        shiftOut(0x00, 0x68);
  val = shiftOut(0x00, 0x6C);
  Serial.print("LFuse: ");
  Serial.print(val, HEX);
        shiftOut(0x04, 0x4C);  // HFuse
        shiftOut(0x00, 0x7A);
  val = shiftOut(0x00, 0x7E);
  Serial.print(", HFuse: ");
  Serial.print(val, HEX);
        shiftOut(0x04, 0x4C);  // EFuse
        shiftOut(0x00, 0x6A);
  val = shiftOut(0x00, 0x6E);
  Serial.print(", EFuse: ");
  Serial.println(val, HEX);
#endif
}

unsigned int readSignature () {
  unsigned int sig = 0;
  byte val;
  for (int ii = 1; ii < 3; ii++) {
          shiftOut(0x08, 0x4C);
          shiftOut(  ii, 0x0C);
          shiftOut(0x00, 0x68);
    val = shiftOut(0x00, 0x6C);
    sig = (sig << 8) + val;
  }
  return sig;
}

uint8_t programmed = 0;

void setup() {
  digitalWrite(RST, HIGH);  // Level shifter is inverting, this shuts off 12V
  digitalWrite(VCC, LOW);   // start with power off
  pinMode(RST, OUTPUT);
  pinMode(VCC, OUTPUT);
  pinMode(SDI, OUTPUT);
  pinMode(SII, OUTPUT);
  pinMode(SCI, OUTPUT);
  pinMode(SDO, OUTPUT);     // Configured as input when in programming mode

  wdt_delay(1000);          // wait for battery

  pinMode(SDO, OUTPUT);     // Set SDO to output
  digitalWrite(SDI, LOW);
  digitalWrite(SII, LOW);
  digitalWrite(SDO, LOW);
  digitalWrite(RST, HIGH);  // 12v Off
  digitalWrite(VCC, HIGH);  // Vcc On
  delayMicroseconds(20);
  digitalWrite(RST, LOW);   // 12v On
  delayMicroseconds(10);
  pinMode(SDO, INPUT);      // Set SDO to input
  delayMicroseconds(300);
  unsigned int sig = readSignature();
  readFuses();
  if (sig == ATTINY13) {
    writeFuse(HFUSE, 0xFF);
    writeFuse(LFUSE, 0x3A);
    programmed = 1;
  } else if (sig == ATTINY24 || sig == ATTINY44 || sig == ATTINY84) {
    writeFuse(EFUSE, 0xFE);
    writeFuse(HFUSE, 0xD5);
    writeFuse(LFUSE, 0xE2);
    programmed = 1;
  } else if (sig == ATTINY25 || sig == ATTINY45 || sig == ATTINY85) {
    writeFuse(EFUSE, 0xFE);
    writeFuse(HFUSE, 0xD5);
    writeFuse(LFUSE, 0xE1);
    programmed = 1;
  }
  readFuses();
  digitalWrite(SCI, LOW);
  digitalWrite(VCC, LOW);    // Vcc Off
  digitalWrite(RST, HIGH);   // 12v Off

  // tristate all except VCC and RST
  pinMode(SDI, INPUT); digitalWrite(SDI, LOW);
  pinMode(SII, INPUT); digitalWrite(SII, LOW);
  pinMode(SCI, INPUT); digitalWrite(SCI, LOW);
  pinMode(SDO, INPUT); digitalWrite(SDO, LOW);
}

void loop() {
  // blink
  wdt_delay(500);
  digitalWrite(VCC, HIGH);
  if (programmed)
    wdt_delay(500);
  else
    wdt_delay(100);
  digitalWrite(VCC, LOW);
}

I believe this covers the majority of the details. The main difficulty with the t13 or t85 is that HV requires all of the pins. As a word of explanation, I use a 10 pin connector that I often have an nRF24L01+ module plugged into, but I can pull out to connect a programmer (HV or SPI).

zw commented 7 years ago

Maybe this is slightly off-topic for the bug, but seeing as discussion seems to have covered HV...

Personally I'm comfortable with ISP; never had to recover via HV so never built it; wouldn't shell out for a MkII JTAGICE clone, nor a Dragon. I also have things it'd be useful to debug in place but which might disapprove of the HV spike. Once debugging was finished, it'd be good to restore the ability to reset physically, and nice to restore serial auto-reset on things like Pro Minis that aren't complicated by a permanent USB<->UART loading the /RESET line. So a low-cost/low-hassle way to flip DWEN back off would be good.

So while it doesn't quite solve @plasticassius's goal of being able to consign all other programmer types to the bottom of a drawer, has anyone tried replicating "The second option"? Seems to say that once you've achieved a debugWire reset, you're left in a state where (normal-voltage) ISP programming should work again (perhaps there are conditions attached, such as not touching the /RESET line?). If it's been made to work, it might be worth making prominent in the README.

As an aside: dwire-debug's top-level README.md says:

special-pupose hardware like the AVR-ICE or AVR-Dragon (although one of these is needed initially to program the DWEN fuse which enables the DebugWIRE port)

Is that something specific to ATTiny? While I've never programmed DWEN on ATMega328, it's my understanding that avrdude is happy to do it in ISP mode, which just needs another ATMega board and the readily available ArduinoISP firmware. Wouldn't want any fellow cheapskates to be put off this project thinking that fancier programmers were required!

zw commented 7 years ago

I'm sure it's easy to get from the avrdude source, but it looks like my wanderings found a forum-posted version already:

As far as I can tell, the way it is done is by sending a CMND_RESET with FLAG set to 0x04 ("reset with MonCom disable" - see AVR067 - JTAGICE mkII Communication Protocol) to the ICE, which disables the debugWire interface and enables the SPI interface, and then the SPI interface can be used to reprogram the DWEN fuse. This is effective as long as the chip power is not cycled.

And here's someone using the feature.

Apologies if all this is known and obvious to you, but it wasn't to me.

dcwbrown commented 7 years ago

As far as I understand it, AVRdude does not talk the debugWIRE protocol directly, rather it talks the dragon or jtagice protocols, and those units translate it to debugWire - i.e. (being pedantic here) that 04H code is not a debugWire command but a jtagice command.

Nevertheless, yes, from RikusW's analysis, it's just a matter of sending a single byte (actually 06H) down the debugWIRE interface. This should reset the device and leave it with debugWire temporarily disabled and thus ISP available.

However ISP means more hardware connections. As well as the reset line, it uses MOSI, MISO and SCK. So to benefit from taking the device out of debugWIRE mode, you would need an ICE connected to your device as well as the FT232 being controlled by dwdebug.

But then if you have an ICE connected, you wouldn't need dwdebug to send the 06H, as the ice would do so. I'm pretty sure avrdude does this automatically.

Summary: it would be trivial to add a 'reset and temporarily disable debugWIRE' command to dwdebug, but I don't see a use case.

Probably I'm missing something?

BTW, thanks for your points about the readme sounding overly discouraging - I'll generalise it and mention the ArduinoISP option.

(I wasn't aware of the ArduinoISP -- nice -- it might make a great alternative to the FT232L for dwdebug. With the capability of the ArduinoISP dwdebug could control the flags and switch in and out of debugWIRE mode all by itself. I wonder how much work the AuduinoISP firmware would need to provide as USB accessed API for the debugWIRE protocol. Interesting project for another day.)

zw commented 7 years ago

you would need an ICE connected to your device as well as the FT232 ... I don't see a use case.

This is more a question of money/tools/access than a technical point. It sounds like you already have an ICE; I don't, and they're discouragingly expensive to many people who tinker just for fun and/or on a budget. In the Arduino world the ATMega328 is (or certainly was) by far the most popular chip, and only supports debugWire (no JTAG), so before dwdebug the only debugging choices were to buy a debugWire-capable thing (Dragon, ICE MkII, maybe some newer ones), or make do with printf-style debugging.

The programming-only (ISP) choices are much better; DIY/clone ISPs are dirt cheap and can flip fuses. So a good proportion of Arduino-ers have an ISP but not hardware ICE; they already have a bit of kit which will flip DWEN on. But if they do, without support for the alternate reset, they're trapped; they can't set fuses any more, including being unable to disable DW and regain the /RESET pin; they'd have to either buy or borrow an ICE or build an HV thing (not nearly so common, and sufficiently faffy that many sites documenting the DWEN fuse broadly compare setting it with bricking the MCU). With support for the alternate reset, they need not buy or build anything to regain fuse (and thus /RESET) control.

The Arduino world basically ruled out enthusiast-compatible debugging support and wanted to make reset convenient, so unfortunately most Arduino boards couple /RESET to DTR or RTS on a USB/UART chip via a capacitor, and thus would probably need to be physically hacked to remove that load. But that hack is documented, and the Pro Mini is free of that concern.

zw commented 7 years ago

I wasn't aware of the ArduinoISP -- nice -- it might make a great alternative to the FT232L for dwdebug.

The way I see it, dwdebug is ultimately destined to be an upgrade to the standard FOSS tooling:

But I'm sure there are reasons you didn't do it that way.

For those wanting a ICE-like appliance, ArduinoISP would not be enough; it implements AVR068 which was only ever for ISP, not for ICE. But with a sufficiently portable dwdebug library, firmware like debugwire-programmer or usbprog that implements AVR067 could use it; before dwdebug there'd have been little point (a MkII without DW is just an overcomplicated STK500) but now there is.

dcwbrown commented 7 years ago

Lots of interesting info, thanks zw. Until recently I have only looked at working directly with the attinys, and I had little knowledge of all things Arduino. Since your email I have looked further.

First with regard to ICE vs ISP and costs. Yes, It really annoyed me that there was no low cost programming and debugging platform and this stopped me going anywhere with Atmel for years. Eventually I gave in and bought a Dragon - the cheapest approach but still about £50 which, I agree, is a heck of a lot.

Then I found the Atmel tools way too clunky. Even avrdude is somewhat clunky. I suppose that's maybe forgiveable due to the wide range of devices it has to support and that it has to go through the tool interfaces, but I still really dislike it.

If I understand correctly you are pointing out that there are plenty of people out there with cheap ISP rather than ICE tools, and that if dwdebug supported debugwire disable they could be used. It would mean having both the ISP and the FT232 connected at the same time, but yes, I hadn't thought of that, and now I see how that would work.

I'll add a command for that.


Further I looked into using an Arduino Nano in place of an FT232.

Hardware wise I don't see why an ArduinoISP board is necessary. Nano's are easily available and can be very cheap - cheaper than FT232 boards.

I doubt there's a lot of code required to make a glorified usb to serial converter with ISP pins as well as debugWIRE, and built in debugWire baud rate detection. Even if it needs to be bit-banged to work at arbitrary baud rates and switch easily between transmit and receive on the same pin, that's not difficult.

Dwdebug would need very little change, and could take over the fuse programming automatically. Maybe something I'll get around to ...


Then again there are your aspirations in your second message :-).

Thanks for the info about AVR067 and 068 interfaces, I had no idea these were well documented.

Yes, I guess Implementing AVR067 could make the nano host look like an ICE and work with Atmel Studio, avrdude and avrice etc. I see that could benefit a lot of people. But I suspect the AVR067 interface would take a long time to develop and debug.

It looks like more effort than building dwdebug itself. Have you developed an AVR067 implementation?

plasticassius commented 7 years ago

I'm not familiar with most of the tools mentioned. I've been using the do it yourself kind like the HV programmer I mentioned before. For completeness, the other ones are Little-Wire and micronucleus. All of these can be used on a bare t85 or a Digispark which is essentially a t85 with a USB connector and has widely available clones. Also, Little-Wire is avrdude compatible via the usbtiny protocol. At this point dwdebug has allowed me to stop using micronucleus entirely since it has all of the functionality, no flash usage, and fewer wires!

I think that a t85 would lend itself well as a FT232 replacement as well as offering a number of advantages such as allowing any AVR compatible power supply voltage and being able to measure the baud rate from a single 0x55 byte. Implementing this has been in the back of my mind for a while now.

dcwbrown commented 7 years ago

I committed a 'qdd' command -- quit and disable debugWIRE -- yesterday. DebugWIRE remains disabled until the power is cycled.

I am now looking at using the digispark as an alternative to an FT232, and adding ISP programming support.

So far I've worked out that

Hopefully these will work together.

plasticassius commented 7 years ago

@dcwbrown , I've worked through a number of v-usb applications, so I'm sure I can help you out with this since I've worked out solutions to a number of difficulties I'm sure you'd run into. I have some working examples I can post given some time, but it's not necessary to use my take on it.

I can already make a few recommendations. The first is start with Little-Wire rather than micronucleus. Both are on github, so getting the code is no problem for either. However, Little-Wire already has ISP programming support as well as v-usb, so it already has much of what you need and works well. Also, my experience shows that Little-Wire is more robust. I get micronucleus failures as a matter of course, but not with Little-Wire. Also, I would guess that speed and compatibility of both are similar since they are both based on v-usb.

Building as an arduino sketch makes getting started quicker, but leads to some issues later on. And, I think the most straightforward approach would be to start with Little-Wire which is not an arduino sketch, and is not going to gain anything from being converted to one.

Making a v-usb hid or non-hid device is a matter of some configuration, not a major difference. However, I don't believe you can do without libusb, what you can avoid with a hid device is needing a custom Windows driver for it. But, I have to admit that I don't work on Windows any more, so I'm not positive about this.

Also, have a look at Arduino-Makefile. I find it helpful to build sketches with this since it avoids the gui.

dcwbrown commented 7 years ago

Thanks a lot for offering, I'll follow your advice.

I've installed little-wire on my digispark, and am relieved to find Windows device manager didn't find fault, it just wanted the driver.

Installing the windows driver is more painful, requiring a special reboot and turning off driver security in the startup options. But again I was relieved to find it works.

It's important to me to support both Unix based and WIndows platforms. So I am going to start by fiddling around for a bit in the hope that I can get it working as a HID device without needing a WIndows driver.

Reopening this issue as it is still generally about enabling FUSE programming.

plasticassius commented 7 years ago

Ok, please let me know if I get inappropriate with the advice.

I think mainly what's needed for a HID device is

PROGMEM const char usbHidReportDescriptor[22] = {
    0x06, 0x00, 0xff,              // USAGE_PAGE (Generic Desktop)
    0x09, 0x01,                    // USAGE (Vendor Usage 1)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0xc0                           // END_COLLECTION
};
#define USB_CFG_DEVICE_CLASS 0
#define USB_CFG_INTERFACE_CLASS 3
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 22

I believe that what's in the usbHidReportDescriptor is not at all important, it's sole function is to make the Windows device manager shut up about loading a driver. You're still free to implement whatever USB functionality you want (which you access via libusb).

After that, to play nice with vids and pids, follow the directions in v-usb's USB-ID-FAQ.txt and USB-IDs-for-free.txt. For generic HID class devices, use 16c0:05df and vendor:device strings like below:

#define USB_CFG_VENDOR_ID       0xc0, 0x16
#define USB_CFG_DEVICE_ID       0xdf, 0x05
#define USB_CFG_VENDOR_NAME ...
#define USB_CFG_DEVICE_NAME ...

It's possible to open the device with libusb based on the strings you give above. I also return a 16 bit serial number from the eeprom as the USB serial number. This allows multiple devices at the same time (like the way multiple FT232's would have differing device names).

Let me know if you'd like me to post a working example of this. Unfortunately you may not like my use of C++.

dcwbrown commented 7 years ago

FYI have been looking at this on and off between other things.

Thanks for the report descriptor which worked nicely (it needed a different device id to avoid being detected as a DigiUSB on windows).

I was able to enumerate it and connect successfully from a win32 app. However having spent plenty of time learning various window wrinkles it slowly dawned on me that I wasn't going to be able to get the Win32 hid api to give access to control transfers. I also considered WinUSB.

I gradually realised I didn't want to break compatibility with littlewire and usbtiny, so I have finally decided to give up on HID. ...

Back to libusb-win32 which is what little-wire uses on Windows.

Does little wire really need to use it's own driver installation I wonder - wouldn't a standard libusb-win32 work? I read that the standard libusb-win32 is already signed, so should be installable on windows without the need to reboot and turn off driver security.

Still pottering about and puttering along ...

dcwbrown commented 7 years ago

I can confirm that using Zadig to install drivers on windows works nicely. This avoids the need to reboot and turn off driver security. Much happier now.

So my plan now is to add a couple of functions to little wire, probably dwCconnect, dwWrite and dwRead.

dcwbrown commented 7 years ago

Arghh! My dgispark came without RSTDISBL programmed, meaning neither its usbtiny functionality was workable nor could I use the reset pin for debugwire.

Have now used my AVRDragon to program RSTDISBL and will try again.

But: if digisparks normally come without RSTDISBL programmed then it's going to be a real pain for our users.

Fortunately it does come with SPIEN programmed, so any SPI programmer will let you program RSTDISBL, just not an as-purchased digispark.

@plasticassius: Did any of your digisparks come with RSTDISBL programmed?

plasticassius commented 7 years ago

The original digisparks come with RSTDISBL programmed so you can use the reset pin the way that littlewire requires. All of the ones I've gotten are clones and those don't have RSTDISBL programmed. I imagine at this time, the clones have squeezed the originals out. I also have a nano, so I used an SPI sketch to program my digisparks' fuses at first.

dcwbrown commented 7 years ago

Thanks, that makes sense. Will need to point it out in the docs.

FYI I have now been able to send a break to connect to debugWIRE and successfully measured all the pulses that come back.

plasticassius commented 7 years ago

It's great you've made progress. Perhaps this would help with clock rate changes I get from resets by being fast enough to measure the baud rate every time one of those 0x55's is received.

plasticassius commented 6 years ago

Dave, I've been doing other things for a while now, so sorry if I'm confusing something here. Is it possible to enable RSTDISBL until the next power cycle similarly to the way the qi command enables ISP mode until next power cycle? I'm now working on a project where having that extra i/o pin temporarily would be quite handy for debugging purposes.

dcwbrown commented 6 years ago

To clarify, are you looking for one of these:

. stop using the reset pin for debugwire and use it instead as a reset signal until the next power cycle . stop using the reset pin for debugwire and use it instead for an i/o pin until the next power cycle

I don't believe you can do either of those. However you can make this specific use of it when debugWIRE is enabled: an IN or OUT to the debugwire port will receive/send a byte (n,8,1) on the reset pin - i.e. the same as debugwire itself does.

I doubt this helps ...

plasticassius commented 6 years ago

What I was thinking is temporarily use the reset pin as an i/o pin for debug purposes. However, what you mentioned is even more interesting! If I understand right, it sounds like it would be possible to implement something like a debug console! Do you know offhand if Littlewire with or without your mods is able to communicate in this way? In any event, it seems that implementing a text shell that talks to the code being debugged should be quite straightforward.

dcwbrown commented 6 years ago

Ironically this would be easy with an FT232/CH430 and is much more difficult with littlewire. The problem is that littlewire uses a software USB implementation and cannot listen to both the USB and debugWIRE ports at the same time.

This made detecting breakpoint-reached very difficult on littlewire ...

A Go command stops when either a breakpoint is reached or the user presses a key. On a UART these are both system events and a system wait works. On the littlewire, dwdebug has to poll the littlewire asking whether breakpoint is reached. Since the littlewire is a software USB implementation interrupts are off while the USB implementation decodes the USB packet sent by dwDebug to ask whether a breakpoint has been hit.

For the current implementation, when you run your program with 'g', my littlewire implementation turns on the pin change interrupt for the debugWIRE pin, so that a state transition does not get lost.

That's OK for detecting a breakpoint, but it cannot read bytes of data.

My debugWIRE implementation in littlewire is a fully software implemented uart, with rx and tx sharing pin 1 on the littlewire's t85.

This pin arrangement has the advantage of being connection compatible with ISP mode - that is, you can switch between debugWIRE and ISP mode, between dwdebug and avrdude, without having to change the connections between the littlewire and the target.

If we gave up this convenience, it may well be possible to use the universal serial interface on the littlewire's t85 (wiring both data-out and data-in together to the target debugWIRE pin). It might be possible then for the t85 serial interface to buffer a character coming from the target while interrupts are off handling a USB packet. Or maybe not. There would be a whole lot of work here ...

plasticassius commented 6 years ago

It's unfortunate that this is difficult on the t85; however, I still like the idea of a debug monitor. Perhaps as a proof of concept, I'll try to connect two devices together. I'll try a t85 for debugging run by dwdebug, and an FT232 running a terminal program. Perhaps that's enough for starters, not an elegant solution, but potentially very useful. If this works well, it may be worthwhile giving some thought about how to implement it properly, and on what device.

plasticassius commented 6 years ago

Do you know how the full / empty detection works for DWDR on the target? I can't seem to find any status bits. Is it that writing to DWDR stops the target until the byte is sent? How does reading DWDR sync?

dcwbrown commented 6 years ago

I think I read somewhere for one of the chips that debugWire is implemented using the built in serial port hardware, so you might find it in one of the universal serial interface flag registers?

I suspect the IN instruction pauses the processor until a byte is ready. That may sound odd, but the virtual instructions executed by the hardware implementation of the dw protocol don't appear to include anything to wait for a byte to be ready before an IN DWDR.

If the worst comes to the worst I guess you could define a protocol in which every byte is guaranteed to differ from the previous and poll for changes.

plasticassius commented 6 years ago

So, on a running t85, writing to DWDR has no observable effect. It doesn't do anything electrically on the debugwire pin, and it doesn't change the value that comes from a read of DWDR. After a power on or WDT reset, DWDR reads as 0xFF; after a dwdebug qr command, DWDR reads as 0x30.

It seems that DWDR is completely disabled while running. I imagine that's the meaning of the datasheet's cryptic "This register is only accessible by the debugWIRE and can therefore not be used as a general purpose register in the normal operations." However, this seems to contradict "The DWDR Register provides a communication channel from the running program in the MCU to the debugger." As far as I can tell, the DWDR register only works when the MCU is not running.

dcwbrown commented 6 years ago

That's disappointing.

Thinking out loud:

Looking at the notes at the end of dwire/DwPort.c under e.g. 'regs repeating instructions' it seems that the dwire handling does use in and out instructions internally so very sad that this is not available to the user.

For example DwGetRegs sets PC and BP to the first and limit register numbers then issues:

0x66 - Set up for read/write using repeating simulated instructions 0xC2 - Set read/write mode (followed by 0/4 SRAM, 1/5 regs, 2 flash) 1 - read/write mode is regs 0x20 - go start reading/writing SRAM/Flash based on low byte of IR

DwGo differs like this: it sets PC (and maybe BP) to code addresses then issues: 0x60 - Set GO context 0x30 - GO - normal execution

It's along shot, but I wonder if there are any flags in here that could be set in the GO case to enable use of the dwire port.

In lines 382 to 416 of dwport.c I've tried to chart the meaning of the individual bits in the 4x/6x and the 2x/3x dwire commands, based on RikusW's work and a few things that made sense to me.

Some things show up on both bytes, like selecting the virtual repeating instructions and selecting single step, so the flags are probably doing different aspects of setting up for these modes.

It may just be that one of them controls IN and OUT to DWDR.

Hmm, the more I look at it the more it seems it would probably be an awful lot of trial and error with little chance of success.

Oh well.

Thanks for trying - now we know it doesn't 'just work'.

-- Dave.

plasticassius commented 6 years ago

It is disappointing that DWDR doesn't work in the running state. I still think I may have missed something, but I can't think of what it might be. Also, it's hard to see this is worthwhile to pursue much further, but I have a few more thoughts about possible alternate ways to make this work.

The debugger could read and write a buffer to communicate with code running on the target. So, this could be used to implement a "serial" read and write protocol between the target and the debugger. This would actually be a kind of rpc protocol where the target could send packets that the debugger would write to the console, and then could return any keypresses. The debugger already modifies the target's state, the twist here is that there would be dedicated code on the target to help with the process.

Unfortunately, the only way I can think of for the target to send a signal to the debugger is to execute a software breakpoint. (Is there another way?) A way for the debugger to differentiate this from an actual software breakpoint could be placing an invalid op code right after the breakpoint instruction, for example. The main drawback I can see is that the target wouldn't be able to tell if the debugger was connected, would stop executing at the breakpoint, and would stay halted if the debugger wasn't there to wake it up again. So, unfortunately we're limited by the inability of the target to signal anything on the debugwire pin other than by executing a breakpoint.

In the absence of the target being able to signal the debugger, the debugger could just poll the target. This has unfortunate overhead; however, so does the idea with the software breakpoint since they both involve stopping the target.

Anyway, just a few more thoughts for what it's worth.

plasticassius commented 6 years ago

As an educated guess, I programmed both DWEN and RSTDISBL fuses. Debug wire works, and ... after I ran dwdebug qi, the reset pin works as an i/o pin! This is what I was after in the first place! I need to think about how to best use this, but it's a pleasant surprise!

dcwbrown commented 6 years ago

That's nice and simple.

Re differentiating a software breakpoint, this could be by address - from the debugger point of view it already gets the instruction address following breakpoint, whereas reading the next instruction is a bit more effort.

Staying in dwire mode would allow code on the dwdebug side to directly communicate whole buffers of data to/from the target, which could reduce the amount of code required on the target. It would also avoid implementing a software half duplex uart on the reset pin.

Depends on what you want to do in the target. Maybe a minimal Forth interpreter?

plasticassius commented 6 years ago

I've been toying with this a bit, while I can imagine all kinds of schemes where the target can cooperate with the debugger. However, as long as the target is in dw mode, the debugger has to stop the target in order to do anything. And, this will interfere with timing, interrupts, and so on. On the other hand, it's quite easy to implement at least a send only uart on the reset pin:

#include "rpcdw.hpp"

#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny13__)
#define DW_PIN (B,5)
#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
#define DW_PIN (B,3)
#else // defined(__AVR_*)
#error DW ports
#endif // defined(__AVR_*)

RpcDw rpcDw;

void RpcDw::send(Rpc& rpc, uint8_t index) {
// send an application specific structure...
}

void RpcDw::begin() {
  DIO_OP(DDR,DW_PIN,&=~); // input
  DIO_OP(PORT,DW_PIN,|=); // pullup
}

void RpcDw::write(uint8_t c) {
  uint16_t bit = uint16_t(c) << 1;

  uint8_t sreg = SREG;
  cli();

  for (int8_t i = 9; i > 0; --i) {
    if (bit & 1) {
      DIO_OP(DDR,DW_PIN,&=~); // input
      DIO_OP(PORT,DW_PIN,|=); // pullup
    }
    else {
      DIO_OP(PORT,DW_PIN,&=~);// low
      DIO_OP(DDR,DW_PIN,|=);  // output
    }
    bit >>= 1;
    _delay_us(10);
  }
  DIO_OP(DDR,DW_PIN,&=~); // input
  DIO_OP(PORT,DW_PIN,|=); // pullup

  SREG = sreg;
}

This does indeed send data out of the reset pin at 100000 baud or close enough. Also, this code turns interrupts off for a short enough time so that I don't think it will interfere with other services the target is doing. Up till now, I've used bit banged usb for this purpose which is much more intrusive in terms of number of pins, memory usage, and latency. I think this would be enough code on the target to handle the majority of use cases. Implementing the receive code is of course more complicated, but should still be vastly simpler than usb.

This comes in handy for logging messages from the target while it's running, possibly for an extended period. This is in contrast to the typical use of dwdebug to single step, examine registers, etc. I can imagine that dwdebug command name qt (quit to text mode) could be implemented which disables dw mode and then receives text which it then echoes. A possible further enhancement could be to take typed text and send it to the target (I've found that single characters are typically most useful).

For my purposes, having a separate console application would help even more since it could interpret application specific data sent by the target. I already have structures defined in headers that I exchange by usb with a host or nrf radio with other devices. And, it would be handy to just send them to a host by dw.

Offloading the formatting, etc to a console app makes it possible to show integers and other data that the target doesn't have capacity to deal with. A basic structure (index 1) could have a union with text and integers that carry a radix, sign bit, field width, etc. Further structures could carry application specific enums, etc. A return path would allow for bidirectional communication, but perhaps that's getting carried away for now.

Unless I'm missing something, it should be possible to make such an app that communicates with your current version of littlewire. dwdebug already can exit dw mode, and I've made an app that wiggles a bit through littlewire to power cycle the target in order to get it back to dw mode:

const uint8_t pin = 0;
Xfer(/*set output*/14, pin);
Xfer(/*set high*/18, pin);
usleep(100 * 1000);
Xfer(/*set low*/19, pin);
//Xfer(/*set input*/13, pin);

This could be a dwdebug command as well. Trickier is to somehow test to see if the target is in dw mode or not, this would require the target code to detect a break and respond in some way.

plasticassius commented 6 years ago

Here's something I happened to stumble across:

Single-wire Software UART http://www.atmel.com/Images/AVR274.pdf http://www.atmel.com/dyn/resources/prod_documents/AVR274.zip

nerdralph commented 6 years ago

@plasticassius can you elaborate on the DWEN + RSTDISBL combination? I'm surprised that you say debugWire still works, since it is activated by a reset (the break signal pulls RST low).

p.s. if you are looking for a small and fast soft UART, I've got a couple versions in asm. https://github.com/nerdralph/nerdralph/tree/master/avr/libs/bbuart

plasticassius commented 6 years ago

Ok @nerdralph, DWEN puts the part into DW mode after a power cycle. There is a difference when DW mode is exited (like with the dwdebug qi command). Afterwards, without RSTDISBL, the part is in ISP mode which allows programming with avrdude. With RSTDISBL after exiting DW mode, the part has the RESET pin available as a GPIO pin the same way it does with RSTDISBL and without DWEN.

Thanks for the UART code, I've already implemented a soft UART that works over the RESET pin after exiting DW mode in #30. It mimics DW in that after a break it generates the baud rate sync response.

nerdralph commented 6 years ago

Thanks @plasticassius. So you are still able to re-flash the chip with new code through debugWire, but if ISP mode no longer works then a HV programmer would be required to make further changes to the fuse settings. I suspect there may still be a way to use the ISP mode, if you are very particular about the timing. My suspicion is based on the following datasheet note:

If the RSTDISBL fuse is programmed, this start-up time will be increased to 14CK + 4 ms to ensure programming mode can be entered.

plasticassius commented 6 years ago

That would be an interesting way to get to ISP mode, but I wounder if the datasheet note doesn't mean HV programming when it says "programming mode."