/* Name: usbdrvasm20.inc * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers * Author: Jeroen Benschop * Based on usbdrvasm16.inc from Christian Starkjohann * Creation Date: 2008-03-05 * Tabsize: 4 * Copyright: (c) 2008 by Jeroen Benschop and OBJECTIVE DEVELOPMENT Software GmbH * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) */ /* Do not link this file! Link usbdrvasm.S instead, which includes the * appropriate implementation! */ /* General Description: This file is the 20 MHz version of the asssembler part of the USB driver. It requires a 20 MHz crystal (not a ceramic resonator and not a calibrated RC oscillator). See usbdrv.h for a description of the entire driver. Since almost all of this code is timing critical, don't change unless you really know what you are doing! Many parts require not only a maximum number of CPU cycles, but even an exact number of cycles! */ #define leap2 x3 #ifdef __IAR_SYSTEMS_ASM__ #define nextInst $+2 #else #define nextInst .+0 #endif ;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes ;nominal frequency: 20 MHz -> 13.333333 cycles per bit, 106.666667 cycles per byte ; Numbers in brackets are clocks counted from center of last sync bit ; when instruction starts ;register use in receive loop: ; shift assembles the byte currently being received ; x1 holds the D+ and D- line state ; x2 holds the previous line state ; x4 (leap) is used to add a leap cycle once every three bytes received ; X3 (leap2) is used to add a leap cycle once every three stuff bits received ; bitcnt is used to determine when a stuff bit is due ; cnt holds the number of bytes left in the receive buffer USB_INTR_VECTOR: ;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt push YL ;[-28] push only what is necessary to sync with edge ASAP in YL, SREG ;[-26] push YL ;[-25] push YH ;[-23] ;---------------------------------------------------------------------------- ; Synchronize with sync pattern: ;---------------------------------------------------------------------------- ;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] ;sync up with J to K edge during sync pattern -- use fastest possible loops ;The first part waits at most 1 bit long since we must be in sync pattern. ;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to ;waitForJ, ensure that this prerequisite is met. waitForJ: inc YL sbis USBIN, USBMINUS brne waitForJ ; just make sure we have ANY timeout waitForK: ;The following code results in a sampling window of < 1/4 bit which meets the spec. sbis USBIN, USBMINUS ;[-19] rjmp foundK ;[-18] sbis USBIN, USBMINUS rjmp foundK sbis USBIN, USBMINUS rjmp foundK sbis USBIN, USBMINUS rjmp foundK sbis USBIN, USBMINUS rjmp foundK sbis USBIN, USBMINUS rjmp foundK sbis USBIN, USBMINUS rjmp foundK sbis USBIN, USBMINUS rjmp foundK sbis USBIN, USBMINUS rjmp foundK #if USB_COUNT_SOF lds YL, usbSofCount inc YL sts usbSofCount, YL #endif /* USB_COUNT_SOF */ #ifdef USB_SOF_HOOK USB_SOF_HOOK #endif rjmp sofError foundK: ;[-16] ;{3, 5} after falling D- edge, average delay: 4 cycles ;bit0 should be at 34 for center sampling. Currently at 4 so 30 cylces till bit 0 sample ;use 1 bit time for setup purposes, then sample again. Numbers in brackets ;are cycles from center of first sync (double K) bit after the instruction push bitcnt ;[-16] ; [---] ;[-15] lds YL, usbInputBufOffset;[-14] ; [---] ;[-13] clr YH ;[-12] subi YL, lo8(-(usbRxBuf));[-11] [rx loop init] sbci YH, hi8(-(usbRxBuf));[-10] [rx loop init] push shift ;[-9] ; [---] ;[-8] ldi shift,0x40 ;[-7] set msb to "1" so processing bit7 can be detected nop2 ;[-6] ; [---] ;[-5] ldi bitcnt, 5 ;[-4] [rx loop init] sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early) rjmp haveTwoBitsK ;[-2] pop shift ;[-1] undo the push from before pop bitcnt ;[1] rjmp waitForK ;[3] this was not the end of sync, retry ; The entire loop from waitForK until rjmp waitForK above must not exceed two ; bit times (= 27 cycles). ;---------------------------------------------------------------------------- ; push more registers and initialize values while we sample the first bits: ;---------------------------------------------------------------------------- haveTwoBitsK: push x1 ;[0] push x2 ;[2] push x3 ;[4] (leap2) ldi leap2, 0x55 ;[6] add leap cycle on 2nd,5th,8th,... stuff bit push x4 ;[7] == leap ldi leap, 0x55 ;[9] skip leap cycle on 2nd,5th,8th,... byte received push cnt ;[10] ldi cnt, USB_BUFSIZE ;[12] [rx loop init] ldi x2, 1<