; ======================================================================; USB interrupt handler;; This is the handler for the interrupt caused by the initial rising edge; on the D+ USB signal. The NRZI encoding and bit stuffing are removed,; and the packet is saved in one of the two input buffers. In some cases,; a reply packet is sent right away.;; When a DATA0/DATA1 packet directly follows a SETUP or OUT packet, while; this interrupt handler is not yet finished, there would be no time to; return and take another interrupt. In that case, the second packet is; decoded directly in the same invocation. A packet immediately following; an ignored packet is also decoded directly.;; This code is *extremely* time critical. For instance, there is not a; single spare cycle in the receiver loop, and only two in the transmitter; loop. In addition, the various code paths are laid out in such a way that; the various USB timeouts are not violated, in particular the maximum time; between the reception of a packet and the reply, which is 7.5 bit times; (TRSPIPD2) for a low-speed USB captive cable. The worst-case delay here; is 51 cycles, which is well below the 60 cycles limit, and even below the; 6.5 bit times limit for a detachable cable (TRSPIPD1).;; The interrupt handler must be reached within 34 cycles after D+ goes high; for the first time. The interrupt response time is 4 cycles, and the RJMP; in the vector table takes 2 cycles. Therefore, the interrupts should not; be disabled for longer than: 34 - 4 - 2 = 28 cycles. When the I-bit is; reenabled, a single instruction is always executed before a pending; interrupt is served, so this instruction should be included in the; calculation. For RETI, the next instruction can be anything, so we; should assume the worst-case of 4 cycles.;; The end-of-packet (EOP) is sampled in the second bit, because the USB; standard allows the EOP to be delayed by up to one bit. As the EOP; duration is two bits, this is not a problem.;; Stack usage including the return address: 11 bytes.;; Copyright 2006-2010 Dick Streefland;; This is free software, licensed under the terms of the GNU General; Public License as published by the Free Software Foundation.; ======================================================================#include "def.h"; ----------------------------------------------------------------------; local data; ----------------------------------------------------------------------.datatx_ack: .byte USB_PID_ACK ; ACK packettx_nak: .byte USB_PID_NAK ; NAK packet.lcomm token_pid, 1 ; PID of most recent token packet.global __do_copy_data; ----------------------------------------------------------------------; register definitions; ----------------------------------------------------------------------// receiver:#define count r16#define usbmask r17#define odd r18#define byte r19#define fixup r20#define even r22// transmitter:#define output odd#define done fixup#define next even// control:#define pid odd#define addr usbmask#define tmp fixup#define nop2 rjmp .+0 // not .+2 for some strange reason; ----------------------------------------------------------------------; interrupt handler; ----------------------------------------------------------------------.text.global USB_INT_VECTOR.type USB_INT_VECTOR, @function; ----------------------------------------------------------------------; This handler must be reached no later than 34 cycles after D+ goes high; for the first time.; ----------------------------------------------------------------------USB_INT_VECTOR:; save registerspush countpush usbmaskpush oddpush YHpush YLin count, SREGpush count; ----------------------------------------------------------------------; Synchronize to the pattern 10101011 on D+. This code must be reached; no later than 47 cycles after D+ goes high for the first time.; ----------------------------------------------------------------------sync:; wait until D+ == 0sbic USB_IN, USBTINY_DPLUSrjmp sync ; jump if D+ == 1resync:; sync on 0-->1 transition on D+ with a 2 cycle resolutionsbic USB_IN, USBTINY_DPLUSrjmp sync6 ; jump if D+ == 1sbic USB_IN, USBTINY_DPLUSrjmp sync6 ; jump if D+ == 1sbic USB_IN, USBTINY_DPLUSrjmp sync6 ; jump if D+ == 1sbic USB_IN, USBTINY_DPLUSrjmp sync6 ; jump if D+ == 1sbic USB_IN, USBTINY_DPLUSrjmp sync6 ; jump if D+ == 1ldi count, 1<<USB_INT_PENDING_BITout USB_INT_PENDING, countrjmp return ; ==> false start, bail outsync6:; we are now between -1 and +1 cycle from the center of the bit; following the 0-->1 transitionlds YL, usb_rx_offclr YHsubi YL, lo8(-(usb_rx_buf)) ; Y = & usb_rx_buf[usb_rx_off]sbci YH, hi8(-(usb_rx_buf))ldi count, USB_BUFSIZE ; limit on number of bytes to receiveldi usbmask, USB_MASK ; why is there no eori instruction?ldi odd, USB_MASK_DPLUSsync7:; the last sync bit should also be 1sbis USB_IN, USBTINY_DPLUS ; bit 7 of sync byte?rjmp resync ; no, wait for next transitionpush bytepush fixuppush even; ----------------------------------------------------------------------; receiver loop; ----------------------------------------------------------------------in even, USB_IN ; sample bit 0ldi byte, 0x80 ; load sync byte for correct unstuffingrjmp rxentry ; 2 cyclesrxloop:in even, USB_IN ; sample bit 0or fixup, bytest Y+, fixup ; 2 cyclesrxentry:clr fixupandi even, USB_MASKeor odd, evensubi odd, 1in odd, USB_IN ; sample bit 1andi odd, USB_MASKbreq eop ; ==> EOP detectedror bytecpi byte, 0xfcbrcc skip0skipped0:eor even, oddsubi even, 1in even, USB_IN ; sample bit 2andi even, USB_MASKror bytecpi byte, 0xfcbrcc skip1skipped1:eor odd, evensubi odd, 1ror bytein odd, USB_IN ; sample bit 3andi odd, USB_MASKcpi byte, 0xfcbrcc skip2eor even, oddsubi even, 1ror byteskipped2:cpi byte, 0xfcin even, USB_IN ; sample bit 4andi even, USB_MASKbrcc skip3eor odd, evensubi odd, 1ror byteskipped4:cpi byte, 0xfcskipped3:brcc skip4in odd, USB_IN ; sample bit 5andi odd, USB_MASKeor even, oddsubi even, 1ror byteskipped5:cpi byte, 0xfcbrcc skip5dec countin even, USB_IN ; sample bit 6brmi overflow ; ==> overflowandi even, USB_MASKeor odd, evensubi odd, 1ror byteskipped6:cpi byte, 0xfcbrcc skip6in odd, USB_IN ; sample bit 7andi odd, USB_MASKeor even, oddsubi even, 1ror bytecpi byte, 0xfcbrcs rxloop ; 2 cyclesrjmp skip7eop:rjmp eop2overflow:rjmp ignore; ----------------------------------------------------------------------; out-of-line code to skip stuffing bits; ----------------------------------------------------------------------skip0: ; 1+6 cycleseor even, usbmaskin odd, USB_IN ; resample bit 1andi odd, USB_MASKcbr byte, (1<<7)sbr fixup, (1<<0)rjmp skipped0skip1: ; 2+5 cyclescbr byte, (1<<7)sbr fixup, (1<<1)in even, USB_IN ; resample bit 2andi even, USB_MASKeor odd, usbmaskrjmp skipped1skip2: ; 3+7 cyclescbr byte, (1<<7)sbr fixup, (1<<2)eor even, usbmaskin odd, USB_IN ; resample bit 3andi odd, USB_MASKeor even, oddsubi even, 1ror byterjmp skipped2skip3: ; 4+7 cyclescbr byte, (1<<7)sbr fixup, (1<<3)eor odd, usbmaskori byte, 1in even, USB_IN ; resample bit 4andi even, USB_MASKeor odd, evensubi odd, 1ror byterjmp skipped3skip4: ; 5 cyclescbr byte, (1<<7)sbr fixup, (1<<4)eor even, usbmaskrjmp skipped4skip5: ; 5 cyclescbr byte, (1<<7)sbr fixup, (1<<5)eor odd, usbmaskrjmp skipped5skip6: ; 5 cyclescbr byte, (1<<7)sbr fixup, (1<<6)eor even, usbmaskrjmp skipped6skip7: ; 7 cyclescbr byte, (1<<7)sbr fixup, (1<<7)eor odd, usbmasknop2rjmp rxloop; ----------------------------------------------------------------------; end-of-packet detected (worst-case: 3 cycles after end of SE0); ----------------------------------------------------------------------eop2:; clear pending interrupt (SE0+3)ldi byte, 1<<USB_INT_PENDING_BITout USB_INT_PENDING, byte ; clear pending bit at end of packet; calculate packet lengthsubi count, USB_BUFSIZEneg count ; count = packet length; get PIDsub YL, countsbci YH, 0ld pid, Y; separate out the non-Token packets (SE0+11)sbrc pid, 1rjmp is_data_handshake ; jump for Data or Handshake packet; check ADDR of Token packet (SE0+13)ldd addr, Y+1andi addr, 0x7flds tmp, usb_addresscp addr, tmp ; is this packet for me?brne ignore ; no, ignore; dispatch Token packets (SE0+20)cpi pid, USB_PID_INbrne is_setup_out ; handle SETUP and OUT packets; ----------------------------------------------------------------------; Handle IN (SE0+22); ----------------------------------------------------------------------lds count, usb_tx_lentst count ; data ready?breq nak ; no, reply with NAKlds tmp, usb_rx_lentst tmp ; unprocessed input packet?brne nak ; yes, don't send old data for new packetsts usb_tx_len, tmp ; buffer is available again (after reti)lds tmp, usb_new_addresssts usb_address, tmp ; assign new address at end of transferldi YL, lo8(usb_tx_buf)ldi YH, hi8(usb_tx_buf)rjmp send_packet ; SE0+40, SE0 --> SOP <= 51; ----------------------------------------------------------------------; exit point for ignored packets (SE0+21); ----------------------------------------------------------------------ignore:clr pidignore0:; ----------------------------------------------------------------------; Handle SETUP/OUT (SE0+23); ----------------------------------------------------------------------is_setup_out:sts token_pid, pid ; save PID of token packetpop evenpop fixuppop bytein count, USB_INT_PENDING ; next packet already started?sbrc count, USB_INT_PENDING_BITrjmp sync ; yes, get it right away (SE0+35); ----------------------------------------------------------------------; restore registers and return from interrupt (SE0+34); ----------------------------------------------------------------------return:pop countout SREG, countpop YLpop YHpop oddpop usbmaskpop countreti; ----------------------------------------------------------------------; send NAK packet (SE0+31); ----------------------------------------------------------------------nak:ldi YL, lo8(tx_nak)ldi YH, hi8(tx_nak)rjmp send_token; ----------------------------------------------------------------------; Handle Data and Handshake packets (SE0+14); ----------------------------------------------------------------------is_data_handshake:andi pid, 0x01breq ignore0 ; ignore ACK/NAK/STALL; ----------------------------------------------------------------------; Handle DATA0/DATA1 (SE0+16); ----------------------------------------------------------------------lds pid, token_pidtst pid ; data following our SETUP/OUTbreq ignore0 ; no, ignorelds tmp, usb_rx_lentst tmp ; buffer free?brne nak ; no, reply with NAKsts usb_rx_len, count ; pass buffer lengthsts usb_rx_token, pid ; pass PID of token (SETUP or OUT)lds count, usb_rx_off ; switch to other input bufferldi tmp, USB_BUFSIZEsub tmp, countsts usb_rx_off, tmp; ----------------------------------------------------------------------; send ACK packet (SE0+34); ----------------------------------------------------------------------ldi YL, lo8(tx_ack)ldi YH, hi8(tx_ack)send_token:ldi count, 1 ; SE0+37, SE0 --> SOP <= 48; ----------------------------------------------------------------------; acquire the bus and send a packet (11 cycles to SOP); ----------------------------------------------------------------------send_packet:in output, USB_OUTcbr output, USB_MASKori output, USB_MASK_DMINUSin usbmask, USB_DDRori usbmask, USB_MASKout USB_OUT, output ; idle stateout USB_DDR, usbmask ; acquire busldi usbmask, USB_MASKldi byte, 0x80 ; start with sync byte; ----------------------------------------------------------------------; transmitter loop; ----------------------------------------------------------------------txloop:sbrs byte, 0eor output, usbmaskout USB_OUT, output ; output bit 0ror byteror donestuffed0:cpi done, 0xfcbrcc stuff0sbrs byte, 0eor output, usbmaskror bytestuffed1:out USB_OUT, output ; output bit 1ror donecpi done, 0xfcbrcc stuff1sbrs byte, 0eor output, usbmaskror bytenopstuffed2:out USB_OUT, output ; output bit 2ror donecpi done, 0xfcbrcc stuff2sbrs byte, 0eor output, usbmaskror bytenopstuffed3:out USB_OUT, output ; output bit 3ror donecpi done, 0xfcbrcc stuff3sbrs byte, 0eor output, usbmaskld next, Y+ ; 2 cyclesout USB_OUT, output ; output bit 4ror byteror donestuffed4:cpi done, 0xfcbrcc stuff4sbrs byte, 0eor output, usbmaskror bytestuffed5:out USB_OUT, output ; output bit 5ror donecpi done, 0xfcbrcc stuff5sbrs byte, 0eor output, usbmaskror bytestuffed6:ror doneout USB_OUT, output ; output bit 6cpi done, 0xfcbrcc stuff6sbrs byte, 0eor output, usbmaskror bytemov byte, nextstuffed7:ror doneout USB_OUT, output ; output bit 7cpi done, 0xfcbrcc stuff7dec countbrpl txloop ; 2 cyclesrjmp gen_eop; ----------------------------------------------------------------------; out-of-line code to insert stuffing bits; ----------------------------------------------------------------------stuff0: ; 2+3eor output, usbmaskclr doneout USB_OUT, outputrjmp stuffed0stuff1: ; 3eor output, usbmaskrjmp stuffed1stuff2: ; 3eor output, usbmaskrjmp stuffed2stuff3: ; 3eor output, usbmaskrjmp stuffed3stuff4: ; 2+3eor output, usbmaskclr doneout USB_OUT, outputrjmp stuffed4stuff5: ; 3eor output, usbmaskrjmp stuffed5stuff6: ; 3eor output, usbmaskrjmp stuffed6stuff7: ; 3eor output, usbmaskrjmp stuffed7; ----------------------------------------------------------------------; generate EOP, release the bus, and return from interrupt; ----------------------------------------------------------------------gen_eop:cbr output, USB_MASKout USB_OUT, output ; output SE0 for 2 bit timespop evenpop fixuppop byteldi count, 1<<USB_INT_PENDING_BITout USB_INT_PENDING, count ; interrupt was triggered by transmitpop YH ; this is the saved SREGpop YLin usbmask, USB_DDRmov count, outputori output, USB_MASK_DMINUSout USB_OUT, output ; output J state for 1 bit timecbr usbmask, USB_MASKout SREG, YHpop YHpop odd ; is the same register as output!nopout USB_DDR, usbmask ; release busout USB_OUT, count ; disable D- pulluppop usbmaskpop countreti