1 /* Name: usbdrvasm20.inc
2 * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers
3 * Author: Jeroen Benschop
4 * Based on usbdrvasm16.inc from Christian Starkjohann
5 * Creation Date: 2008-03-05
7 * Copyright: (c) 2008 by Jeroen Benschop and OBJECTIVE DEVELOPMENT Software GmbH
8 * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
11 /* Do not link this file! Link usbdrvasm.S instead, which includes the
12 * appropriate implementation!
17 This file is the 20 MHz version of the asssembler part of the USB driver. It
18 requires a 20 MHz crystal (not a ceramic resonator and not a calibrated RC
21 See usbdrv.h for a description of the entire driver.
23 Since almost all of this code is timing critical, don't change unless you
24 really know what you are doing! Many parts require not only a maximum number
25 of CPU cycles, but even an exact number of cycles!
29 #ifdef __IAR_SYSTEMS_ASM__
35 ;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes
36 ;nominal frequency: 20 MHz -> 13.333333 cycles per bit, 106.666667 cycles per byte
37 ; Numbers in brackets are clocks counted from center of last sync bit
38 ; when instruction starts
39 ;register use in receive loop:
40 ; shift assembles the byte currently being received
41 ; x1 holds the D+ and D- line state
42 ; x2 holds the previous line state
43 ; x4 (leap) is used to add a leap cycle once every three bytes received
44 ; X3 (leap2) is used to add a leap cycle once every three stuff bits received
45 ; bitcnt is used to determine when a stuff bit is due
46 ; cnt holds the number of bytes left in the receive buffer
49 ;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt
50 push YL ;[-28] push only what is necessary to sync with edge ASAP
54 ;----------------------------------------------------------------------------
55 ; Synchronize with sync pattern:
56 ;----------------------------------------------------------------------------
57 ;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K]
58 ;sync up with J to K edge during sync pattern -- use fastest possible loops
59 ;The first part waits at most 1 bit long since we must be in sync pattern.
60 ;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to
61 ;waitForJ, ensure that this prerequisite is met.
65 brne waitForJ ; just make sure we have ANY timeout
67 ;The following code results in a sampling window of < 1/4 bit which meets the spec.
68 sbis USBIN, USBMINUS ;[-19]
90 #endif /* USB_COUNT_SOF */
96 ;{3, 5} after falling D- edge, average delay: 4 cycles
97 ;bit0 should be at 34 for center sampling. Currently at 4 so 30 cylces till bit 0 sample
98 ;use 1 bit time for setup purposes, then sample again. Numbers in brackets
99 ;are cycles from center of first sync (double K) bit after the instruction
102 lds YL, usbInputBufOffset;[-14]
105 subi YL, lo8(-(usbRxBuf));[-11] [rx loop init]
106 sbci YH, hi8(-(usbRxBuf));[-10] [rx loop init]
109 ldi shift,0x40 ;[-7] set msb to "1" so processing bit7 can be detected
112 ldi bitcnt, 5 ;[-4] [rx loop init]
113 sbis USBIN, USBMINUS ;[-3] we want two bits K (sample 3 cycles too early)
114 rjmp haveTwoBitsK ;[-2]
115 pop shift ;[-1] undo the push from before
117 rjmp waitForK ;[3] this was not the end of sync, retry
118 ; The entire loop from waitForK until rjmp waitForK above must not exceed two
119 ; bit times (= 27 cycles).
121 ;----------------------------------------------------------------------------
122 ; push more registers and initialize values while we sample the first bits:
123 ;----------------------------------------------------------------------------
128 ldi leap2, 0x55 ;[6] add leap cycle on 2nd,5th,8th,... stuff bit
130 ldi leap, 0x55 ;[9] skip leap cycle on 2nd,5th,8th,... byte received
132 ldi cnt, USB_BUFSIZE ;[12] [rx loop init]
133 ldi x2, 1<<USBPLUS ;[13] current line state is K state. D+=="1", D-=="0"
135 in x1, USBIN ;[0] sample line state
136 andi x1, USBMASK ;[1] filter only D+ and D- bits
137 rjmp handleBit ;[2] make bit0 14 cycles long
139 ;----------------------------------------------------------------------------
140 ; Process bit7. However, bit 6 still may need unstuffing.
141 ;----------------------------------------------------------------------------
147 subi cnt, 1 ;[11] cannot use dec becaus it does not affect the carry flag
148 brcs overflow ;[12] Too many bytes received. Ignore packet
149 in x1, USBIN ;[0] sample line state
150 andi x1, USBMASK ;[1] filter only D+ and D- bits
151 cpse x1, x2 ;[2] when previous line state equals current line state, handle "1"
152 rjmp b7handle0 ;[3] when line state differs, handle "0"
154 ror shift ;[5] shift "1" into the data
155 st y+, shift ;[6] store the data into the buffer
156 ldi shift, 0x40 ;[7] reset data for receiving the next byte
157 subi leap, 0x55 ;[9] trick to introduce a leap cycle every 3 bytes
158 brcc nextInst ;[10 or 11] it will fail after 85 bytes. However low speed can only receive 11
159 dec bitcnt ;[11 or 12]
160 brne bit0 ;[12 or 13]
161 ldi x1, 1 ;[13 or 14] unstuffing bit 7
162 in bitcnt, USBIN ;[0] sample stuff bit
166 mov x2,x1 ;[5] Set x2 to current line state
168 lsr shift ;[7] shift "0" into the data
169 st y+, shift ;[8] store data into the buffer
170 ldi shift, 0x40 ;[10] reset data for receiving the next byte
171 subi leap, 0x55 ;[11] trick to introduce a leap cycle every 3 bytes
172 brcs bit0 ;[12] it will fail after 85 bytes. However low speed can only receive 11
176 ;----------------------------------------------------------------------------
178 ; x1==0xFF indicate unstuffing bit6
179 ;----------------------------------------------------------------------------
182 ldi x1,0xFF ;[12] indicate unstuffing bit 6
183 in bitcnt, USBIN ;[0] sample stuff bit
186 mov x2,bitcnt ;[3] [2] [3] Set x2 to match line state
187 subi leap2, 0x55 ;[4] [3] [4] delay loop
188 brcs nextInst ;[5] [4] [5] add one cycle every three stuff bits
189 sbci leap2,0 ;[6] [5] [6]
190 ldi bitcnt,6 ;[7] [6] [7] reset bit stuff counter
191 andi x2, USBMASK ;[8] [7] [8] only keep D+ and D-
192 cpi x1,0 ;[9] [8] [9]
193 brmi bit7 ;[10] [9] [10] finished unstuffing bit6 When x1<0
194 breq bitloop ;[11] --- [11] finished unstuffing bit0-5 when x1=0
196 in x1, USBIN ;--- --- [0] sample line state for bit0
197 andi x1, USBMASK ;--- --- [1] filter only D+ and D- bits
198 rjmp handleBit ;--- --- [2] make bit0 14 cycles long
200 ;----------------------------------------------------------------------------
201 ; Receiver loop (numbers in brackets are cycles within byte after instr)
202 ;----------------------------------------------------------------------------
204 in x1, USBIN ;[0] sample line state
205 andi x1, USBMASK ;[1] filter only D+ and D- bits
206 breq se0 ;[2] both lines are low so handle se0
208 cpse x1, x2 ;[3] when previous line state equals current line state, handle "1"
209 rjmp handle0 ;[4] when line state differs, handle "0"
211 ror shift ;[6] shift "1" into the data
212 brcs b6checkUnstuff ;[7] When after shift C is set, next bit is bit7
216 ldi x1,0 ;[12] indicate unstuff for bit other than bit6 or bit7
217 in bitcnt, USBIN ;[0] sample stuff bit
221 mov x2, x1 ;[6] Set x2 to current line state
222 ldi bitcnt, 6 ;[7] reset unstuff counter.
223 lsr shift ;[8] shift "0" into the data
224 brcs bit7 ;[9] When after shift C is set, next bit is bit7
228 ;----------------------------------------------------------------------------
229 ; End of receive loop. Now start handling EOP
230 ;----------------------------------------------------------------------------
232 macro POP_STANDARD ; 14 cycles
241 macro POP_RETI ; 7 cycles
250 #include "asmcommon.inc"
254 ; J = (D+ = 0), (D- = 1)
255 ; K = (D+ = 1), (D- = 0)
256 ; Spec allows 7.5 bit times from EOP to SOP for replies
257 ; 7.5 bit times is 100 cycles. This implementation arrives a bit later at se0
258 ; then specified in the include file but there is plenty of time
264 out USBOUT, x1 ;[12] <-- out
269 ldi x2, 0 ;[7] Carry is zero due to brcc
270 rol shift ;[8] compensate for ror shift at branch destination
275 ldi x3, USBPID_NAK ;[-18]
276 rjmp sendX3AndReti ;[-17]
278 ldi cnt, USBPID_ACK ;[-17]
282 ldi YL, 20 ;[-15] x3==r20 address is 20
285 ; rjmp usbSendAndReti fallthrough
288 ;pointer to data in 'Y'
289 ;number of bytes in 'cnt' -- including sync byte [range 2 ... 12]
290 ;uses: x1...x4, btcnt, shift, cnt, Y
291 ;Numbers in brackets are time since first bit of sync pattern is sent
292 ;We don't match the transfer rate exactly (don't insert leap cycles every third
293 ;byte) because the spec demands only 1.5% precision anyway.
294 usbSendAndReti: ; 12 cycles until SOP
296 ori x2, USBMASK ;[-11]
297 sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups)
298 in x1, USBOUT ;[-8] port mirror for tx loop
299 out USBDDR, x2 ;[-7] <- acquire bus
300 ; need not init x2 (bitstuff history) because sync starts with 0
301 ldi x4, USBMASK ;[-6] exor mask
302 ldi shift, 0x80 ;[-5] sync byte is first byte sent
304 ldi bitcnt, 0x49 ;[-4] [10] binary 01001001
306 sbrs shift, 0 ;[-3] [10] [11]
307 eor x1, x4 ;[-2] [11] [12]
308 out USBOUT, x1 ;[-1] [12] [13] <-- out N
309 ror shift ;[0] [13] [14]
323 out USBOUT, x1 ;[-1] [13] <-- out 7
333 cbr x1, USBMASK ;[9] prepare SE0 [spec says EOP may be 25 to 30 cycles]
334 lds x2, usbNewDeviceAddr;[10]
335 lsl x2 ;[12] we compare with left shifted address
336 out USBOUT, x1 ;[13] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle
337 subi YL, 20 + 2 ;[0] Only assign address on data packets, not ACK/NAK in x3
339 ;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm:
340 ;set address only after data packet was sent, not after handshake
341 breq skipAddrAssign ;[2]
342 sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer
344 ;end of usbDeviceAddress transfer
345 ldi x2, 1<<USB_INTR_PENDING_BIT;[4] int0 occurred during TX -- clear pending flag
346 USB_STORE_PENDING(x2) ;[5]
349 cbr x2, USBMASK ;[8] set both pins to input
351 cbr x3, USBMASK ;[10] configure no pullup on both pins
354 dec x4 ;[12] [15] [18] [21] [24]
355 brne se0Delay ;[13] [16] [19] [22] [25]
356 out USBOUT, x1 ;[26] <-- out J (idle) -- end of SE0 (EOP signal)
357 out USBDDR, x2 ;[27] <-- release bus now
358 out USBOUT, x3 ;[28] <-- ensure no pull-up resistors are active