usbdrv/usbdrvasm20.inc

changeset 0
9e9b2c78bd31
equal deleted inserted replaced
-1:000000000000 0:9e9b2c78bd31
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
6 * Tabsize: 4
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)
9 */
10
11 /* Do not link this file! Link usbdrvasm.S instead, which includes the
12 * appropriate implementation!
13 */
14
15 /*
16 General Description:
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
19 oscillator).
20
21 See usbdrv.h for a description of the entire driver.
22
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!
26 */
27
28 #define leap2 x3
29 #ifdef __IAR_SYSTEMS_ASM__
30 #define nextInst $+2
31 #else
32 #define nextInst .+0
33 #endif
34
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
47
48 USB_INTR_VECTOR:
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
51 in YL, SREG ;[-26]
52 push YL ;[-25]
53 push YH ;[-23]
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.
62 waitForJ:
63 inc YL
64 sbis USBIN, USBMINUS
65 brne waitForJ ; just make sure we have ANY timeout
66 waitForK:
67 ;The following code results in a sampling window of < 1/4 bit which meets the spec.
68 sbis USBIN, USBMINUS ;[-19]
69 rjmp foundK ;[-18]
70 sbis USBIN, USBMINUS
71 rjmp foundK
72 sbis USBIN, USBMINUS
73 rjmp foundK
74 sbis USBIN, USBMINUS
75 rjmp foundK
76 sbis USBIN, USBMINUS
77 rjmp foundK
78 sbis USBIN, USBMINUS
79 rjmp foundK
80 sbis USBIN, USBMINUS
81 rjmp foundK
82 sbis USBIN, USBMINUS
83 rjmp foundK
84 sbis USBIN, USBMINUS
85 rjmp foundK
86 #if USB_COUNT_SOF
87 lds YL, usbSofCount
88 inc YL
89 sts usbSofCount, YL
90 #endif /* USB_COUNT_SOF */
91 #ifdef USB_SOF_HOOK
92 USB_SOF_HOOK
93 #endif
94 rjmp sofError
95 foundK: ;[-16]
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
100 push bitcnt ;[-16]
101 ; [---] ;[-15]
102 lds YL, usbInputBufOffset;[-14]
103 ; [---] ;[-13]
104 clr YH ;[-12]
105 subi YL, lo8(-(usbRxBuf));[-11] [rx loop init]
106 sbci YH, hi8(-(usbRxBuf));[-10] [rx loop init]
107 push shift ;[-9]
108 ; [---] ;[-8]
109 ldi shift,0x40 ;[-7] set msb to "1" so processing bit7 can be detected
110 nop2 ;[-6]
111 ; [---] ;[-5]
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
116 pop bitcnt ;[1]
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).
120
121 ;----------------------------------------------------------------------------
122 ; push more registers and initialize values while we sample the first bits:
123 ;----------------------------------------------------------------------------
124 haveTwoBitsK:
125 push x1 ;[0]
126 push x2 ;[2]
127 push x3 ;[4] (leap2)
128 ldi leap2, 0x55 ;[6] add leap cycle on 2nd,5th,8th,... stuff bit
129 push x4 ;[7] == leap
130 ldi leap, 0x55 ;[9] skip leap cycle on 2nd,5th,8th,... byte received
131 push cnt ;[10]
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"
134 bit0:
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
138
139 ;----------------------------------------------------------------------------
140 ; Process bit7. However, bit 6 still may need unstuffing.
141 ;----------------------------------------------------------------------------
142
143 b6checkUnstuff:
144 dec bitcnt ;[9]
145 breq unstuff6 ;[10]
146 bit7:
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"
153 sec ;[4]
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
163 rjmp unstuff ;[1]
164
165 b7handle0:
166 mov x2,x1 ;[5] Set x2 to current line state
167 ldi bitcnt, 6 ;[6]
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
173 rjmp bit0 ;[13]
174
175
176 ;----------------------------------------------------------------------------
177 ; Handle unstuff
178 ; x1==0xFF indicate unstuffing bit6
179 ;----------------------------------------------------------------------------
180
181 unstuff6:
182 ldi x1,0xFF ;[12] indicate unstuffing bit 6
183 in bitcnt, USBIN ;[0] sample stuff bit
184 nop ;[1] fix timing
185 unstuff: ;b0-5 b6 b7
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
195 nop ;--- --- [12]
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
199
200 ;----------------------------------------------------------------------------
201 ; Receiver loop (numbers in brackets are cycles within byte after instr)
202 ;----------------------------------------------------------------------------
203 bitloop:
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
207 handleBit:
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"
210 sec ;[5]
211 ror shift ;[6] shift "1" into the data
212 brcs b6checkUnstuff ;[7] When after shift C is set, next bit is bit7
213 nop2 ;[8]
214 dec bitcnt ;[10]
215 brne bitloop ;[11]
216 ldi x1,0 ;[12] indicate unstuff for bit other than bit6 or bit7
217 in bitcnt, USBIN ;[0] sample stuff bit
218 rjmp unstuff ;[1]
219
220 handle0:
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
225 nop ;[10]
226 rjmp bitloop ;[11]
227
228 ;----------------------------------------------------------------------------
229 ; End of receive loop. Now start handling EOP
230 ;----------------------------------------------------------------------------
231
232 macro POP_STANDARD ; 14 cycles
233 pop cnt
234 pop x4
235 pop x3
236 pop x2
237 pop x1
238 pop shift
239 pop bitcnt
240 endm
241 macro POP_RETI ; 7 cycles
242 pop YH
243 pop YL
244 out SREG, YL
245 pop YL
246 endm
247
248
249
250 #include "asmcommon.inc"
251
252 ; USB spec says:
253 ; idle = J
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
259
260 bitstuffN:
261 eor x1, x4 ;[8]
262 ldi x2, 0 ;[9]
263 nop2 ;[10]
264 out USBOUT, x1 ;[12] <-- out
265 rjmp didStuffN ;[0]
266
267 bitstuff7:
268 eor x1, x4 ;[6]
269 ldi x2, 0 ;[7] Carry is zero due to brcc
270 rol shift ;[8] compensate for ror shift at branch destination
271 nop2 ;[9]
272 rjmp didStuff7 ;[11]
273
274 sendNakAndReti:
275 ldi x3, USBPID_NAK ;[-18]
276 rjmp sendX3AndReti ;[-17]
277 sendAckAndReti:
278 ldi cnt, USBPID_ACK ;[-17]
279 sendCntAndReti:
280 mov x3, cnt ;[-16]
281 sendX3AndReti:
282 ldi YL, 20 ;[-15] x3==r20 address is 20
283 ldi YH, 0 ;[-14]
284 ldi cnt, 2 ;[-13]
285 ; rjmp usbSendAndReti fallthrough
286
287 ;usbSend:
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
295 in x2, USBDDR ;[-12]
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
303 txByteLoop:
304 ldi bitcnt, 0x49 ;[-4] [10] binary 01001001
305 txBitLoop:
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]
310 ror x2 ;[1]
311 didStuffN:
312 nop2 ;[2]
313 nop ;[4]
314 cpi x2, 0xfc ;[5]
315 brcc bitstuffN ;[6]
316 lsr bitcnt ;[7]
317 brcc txBitLoop ;[8]
318 brne txBitLoop ;[9]
319
320 sbrs shift, 0 ;[10]
321 eor x1, x4 ;[11]
322 didStuff7:
323 out USBOUT, x1 ;[-1] [13] <-- out 7
324 ror shift ;[0] [14]
325 ror x2 ;[1]
326 nop ;[2]
327 cpi x2, 0xfc ;[3]
328 brcc bitstuff7 ;[4]
329 ld shift, y+ ;[5]
330 dec cnt ;[7]
331 brne txByteLoop ;[8]
332 ;make SE0:
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
338 sbci YH, 0 ;[1]
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
343 skipAddrAssign:
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]
347 ori x1, USBIDLE ;[6]
348 in x2, USBDDR ;[7]
349 cbr x2, USBMASK ;[8] set both pins to input
350 mov x3, x1 ;[9]
351 cbr x3, USBMASK ;[10] configure no pullup on both pins
352 ldi x4, 5 ;[11]
353 se0Delay:
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
359 rjmp doReturn

mercurial