Interrupt Free V-USB

resync

[Tim’s] new version of Micronucleus, Micronucleus 2.0, improves upon V-USB by removing the need for interrupts. The original Micronucleus was a very small implementation of V-USB that took up only 2KB. Removing the need for interrupts is a big leap forward for V-USB.

For those of you that do not know, “V-USB is a software-only implementation of a low-speed USB device for Atmel’s AVR® microcontrollers, making it possible to build USB hardware with almost any AVR® microcontroller, not requiring any additional chip.” One tricky aspect of using V-USB is that the bootloader requires interrupts, which can lead to messy problems within the user program. By removing the need for interrupts, Micronucleus 2.0 reduces the complexity of the bootloader by removing the need to patch the interrupt vector for the user program.

With the added benefit of  speeding up the V-USB data transmission, Micronucleus 2.0 is very exciting for those minimal embedded platforms based on V-USB. Go ahead and try out Micronucleus 2.0! Leave a comment and let us know what you think.

24 thoughts on “Interrupt Free V-USB

  1. Good to know that there are people still working on improving VUSB.

    Might have to look at the code as I ran into some problems in my programmer. Similar issues with VUSB interrupts into CPU cycles in the middle of a programming cycle. Not much of a problem with FLASH chip with internal state machine, but can potentially mess up chips like EPROM that does require deterministic timing.

  2. Nice, thanks for posting that here!

    In light of that, I’d like to add that Micronucleus V2 is not yet ready for release. If something breaks I can not support it right now. But you are welcome to help in further development. The current state is summarized in this pull request:

    https://github.com/micronucleus/micronucleus/pull/43

    Next steps before the release will be to add a way to build multiple configurations to allow support for other ATtinies apart from ATtiny85. It should already work on the entire family (minus ATtiny13), but configuration is a bit messy.

    If you just want a nice, small, bootloader for the ATtiny85, I would suggest you try the current release, Micronucleus V1.11.

    https://github.com/micronucleus/micronucleus

    The advantage of Micronucleus V2 is that it is extremely small (<1600 bytes vs 1816 for 1.11), very fast, and it does not mess with the interrupts of the user program, unlike MN V1.x and the trinket bootloader. The trinket bootloader is almost twice as big as Micronucleus V2 and several times slower.

      1. >I notice you still handle the rewrite of the reset vector in the bootloader. You could cut out probably 100 more bytes if you do it in the uploader code.

        Actually this is intentional. The idea is to ensure that a device with the bootloader can not be rendered into an inoperable state by anything written to it via USB (“bricking” it). If the patching was done in the commandline tool, a USB error could lead to an incorrect reset vector.

        Well, I might add an option to disable client side reset vector patching for people who like living on the edge and mind about the last 50 bytes :)

        >I could even help writing an avrdude programmer to support micronucleus.

        That would be great! I have not looked into this too much, but in principle there shouldn’t be a reason why it is not possible to micronucleus to AVRdude as a device? Verification would have to be disabled, but i guess that is doable.

        1. >If the patching was done in the commandline tool, a USB error could lead to an incorrect reset vector.

          I’d uncomment the crc16 check in usbPoll(), and discard the packet if there is an error to protect against corruption.

          Re avrdude, verification doesn’t have to be “disabled”. The programmer just implements paged_write and not paged_read. When it tries to verify avrdude will spit out a couple “avrdude.exe: programmer operation not supported” messages followed by “avrdude.exe done. Thank you.”

          I’ve looked at the micronucleus uploader, and it uses libusb as does avrdude. There is a steep learning curve figuring out the architecture of avrdude, but since I’ve already gone through that, I could code a micronucleus programmer in no more than a weekend. I’d need to write a micronucleus.[ch] that implement a few standard avrdude functions, add two lines to pgm_type.c, add a micronucleus entry to avrdude.conf.in, and add micronucleus.[ch] to Makefile.in.

          1. >I’d uncomment the crc16 check in usbPoll(), and discard the packet if there is an error to protect against corruption.

            v1.11 does it that way. I’ll add it to V2 again, but I need to substitute a faster crc, since the slow one takes 45µs. However, CRC checking only helps with transmission errors, not with a faulty host tool or similar.

            > I could code a micronucleus programmer in no more than a weekend.

            That would be great. Actually the host tool is modularized, so it is maybe possible to keep the core lib portable between avrdude and the commandline tool. It is also possible to add a device ID to the client configuration response, without additional code.

          2. > However, CRC checking only helps with transmission errors, not with a faulty host tool or similar.
            But it’s impossible to protect against a rogue host tool without hardware protection for the boot code. I could easily upload code which, when run, stomps all over micronucleus. A bug in the micronucleus updater could be just as damaging…

  3. Cool, I started doing the same thing back in August, along with a rewrite of the v-usb assembly code to get it to work at 8Mhz, but ended up spending most of my spare time coding picoboot (a 64-byte serial bootloader).
    Nice to see someone beat me to cleaning up a v-usb bootloader. One of these days I still plan to get the 8Mhz code done. To do it with in an average of 5 1/3 cycles per bit, I just read and store the bits as they come in, and leave the NRZI decode and bit unstuffing to post-processing. 5 cycles per bit is enough for in, st, & 2 ror instructions. For 1 out of every 4 bits I have an optional 1-cycle delay (to allow for 1.5% timing variation with the internal RC oscillator), and 1 out of every 4 bits is fixed at 6 cycles. The core receive loop is only 21 instructions.

      1. Nice clean asm code! Too bad I didn’t come across it before I started picoboot. How many bytes does it compile to? It probably wouldn’t take much work to make it arduino compatible, and it would be a lot smaller than optiboot.

        You can reduce the code size a few more words. Most AVR’s with a USART have 81N as the reset value of UCSRC, so can ifdef out the ldi/out UCSRC for those parts.
        In cmd_write_flash, you can use addiw ZL, 2 instead of ldi & add.

        1. Precisely 256 bytes. Also the minimal bootloader size on mega8. I didn’t really care to make it smaller because of that restriction.

          It’s been 3 years ago and I moved on to STM32, never been an Arduino fan too… you’re welcome to fork the repo and I’ll happily merge/discuss any changes you have.

          1. I’m not really an arduino fan either – been using vi for over 20 yrs and still happy with it. When I do use the IDE, I don’t use the slow and bloated digitalWrite/digitalRead. The Serial.println was handy for debugging before I wrote a soft uart in <20 asm instructions. The reason I mention arduino bootloader compatibility is because avrdude already supports it, so there would be no need for new programmer software.

            Right now I'm primarily working on making wireless sensor nodes using ATtiny88's and nrf24l01 modules, but when I'm finished that I'll try modifying your code to see if I can get it to do stk500/arduino protocol and still stay under the 256 byte limit.

            The STM32 (and other ARM M0 MCUs) look interesting, but so far everything I've tried to do, a cheap AVR has been enough. The 88-AU are great – only 56c/q25, and if you can manage the 0.5mm pitch of the 88-MU, they're just 50 ea/q25.

          2. STM32 is M3 though. I find M0 largely pointless, except perhaps for a few edge cases. Overall, ARM MCUs seem to be generally cheaper than largely equivalent AVRs, while providing a much nicer 32-bit ISA, awesome NVIC, bitband areas and other goodies.

            I’ve had plans for wireless sensor nodes as well, although right now I’m stuck with other projects. When I’m done, I want to have a full 6lowpan stack in Rust. I think nrf24l01+ doesn’t qualify for 6lowpan due to message length limitation, but I may be wrong.

          3. >I find M0 largely pointless, except perhaps for a few edge cases.

            It’s not that pointless if your general workflow is geared towards ARM and your project becomes too un-sophisticated for an M3. It makes a lot of sense to retain compatibility to peripherical libraries etc., and just switch to M0 instead of going to a different architecture all together.

      1. Well, all I can say is, “great minds think alike”!

        I committed my first back-of-the-envelope 8Mhz code in August:
        http://code.google.com/p/picoboot/source/browse/trunk/usbdrv/usbdrvasm8.inc
        It’s not really part of picoboot, but I like to keep even my prototype code in a public source repository.
        That version has a 6-bit receive loop, does eor in realtime, but no bit-unstuffing. It also requires 8Mhz +-0.5% fixed timing (i.e. crystal or resonator). The latest (uncommitted) prototype has a 4-bit receive loop with no eor, and can handle +-2% timing – i.e. RC oscillator. It also requires one of the USB data lines to be on pin 0 or pin7 – that allows a bit to be stored in 3 cycles (in, + 2 shift) vs 4 (in, bst, bld, & shift). On something like the ATtiny85 that means one has to be on PB0, but on chips like the ATtiny88-AU (now my favorite AVR) you have 7 different pins to choose from (PA0, PB0,PB7, PC0, PC7, PD0 & PD7).

        I also have some ideas to improve and simplify the v-usb hardware. I’m ditching the zeners, and putting 7.5K series resistors on D+ and D-. With the 15K host-side termination resistors, it will make a 2/3 voltage divider to bring the 5V AVR output down to 3.33V on the USB bus. I’m also ditching the 1.5K pullup, and make it firmware controlled by just setting the D- pin high. I’ve some USB connectors, 0805 chip resistors, and some QFP ATtiny88’s. Once my PCB’s to arrive from China I’ll be able to test it out

        Besides allowing full use of the D+ and D- pins when not connected to the USB bus, it makes a v-usb device usable for battery powered devices. For example something running off a couple lipos and a 5V regulator will have a constant 933uA drain from the 1.5K pullup.

        1. >http://code.google.com/p/picoboot/source/browse/trunk/usbdrv/usbdrvasm8.inc

          Very interesting. Does it work? You should post it in the V-USB forum. I see that instead of detecting EOP you are detecting an idle bus for six bit times. Considering the EOP SE0 takes two bit times and up to 7.5 bits gap are allowed, that leaves only 3.5 bit times for the ACK. Looks critical to me, but may be doable.

          > With the 15K host-side termination resistors, it will make a 2/3 voltage divider to bring the 5V AVR output down to 3.33V on the USB bus.

          It’s worth a try, but I don’t think that will work very well. The reason for that is that in the transient limit, the AVR will not “see” the 15kohm pull down at the host side, but the characteristic impedance of the line, which is 90 Ohm. You are going to end up with very bad slopes and weird ringing due to reflections.

          http://en.wikipedia.org/wiki/Characteristic_impedance

          > I’m also ditching the 1.5K pullup, and make it firmware controlled by just setting the D- pin high. I’ve some USB connectors, 0805 chip resistors, and some QFP ATtiny88′s. Once my PCB’s to arrive from China I’ll be able to test it out

          Could work, if you are quick enough :)

          1. Very interesting. Does it work?
            I haven’t finished enough of the code to even try testing it. So far it’s just theory.

            >Considering the EOP SE0 takes two bit times and up to 7.5 bits gap are allowed, that leaves only 3.5 bit times for the ACK.
            18 cycles might be tight, but by my reading of the spec I’ll have 24 cycles (4.5 bit times) since EOP is SE0 for 2 bit times followed by J state for 1 bit time, and the 7.5 bit-time response requirement starts after the J bit.

            If that’s still not enough cycles, there’s 4 available cycles per bit in transmitting the sync after an out instruction, so that’s ~30 more cycles I could work with before making the decision to transmit an ACK or NAK. I could even push it out 3 more bit-times, since the first 3 bits in a NAK both ACK are 0, 1, and 0.

            Regarding signal quality, reflections should not be a problem at 1.5Mhz. The bits are 667ns long, and if I did my calculations of the speed of electrons in a wire correctly, any reflections will take less than 1ns to traverse a 1m usb cable. The recommended v-usb hardware has no slew-rate capacitors, so it likely generates transitions that are technically too fast. I’m guessing that 7.5K series resistors reduce the slew rate enough to bring the transitions within spec.

            p.s. thanks for the suggestion about the v-usb discussion forum. I hadn’t come across it before, so I may sign up.

          2. The signal speed in a wire is about 0.8c, so it is ~25 cm per 1 ns. If you have a 1 m cable you’ll see the first reflection after about 10 ns.

            But the problem is rather, that you will initially have a voltage divider between 7500 Ohm and 90 Ohm. So the edge will not be very crisp.

            Anyways, the best way is to try it and attach a scope.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.