|
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 |