From 21a01860ef39a8710fcba8f7b7342e2b3295aed5 Mon Sep 17 00:00:00 2001 From: rgwan Date: Sat, 21 Dec 2013 20:10:55 +0800 Subject: [PATCH 1/2] Added LGT MCU cpu duty support. --- examples/drivertest/firmware/Makefile | 2 +- examples/drivertest/firmware/usbconfig.h | 3 +- usbdrv/usbconfig-prototype.h | 3 +- usbdrv/usbdrvasm.S | 41 +++++++++++++++--------- usbdrv/usbportability.h | 34 +++++++++++++++++--- 5 files changed, 61 insertions(+), 22 deletions(-) diff --git a/examples/drivertest/firmware/Makefile b/examples/drivertest/firmware/Makefile index 326072b..ac243e6 100644 --- a/examples/drivertest/firmware/Makefile +++ b/examples/drivertest/firmware/Makefile @@ -7,7 +7,7 @@ # License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) DEVICE = atmega8 -F_CPU = 12800000 # in Hz +F_CPU = 12000000 # in Hz FUSE_L = 0x94 FUSE_H = 0xc9 AVRDUDE = avrdude -c stk500v2 -P avrdoper -p $(DEVICE) # edit this line for your programmer diff --git a/examples/drivertest/firmware/usbconfig.h b/examples/drivertest/firmware/usbconfig.h index 7272869..17f69ca 100644 --- a/examples/drivertest/firmware/usbconfig.h +++ b/examples/drivertest/firmware/usbconfig.h @@ -21,7 +21,8 @@ section at the end of this file). */ /* ---------------------------- Hardware Config ---------------------------- */ - +#define USB_RUNNING_ON_LGT 1 +/* This is the switch of support LGT 's cpu duty or AVR 's CPU duty */ #define USB_CFG_IOPORTNAME D /* This is the port where the USB bus is connected. When you configure it to * "B", the registers PORTB, PINB and DDRB will be used. diff --git a/usbdrv/usbconfig-prototype.h b/usbdrv/usbconfig-prototype.h index 93721c2..4c48048 100644 --- a/usbdrv/usbconfig-prototype.h +++ b/usbdrv/usbconfig-prototype.h @@ -24,7 +24,8 @@ section at the end of this file). */ /* ---------------------------- Hardware Config ---------------------------- */ - +#define USB_RUNNING_ON_LGT 1 +/* This is the switch of support LGT 's cpu duty or AVR 's CPU duty */ #define USB_CFG_IOPORTNAME D /* This is the port where the USB bus is connected. When you configure it to * "B", the registers PORTB, PINB and DDRB will be used. diff --git a/usbdrv/usbdrvasm.S b/usbdrv/usbdrvasm.S index 32ce8ef..ba81ed7 100644 --- a/usbdrv/usbdrvasm.S +++ b/usbdrv/usbdrvasm.S @@ -374,19 +374,30 @@ usbMFTimeout: # error "USB_CFG_CLOCK_KHZ is not one of the supported crc-rates!" # endif #else /* USB_CFG_CHECK_CRC */ -# if USB_CFG_CLOCK_KHZ == 12000 -# include "usbdrvasm12.inc" -# elif USB_CFG_CLOCK_KHZ == 12800 -# include "usbdrvasm128.inc" -# elif USB_CFG_CLOCK_KHZ == 15000 -# include "usbdrvasm15.inc" -# elif USB_CFG_CLOCK_KHZ == 16000 -# include "usbdrvasm16.inc" -# elif USB_CFG_CLOCK_KHZ == 16500 -# include "usbdrvasm165.inc" -# elif USB_CFG_CLOCK_KHZ == 20000 -# include "usbdrvasm20.inc" -# else -# error "USB_CFG_CLOCK_KHZ is not one of the supported non-crc-rates!" -# endif +# if USB_RUNNING_ON_LGT + /* Logic Green V-USB support */ +# if USB_CFG_CLOCK_KHZ == 12000 +# include "usbdrvasm12-lgt.inc" +# elif USB_CFG_CLOCK_KHZ == 16000 +# include "usbdrvasm16-lgt.inc" +# else +# error "USB_CFG_CLOCK_KHZ is not one of the supported non-crc-rates!" +# endif +# else +# if USB_CFG_CLOCK_KHZ == 12000 +# include "usbdrvasm12.inc" +# elif USB_CFG_CLOCK_KHZ == 12800 +# include "usbdrvasm128.inc" +# elif USB_CFG_CLOCK_KHZ == 15000 +# include "usbdrvasm15.inc" +# elif USB_CFG_CLOCK_KHZ == 16000 +# include "usbdrvasm16.inc" +# elif USB_CFG_CLOCK_KHZ == 16500 +# include "usbdrvasm165.inc" +# elif USB_CFG_CLOCK_KHZ == 20000 +# include "usbdrvasm20.inc" +# else +# error "USB_CFG_CLOCK_KHZ is not one of the supported non-crc-rates!" +# endif +# endif #endif /* USB_CFG_CHECK_CRC */ diff --git a/usbdrv/usbportability.h b/usbdrv/usbportability.h index 0a861d0..fdc428a 100644 --- a/usbdrv/usbportability.h +++ b/usbdrv/usbportability.h @@ -62,7 +62,16 @@ Thanks to Oleg Semyonov for his help with the IAR tools port! #define _BV(x) (1 << (x)) /* assembler compatibility macros */ -#define nop2 rjmp $+2 /* jump to next instruction */ +#if USB_RUNNING_ON_LGT + #ifdef __ASSEMBLER__ + macro nop2 + nop + nop + endm + #endif +#else + #define nop2 rjmp $+2 /* jump to next instruction */ +#endif #define XL r26 #define XH r27 #define YL r28 @@ -111,8 +120,16 @@ static inline void sei(void) #define macro .macro #define endm .endmacro -#define nop2 rjmp .+0 /* jump to next instruction */ - +#if USB_RUNNING_ON_LGT + #ifdef __ASSEMBLER__ + macro nop2 + nop + nop + endm + #endif +#else + #define nop2 rjmp .+0 /* jump to next instruction */ +#endif /* ------------------------------------------------------------------------- */ #else /* default development environment is avr-gcc/avr-libc */ /* ------------------------------------------------------------------------- */ @@ -132,7 +149,16 @@ static inline void sei(void) #define macro .macro #define endm .endm -#define nop2 rjmp .+0 /* jump to next instruction */ +#if USB_RUNNING_ON_LGT + #ifdef __ASSEMBLER__ + macro nop2 + nop + nop + endm + #endif +#else + #define nop2 rjmp .+0 /* jump to next instruction */ +#endif #endif /* development environment */ From 76aa14bddc2c2b24de518140c5560bab53b5c684 Mon Sep 17 00:00:00 2001 From: rgwan Date: Sat, 21 Dec 2013 20:13:27 +0800 Subject: [PATCH 2/2] Added LGT MCU cpu duty support. --- usbdrv/usbdrvasm12-lgt.inc | 433 +++++++++++++++++++++++++++++++++++++ usbdrv/usbdrvasm16-lgt.inc | 372 +++++++++++++++++++++++++++++++ 2 files changed, 805 insertions(+) create mode 100644 usbdrv/usbdrvasm12-lgt.inc create mode 100644 usbdrv/usbdrvasm16-lgt.inc diff --git a/usbdrv/usbdrvasm12-lgt.inc b/usbdrv/usbdrvasm12-lgt.inc new file mode 100644 index 0000000..339f66c --- /dev/null +++ b/usbdrv/usbdrvasm12-lgt.inc @@ -0,0 +1,433 @@ +/* Name: usbdrvasm12.inc + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2004-12-29 + * Tabsize: 4 + * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * This Revision: $Id$ + */ + +/* Do not link this file! Link usbdrvasm.S instead, which includes the + * appropriate implementation! + */ + +/* +General Description: +This file is the 12 MHz version of the asssembler part of the USB driver. It +requires a 12 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! + + +Timing constraints according to spec (in bit times): +timing subject min max CPUcycles +--------------------------------------------------------------------------- +EOP of OUT/SETUP to sync pattern of DATA0 (both rx) 2 16 16-128 +EOP of IN to sync pattern of DATA0 (rx, then tx) 2 7.5 16-60 +DATAx (rx) to ACK/NAK/STALL (tx) 2 7.5 16-60 +*/ + +;Software-receiver engine. Strict timing! Don't change unless you can preserve timing! +;interrupt response time: 4 cycles + insn running = 7 max if interrupts always enabled +;max allowable interrupt latency: 34 cycles -> max 25 cycles interrupt disable +;max stack usage: [ret(2), YL, SREG, YH, shift, x1, x2, x3, cnt, x4] = 11 bytes +;Numbers in brackets are maximum cycles since SOF. +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt + push YL ;2 [35] push only what is necessary to sync with edge ASAP + in YL, SREG ;1 [37] + push YL ;2 [39] +;---------------------------------------------------------------------------- +; 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 + in x5, USBIN + 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. + in x5, USBIN + sbis USBIN, USBMINUS + rjmp foundK + in x5, USBIN + sbis USBIN, USBMINUS + rjmp foundK + in x5, USBIN + sbis USBIN, USBMINUS + rjmp foundK + in x5, USBIN + sbis USBIN, USBMINUS + rjmp foundK + in x5, USBIN + 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: +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] +;we have 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 YH ;1 [1] + push x1 ;1 [2] + lds YL, usbInputBufOffset ;2 [4] + clr YH ;1 [5] + subi YL, lo8(-(usbRxBuf)) ;1 [6] + sbci YH, hi8(-(usbRxBuf)) ;1 [7] + + ;in x1, USBIN ;1 [8] + sbis USBIN, USBMINUS ;1 [9] + ;sbis USBIN, USBMINUS ;1 [8] we want two bits K [sample 1 cycle too early] + rjmp haveTwoBitsK ;1 [10] + pop x1 + pop YH ;1 [11] undo the push from before + rjmp waitForK ;1 [12] this was not the end of sync, retry +haveTwoBitsK: +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- + pop x1 ;1 [10] + push shift ;1 [11];2 [16] + push x1 ;1 [12];2 [12] + push x2 ;1 [13];2 [14] + nop ;2 [15] + nop + + in x1, USBIN ;1 [17] <-- sample bit 0 + ldi shift, 0xff ;1 [18] + bst x1, USBMINUS ;1 [19] + bld shift, 0 ;1 [20] + push x3 ;1 [21];2 [22] + push cnt ;1 [22];2 [24] + nop ;2 [24] + nop + + in x2, USBIN ;1 [25] <-- sample bit 1 + ser x3 ;1 [26] [inserted init instruction] + eor x1, x2 ;1 [27] + bst x1, USBMINUS ;1 [28] + bld shift, 1 ;1 [29] + ldi cnt, USB_BUFSIZE;1 [30] [inserted init instruction] + nop ;1 [31] + rjmp rxbit2 ;1 [32];2[32] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- + +unstuff0: ;1 (branch taken) + andi x3, ~0x01 ;1 [15] + mov x1, x2 ;1 [16] x2 contains last sampled (stuffed) bit + in x2, USBIN ;1 [17] <-- sample bit 1 again + ori shift, 0x01 ;1 [18] + nop ;1 [19] + rjmp didUnstuff0 ;1 [20];2 [20] + +unstuff1: ;1 (branch taken) + mov x2, x1 ;1 [21] x1 contains last sampled (stuffed) bit + andi x3, ~0x02 ;1 [22] + ori shift, 0x02 ;1 [23] + nop ;1 [24] + in x1, USBIN ;1 [25] <-- sample bit 2 again + nop ;1 [26] + rjmp didUnstuff1 ;1 [27];2 [27] + +unstuff2: ;1 (branch taken) + andi x3, ~0x04 ;1 [29] + ori shift, 0x04 ;1 [30] + mov x1, x2 ;1 [31] x2 contains last sampled (stuffed) bit + nop ;1 [32] + in x2, USBIN ;1 [33] <-- sample bit 3 + nop ;1 [34] + rjmp didUnstuff2 ;1 [35];2 [35] + +unstuff3: ;1 (branch taken) + in x2, USBIN ;1 [34] <-- sample stuffed bit 3 [one cycle too late] + andi x3, ~0x08 ;1 [35] + ori shift, 0x08 ;1 [36] + nop ;1 [37] + rjmp didUnstuff3 ;1 [38];2 [38] + +unstuff4: ;1 (branch taken) + andi x3, ~0x10 ;1 [40] + in x1, USBIN ;1 [41] <-- sample stuffed bit 4 + ori shift, 0x10 ;1 [42] + nop ;1 [43] + rjmp didUnstuff4 ;1 [44];2 [44] + +unstuff5: ;1 (branch taken) + andi x3, ~0x20 ;1 [48] + in x2, USBIN ;1 [49] <-- sample stuffed bit 5 + ori shift, 0x20 ;1 [50] + nop ;1 [51] + rjmp didUnstuff5 ;1 [52];2 [52] + +unstuff6: ;1 (branch taken) + andi x3, ~0x40 ;1 [56] + in x1, USBIN ;1 [57] <-- sample stuffed bit 6 + ori shift, 0x40 ;1 [58] + nop ;1 [59] + rjmp didUnstuff6 ;1 [60];2 [60] + +; extra jobs done during bit interval: +; bit 0: store, clear [SE0 is unreliable here due to bit dribbling in hubs] +; bit 1: se0 check +; bit 2: overflow check +; bit 3: recovery from delay [bit 0 tasks took too long] +; bit 4: none +; bit 5: none +; bit 6: none +; bit 7: jump, eor +rxLoop: + eor x3, shift ;1 [0] reconstruct: x3 is 0 at bit locations we changed, 1 at others + in x1, USBIN ;1 [1] <-- sample bit 0 + nop ;1 [2] + st y+, x3 ;1 [3];2 [3] store data + ser x3 ;1 [4] + nop ;1 [5] + eor x2, x1 ;1 [6] + bst x2, USBMINUS;1 [7] + bld shift, 0 ;1 [8] + in x2, USBIN ;1 [9] <-- sample bit 1 (or possibly bit 0 stuffed) + andi x2, USBMASK ;1 [10] + breq se0 ;1 [11] SE0 check for bit 1 + andi shift, 0xf9 ;1 [12] +didUnstuff0: + breq unstuff0 ;1 [13] + eor x1, x2 ;1 [14] + bst x1, USBMINUS;1 [15] + bld shift, 1 ;1 [16] +rxbit2: + in x1, USBIN ;1 [17] <-- sample bit 2 (or possibly bit 1 stuffed) + andi shift, 0xf3 ;1 [18] + breq unstuff1 ;1 [19] do remaining work for bit 1 +didUnstuff1: + subi cnt, 1 ;1 [20] + brcs overflow ;1 [21] loop control + eor x2, x1 ;1 [22] + bst x2, USBMINUS;1 [23] + bld shift, 2 ;1 [24] + in x2, USBIN ;1 [25] <-- sample bit 3 (or possibly bit 2 stuffed) + andi shift, 0xe7 ;1 [26] + breq unstuff2 ;1 [27] +didUnstuff2: + eor x1, x2 ;1 [28] + bst x1, USBMINUS;1 [29] + bld shift, 3 ;1 [30] +didUnstuff3: + andi shift, 0xcf ;1 [31] + breq unstuff3 ;1 [32] + in x1, USBIN ;1 [33] <-- sample bit 4 + eor x2, x1 ;1 [34] + bst x2, USBMINUS;1 [35] + bld shift, 4 ;1 [36] +didUnstuff4: + andi shift, 0x9f ;1 [37] + breq unstuff4 ;1 [38] + nop ;2 [40] + nop + in x2, USBIN ;1 [41] <-- sample bit 5 + eor x1, x2 ;1 [42] + bst x1, USBMINUS;1 [43] + bld shift, 5 ;1 [44] +didUnstuff5: + andi shift, 0x3f ;1 [45] + breq unstuff5 ;1 [46] + nop ;2 [48] + nop + in x1, USBIN ;1 [49] <-- sample bit 6 + eor x2, x1 ;1 [50] + bst x2, USBMINUS;1 [51] + bld shift, 6 ;1 [52] +didUnstuff6: + cpi shift, 0x02 ;1 [53] + brlo unstuff6 ;1 [54] + nop ;2 [56] + nop + in x2, USBIN ;1 [57] <-- sample bit 7 + eor x1, x2 ;1 [58] + bst x1, USBMINUS;1 [59] + bld shift, 7 ;1 [60] +didUnstuff7: + cpi shift, 0x04 ;1 [61] + brsh rxLoop ;2 [63] loop control +unstuff7: + andi x3, ~0x80 ;1 [63] + ori shift, 0x80 ;1 [64] + in x2, USBIN ;1 [65] <-- sample stuffed bit 7 + nop ;2 [67];1 [66] + nop + rjmp didUnstuff7 ;1 [68];2 [68] + +macro POP_STANDARD ; 12 cycles + pop cnt + pop x3 + pop x2 + pop x1 + pop shift + pop YH + endm +macro POP_RETI ; 5 cycles + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +;---------------------------------------------------------------------------- +; Transmitting data +;---------------------------------------------------------------------------- + +txByteLoop: +txBitloop: +stuffN1Delay: ; [03] + ror shift ;[-5] [11] [59] + brcc doExorN1 ;[-4] [60] + subi x4, 1 ;[-3] + brne commonN1 ;[-2] + lsl shift ;[-1] compensate ror after rjmp stuffDelay + nop ;[00] stuffing consists of just waiting 8 cycles + rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear + +sendNakAndReti: ;0 [-19] 19 cycles until SOP + ldi x3, USBPID_NAK ;1 [-18] + nop ;1 [-17] + rjmp usbSendX3 ;1;[-16];2 [-16] +sendAckAndReti: ;0 [-19] 19 cycles until SOP + ldi x3, USBPID_ACK ;1 [-18] + nop ;1 [-17] + rjmp usbSendX3 ;1 [-16];2 [-16] +sendCntAndReti: ;0 [-17] 17 cycles until SOP + mov x3, cnt ;1 [-16] +usbSendX3: ;0 [-16] + ldi YL, 20 ;1 [-15] 'x3' is R20 + ldi YH, 0 ;1 [-14] + ldi cnt, 2 ;1 [-13] +; rjmp usbSendAndReti fallthrough + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 +; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 +; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte +;uses: x1...x2, x4, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x4 = bitstuff cnt] +;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) +usbSendAndReti: + in x2, USBDDR ;[-12] 12 cycles until SOP + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS ;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + nop ;[-9] + out USBDDR, x2 ;[-8] <--- acquire bus + in x1, USBOUT ;[-7] port mirror for tx loop + ldi shift, 0x40 ;[-6] sync byte is first byte sent (we enter loop after ror) + ldi x2, USBMASK ;[-5] + push x4 ;[-4] + nop ;[-3] +doExorN1: + eor x1, x2 ;[-2] [06] [62] + ldi x4, 6 ;[-1] [07] [63] +commonN1: +stuffN2Delay: + out USBOUT, x1 ;[00] [08] [64] <--- set bit + ror shift ;[01] + brcc doExorN2 ;[02] + subi x4, 1 ;[03] + brne commonN2 ;[04] + lsl shift ;[05] compensate ror after rjmp stuffDelay + nop ;[06] + rjmp stuffN2Delay ;[07];[06] after ror, C bit is reliably clear +doExorN2: + eor x1, x2 ;[04] [12] + ldi x4, 6 ;[05] [13] +commonN2: + nop ;[06] [14] + subi cnt, 171 ;[07] [15] trick: (3 * 171) & 0xff = 1 + out USBOUT, x1 ;[08] [16] <--- set bit + brcs txBitloop ;[09] [25] [41] + +stuff6Delay: + ror shift ;[42] [50] + brcc doExor6 ;[43] + subi x4, 1 ;[44] + brne common6 ;[45] + lsl shift ;[46] compensate ror after rjmp stuffDelay + nop ;[48] stuffing consists of just waiting 8 cycles + nop + rjmp stuff6Delay ;[49] after ror, C bit is reliably clear +doExor6: + eor x1, x2 ;[45] [53] + ldi x4, 6 ;[46] +common6: +stuff7Delay: + ror shift ;[47] [55] + out USBOUT, x1 ;[48] <--- set bit + brcc doExor7 ;[49] + subi x4, 1 ;[50] + brne common7 ;[51] + lsl shift ;[52] compensate ror after rjmp stuffDelay + rjmp stuff7Delay ;[53] after ror, C bit is reliably clear +doExor7: + eor x1, x2 ;[51] [59] + ldi x4, 6 ;[52] +common7: + ld shift, y+ ;[53] + nop ;[54] + tst cnt ;[55];[55] + out USBOUT, x1 ;[56];[56] <--- set bit + ;brne txByteLoop ;[57] + breq makeSE0 ;[57] + rjmp txByteLoop ;[58] + +makeSE0: +;make SE0: + cbr x1, USBMASK ;[59] prepare SE0 [spec says EOP may be 15 to 18 cycles] + lds x2, usbNewDeviceAddr;[60] + lsl x2 ;[62] we compare with left shifted address + subi YL, 2 + 20 ;[63] Only assign address on data packets, not ACK/NAK in x3 + out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle + sbci YH, 0 ;[01] +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[02] + sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1< 10.6666666 cycles per bit, 85.333333333 cycles per byte +; Numbers in brackets are clocks counted from center of last sync bit +; when instruction starts + +USB_INTR_VECTOR: +;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt + push YL ;[-25] push only what is necessary to sync with edge ASAP + in YL, SREG ;[-23] + push YL ;[-22] + push YH ;[-20] +;---------------------------------------------------------------------------- +; 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 + in x1, USBIN + sbrs x1, USBMINUS + ;nop + ;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. + in x1, USBIN + sbrs x1, USBMINUS + rjmp foundK + in x1, USBIN + sbrs x1, USBMINUS + rjmp foundK + in x1, USBIN + sbrs x1, USBMINUS + rjmp foundK + in x1, USBIN + sbrs x1, USBMINUS + rjmp foundK + in x1, USBIN + sbrs x1, 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: ;[-12] +;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] +;we have 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 ;[-12] + nop ;[-11] +; [---] ;[-11] + lds YL, usbInputBufOffset;[-10] +; [---] ;[-9] + clr YH ;[-8] + subi YL, lo8(-(usbRxBuf));[-7] [rx loop init] + sbci YH, hi8(-(usbRxBuf));[-6] [rx loop init] + push shift ;[-5] +; [---] ;[] + ldi bitcnt, 0x55 ;[-4] [rx loop init] + in shift, USBIN ;[-3] + sbrs shift, USBMINUS ;[-2] + //nop ;[-3] + // sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) + rjmp haveTwoBitsK ;[-1] + pop shift ;[0] undo the push from before + pop bitcnt ;[2] undo the push from before + rjmp waitForK ;[4] this was not the end of sync, retry +; The entire loop from waitForK until rjmp waitForK above must not exceed two +; bit times (= 21 cycles). + +;---------------------------------------------------------------------------- +; push more registers and initialize values while we sample the first bits: +;---------------------------------------------------------------------------- +haveTwoBitsK: + push x1 ;[0];1 [1] + push x2 ;[1];1 [2] + push x3 ;[2];1 [3] + nop2 ;[3];2 [5] + nop2 ;[5];2 [7] + ldi shift, 0 ;[7];1 [8] + ldi x3, 1<<4 ;[8];1 [9] [rx loop init] first sample is inverse bit, compensate that + push x4 ;[9];1 [10] == leap + nop ;[10];1 [11] + + in x1, USBIN ;[11];1 [12] <-- sample bit 0 + andi x1, USBMASK ;[12];1 [13] + bst x1, USBMINUS ;[13];1 [14] + bld shift, 7 ;[14];1 [15] + push cnt ;[15];1 [16] + nop ;[16];1 [17] + ldi leap, 0 ;[17];1 [18] [rx loop init] + ldi cnt, USB_BUFSIZE;[18];1 [19] [rx loop init] + nop ;[19];1 [20] + rjmp rxbit1 ;[20];1 [21] arrives at [21] + +;---------------------------------------------------------------------------- +; Receiver loop (numbers in brackets are cycles within byte after instr) +;---------------------------------------------------------------------------- + +; duration of unstuffing code should be 10.66666667 cycles. We adjust "leap" +; accordingly to approximate this value in the long run. + +unstuff6: + andi x2, USBMASK ;[03] + ori x3, 1<<6 ;[04] will not be shifted any more + andi shift, ~0x80;[05] + mov x1, x2 ;[06] sampled bit 7 is actually re-sampled bit 6 + subi leap, -1 ;[07] total duration = 11 bits -> subtract 1/3 + rjmp didUnstuff6 ;[08] + +unstuff7: + ori x3, 1<<7 ;[09] will not be shifted any more + in x2, USBIN ;[00] [10] re-sample bit 7 + andi x2, USBMASK ;[01] + andi shift, ~0x80;[02] + subi leap, 2 ;[03] total duration = 10 bits -> add 1/3 + rjmp didUnstuff7 ;[04] + +unstuffEven: + ori x3, 1<<6 ;[09] will be shifted right 6 times for bit 0 + in x1, USBIN ;[00] [10] + andi shift, ~0x80;[01] + andi x1, USBMASK ;[02] + breq se0 ;[03] + subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 + nop2 ;[05] + rjmp didUnstuffE ;[06] + +unstuffOdd: + ori x3, 1<<5 ;[09] will be shifted right 4 times for bit 1 + in x2, USBIN ;[00] [10] + andi shift, ~0x80;[01] + andi x2, USBMASK ;[02] + breq se0 ;[03] + subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 + nop2 ;[05] + rjmp didUnstuffO ;[06] + +rxByteLoop: + andi x1, USBMASK ;[03] + eor x2, x1 ;[04] + subi leap, 1 ;[05] + brpl skipLeap ;[06] + subi leap, -3 ;1 one leap cycle every 3rd byte -> 85 + 1/3 cycles per byte + nop ;1 +skipLeap: + subi x2, 1 ;[08] + ror shift ;[09] +didUnstuff6: + cpi shift, 0xfc ;[10] + in x2, USBIN ;[00] [11] <-- sample bit 7 + brcc unstuff6 ;[01] + andi x2, USBMASK ;[02] + eor x1, x2 ;[03] + subi x1, 1 ;[04] + ror shift ;[05] +didUnstuff7: + cpi shift, 0xfc ;[06] + brcc unstuff7 ;[07] + eor x3, shift ;[08] reconstruct: x3 is 1 at bit locations we changed, 0 at others + st y+, x3 ;[09] store data + nop ;[10] +rxBitLoop: + in x1, USBIN ;[00] [11] <-- sample bit 0/2/4 + andi x1, USBMASK ;[01] + eor x2, x1 ;[02] + andi x3, 0x3f ;[03] topmost two bits reserved for 6 and 7 + subi x2, 1 ;[04] + ror shift ;[05] + cpi shift, 0xfc ;[06] + brcc unstuffEven ;[07] +didUnstuffE: + lsr x3 ;[08] + lsr x3 ;[09] +rxbit1: + in x2, USBIN ;[00] [10] <-- sample bit 1/3/5 + andi x2, USBMASK ;[01] + breq se0 ;[02] + eor x1, x2 ;[03] + subi x1, 1 ;[04] + ror shift ;[05] + cpi shift, 0xfc ;[06] + brcc unstuffOdd ;[07] +didUnstuffO: + subi bitcnt, 0xab;[08] == addi 0x55, 0x55 = 0x100/3 + brcs rxBitLoop ;[09] + + subi cnt, 1 ;[10] + in x1, USBIN ;[00] [11] <-- sample bit 6 + brcc rxByteLoop ;[01] + rjmp overflow + +macro POP_STANDARD ; 14 cycles + pop cnt + pop x4 + pop x3 + pop x2 + pop x1 + pop shift + pop bitcnt + endm +macro POP_RETI ; 7 cycles + pop YH + pop YL + out SREG, YL + pop YL + endm + +#include "asmcommon.inc" + +; USB spec says: +; idle = J +; J = (D+ = 0), (D- = 1) +; K = (D+ = 1), (D- = 0) +; Spec allows 7.5 bit times from EOP to SOP for replies + +bitstuffN: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] + nop2 ;[7] + nop ;[9] + out USBOUT, x1 ;[10] <-- out + nop ;[0] + rjmp didStuffN ;[1] + +bitstuff6: + eor x1, x4 ;[5] + ldi x2, 0 ;[6] Carry is zero due to brcc + rol shift ;[7] compensate for ror shift at branch destination + nop ;[8] + rjmp didStuff6 ;[9] + +bitstuff7: + ldi x2, 0 ;[2] Carry is zero due to brcc + nop ;[3] + rjmp didStuff7 ;[4] + + +sendNakAndReti: + ldi x3, USBPID_NAK ;[-18] + nop ;[-17] + rjmp sendX3AndReti ;[-16] +sendAckAndReti: + ldi cnt, USBPID_ACK ;[-17] +sendCntAndReti: + mov x3, cnt ;[-16] +sendX3AndReti: + ldi YL, 20 ;[-15] x3==r20 address is 20 + ldi YH, 0 ;[-14] + ldi cnt, 2 ;[-13] +; rjmp usbSendAndReti fallthrough + +;usbSend: +;pointer to data in 'Y' +;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] +;uses: x1...x4, btcnt, shift, cnt, Y +;Numbers in brackets are time since first bit of sync pattern is sent +;We don't match the transfer rate exactly (don't insert leap cycles every third +;byte) because the spec demands only 1.5% precision anyway. +usbSendAndReti: ; 12 cycles until SOP + in x2, USBDDR ;[-12] + ori x2, USBMASK ;[-11] + sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) + nop ;[-9] + in x1, USBOUT ;[-8] port mirror for tx loop + out USBDDR, x2 ;[-7] <- acquire bus +; need not init x2 (bitstuff history) because sync starts with 0 + ldi x4, USBMASK ;[-6] exor mask + ldi shift, 0x80 ;[-5] sync byte is first byte sent +txByteLoop: + ldi bitcnt, 0x35 ;[-4] [6] binary 0011 0101 +txBitLoop: + sbrs shift, 0 ;[-3] [7] + eor x1, x4 ;[-2] [8] + out USBOUT, x1 ;[-1] [9] <-- out N + ror shift ;[0] [10] + ror x2 ;[1] +didStuffN: + cpi x2, 0xfc ;[2] + brcc bitstuffN ;[3] + lsr bitcnt ;[4] + brcc txBitLoop ;[5] + brne txBitLoop ;[6] + + sbrs shift, 0 ;[7] + eor x1, x4 ;[8] + nop +didStuff6: + out USBOUT, x1 ;[-1] [9] <-- out 6 + ror shift ;[0] [10] + ror x2 ;[1] + cpi x2, 0xfc ;[2] + brcc bitstuff6 ;[3] + ror shift ;[4] +didStuff7: + ror x2 ;[5] + sbrs x2, 7 ;[6] + eor x1, x4 ;[7] + nop ;[8] + cpi x2, 0xfc ;[9] + out USBOUT, x1 ;[-1][10] <-- out 7 + brcc bitstuff7 ;[0] [11] + ld shift, y+ ;[1] + nop ;[2] + dec cnt ;[3] + brne txByteLoop ;[4] +;make SE0: + cbr x1, USBMASK ;[5] prepare SE0 [spec says EOP may be 21 to 25 cycles] + lds x2, usbNewDeviceAddr;[6] + lsl x2 ;[8] we compare with left shifted address + subi YL, 20 + 2 ;[9] Only assign address on data packets, not ACK/NAK in x3 + sbci YH, 0 ;[10] + out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle +;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: +;set address only after data packet was sent, not after handshake + breq skipAddrAssign ;[0] + sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer +skipAddrAssign: +;end of usbDeviceAddress transfer + ldi x2, 1<