]>
Commit | Line | Data |
---|---|---|
ec1bef8e | 1 | /* Name: usbdrvasm128.inc |
2 | * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers | |
3 | * Author: Christian Starkjohann | |
4 | * Creation Date: 2008-10-11 | |
5 | * Tabsize: 4 | |
6 | * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH | |
7 | * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) | |
8 | * This Revision: $Id: usbdrvasm128.inc 758 2009-08-06 10:12:54Z cs $ | |
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 12.8 MHz version of the USB driver. It is intended for use | |
18 | with the internal RC oscillator. Although 12.8 MHz is outside the guaranteed | |
19 | calibration range of the oscillator, almost all AVRs can reach this frequency. | |
20 | This version contains a phase locked loop in the receiver routine to cope with | |
21 | slight clock rate deviations of up to +/- 1%. | |
22 | ||
23 | See usbdrv.h for a description of the entire driver. | |
24 | ||
25 | LIMITATIONS | |
26 | =========== | |
27 | Although it may seem very handy to save the crystal and use the internal | |
28 | RC oscillator of the CPU, this method (and this module) has some serious | |
29 | limitations: | |
30 | (1) The guaranteed calibration range of the oscillator is only 8.1 MHz. | |
31 | They typical range is 14.5 MHz and most AVRs can actually reach this rate. | |
32 | (2) Writing EEPROM and Flash may be unreliable (short data lifetime) since | |
33 | the write procedure is timed from the RC oscillator. | |
34 | (3) End Of Packet detection (SE0) should be in bit 1, bit it is only checked | |
35 | if bits 0 and 1 both read as 0 on D- and D+ read as 0 in the middle. This may | |
36 | cause problems with old hubs which delay SE0 by up to one cycle. | |
37 | (4) Code size is much larger than that of the other modules. | |
38 | ||
39 | Since almost all of this code is timing critical, don't change unless you | |
40 | really know what you are doing! Many parts require not only a maximum number | |
41 | of CPU cycles, but even an exact number of cycles! | |
42 | ||
43 | Implementation notes: | |
44 | ====================== | |
45 | min frequency: 67 cycles for 8 bit -> 12.5625 MHz | |
46 | max frequency: 69.286 cycles for 8 bit -> 12.99 MHz | |
47 | nominal frequency: 12.77 MHz ( = sqrt(min * max)) | |
48 | ||
49 | sampling positions: (next even number in range [+/- 0.5]) | |
50 | cycle index range: 0 ... 66 | |
51 | bits: | |
52 | .5, 8.875, 17.25, 25.625, 34, 42.375, 50.75, 59.125 | |
53 | [0/1], [9], [17], [25/+26], [34], [+42/43], [51], [59] | |
54 | ||
55 | bit number: 0 1 2 3 4 5 6 7 | |
56 | spare cycles 1 2 1 2 1 1 1 0 | |
57 | ||
58 | operations to perform: duration cycle | |
59 | ---------------- | |
60 | eor fix, shift 1 -> 00 | |
61 | andi phase, USBMASK 1 -> 08 | |
62 | breq se0 1 -> 16 (moved to 11) | |
63 | st y+, data 2 -> 24, 25 | |
64 | mov data, fix 1 -> 33 | |
65 | ser data 1 -> 41 | |
66 | subi cnt, 1 1 -> 49 | |
67 | brcs overflow 1 -> 50 | |
68 | ||
69 | layout of samples and operations: | |
70 | [##] = sample bit | |
71 | <##> = sample phase | |
72 | *##* = operation | |
73 | ||
74 | 0: *00* [01] 02 03 04 <05> 06 07 | |
75 | 1: *08* [09] 10 11 12 <13> 14 15 *16* | |
76 | 2: [17] 18 19 20 <21> 22 23 | |
77 | 3: *24* *25* [26] 27 28 29 <30> 31 32 | |
78 | 4: *33* [34] 35 36 37 <38> 39 40 | |
79 | 5: *41* [42] 43 44 45 <46> 47 48 | |
80 | 6: *49* *50* [51] 52 53 54 <55> 56 57 58 | |
81 | 7: [59] 60 61 62 <63> 64 65 66 | |
82 | *****************************************************************************/ | |
83 | ||
84 | /* we prefer positive expressions (do if condition) instead of negative | |
85 | * (skip if condition), therefore use defines for skip instructions: | |
86 | */ | |
87 | #define ifioclr sbis | |
88 | #define ifioset sbic | |
89 | #define ifrclr sbrs | |
90 | #define ifrset sbrc | |
91 | ||
92 | /* The registers "fix" and "data" swap their meaning during the loop. Use | |
93 | * defines to keep their name constant. | |
94 | */ | |
95 | #define fix x2 | |
96 | #define data x1 | |
97 | #undef phase /* phase has a default definition to x4 */ | |
98 | #define phase x3 | |
99 | ||
100 | ||
101 | USB_INTR_VECTOR: | |
102 | ;order of registers pushed: YL, SREG [sofError], YH, shift, x1, x2, x3, cnt, r0 | |
103 | push YL ;2 push only what is necessary to sync with edge ASAP | |
104 | in YL, SREG ;1 | |
105 | push YL ;2 | |
106 | ;---------------------------------------------------------------------------- | |
107 | ; Synchronize with sync pattern: | |
108 | ;---------------------------------------------------------------------------- | |
109 | ;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] | |
110 | ;sync up with J to K edge during sync pattern -- use fastest possible loops | |
111 | ;The first part waits at most 1 bit long since we must be in sync pattern. | |
112 | ;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to | |
113 | ;waitForJ, ensure that this prerequisite is met. | |
114 | waitForJ: | |
115 | inc YL | |
116 | sbis USBIN, USBMINUS | |
117 | brne waitForJ ; just make sure we have ANY timeout | |
118 | waitForK: | |
119 | ;The following code results in a sampling window of 1/4 bit which meets the spec. | |
120 | sbis USBIN, USBMINUS | |
121 | rjmp foundK | |
122 | sbis USBIN, USBMINUS | |
123 | rjmp foundK | |
124 | sbis USBIN, USBMINUS | |
125 | rjmp foundK | |
126 | sbis USBIN, USBMINUS | |
127 | rjmp foundK | |
128 | sbis USBIN, USBMINUS ;[0] | |
129 | rjmp foundK ;[1] | |
130 | #if USB_COUNT_SOF | |
131 | lds YL, usbSofCount | |
132 | inc YL | |
133 | sts usbSofCount, YL | |
134 | #endif /* USB_COUNT_SOF */ | |
135 | #ifdef USB_SOF_HOOK | |
136 | USB_SOF_HOOK | |
137 | #endif | |
138 | rjmp sofError | |
139 | ||
140 | foundK: | |
141 | ;{3, 5} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling] | |
142 | ;we have 1 bit time for setup purposes, then sample again. Numbers in brackets | |
143 | ;are cycles from center of first sync (double K) bit after the instruction | |
144 | push YH ;[2] | |
145 | lds YL, usbInputBufOffset;[4] | |
146 | clr YH ;[6] | |
147 | subi YL, lo8(-(usbRxBuf));[7] | |
148 | sbci YH, hi8(-(usbRxBuf));[8] | |
149 | ||
150 | sbis USBIN, USBMINUS ;[9] we want two bits K [we want to sample at 8 + 4 - 1.5 = 10.5] | |
151 | rjmp haveTwoBitsK ;[10] | |
152 | pop YH ;[11] undo the push from before | |
153 | rjmp waitForK ;[13] this was not the end of sync, retry | |
154 | haveTwoBitsK: | |
155 | ;---------------------------------------------------------------------------- | |
156 | ; push more registers and initialize values while we sample the first bits: | |
157 | ;---------------------------------------------------------------------------- | |
158 | #define fix x2 | |
159 | #define data x1 | |
160 | ||
161 | push shift ;[12] | |
162 | push x1 ;[14] | |
163 | push x2 ;[16] | |
164 | ldi shift, 0x80 ;[18] prevent bit-unstuffing but init low bits to 0 | |
165 | ifioset USBIN, USBMINUS ;[19] [01] <--- bit 0 [10.5 + 8 = 18.5] | |
166 | ori shift, 1<<0 ;[02] | |
167 | push x3 ;[03] | |
168 | push cnt ;[05] | |
169 | push r0 ;[07] | |
170 | ifioset USBIN, USBMINUS ;[09] <--- bit 1 | |
171 | ori shift, 1<<1 ;[10] | |
172 | ser fix ;[11] | |
173 | ldi cnt, USB_BUFSIZE ;[12] | |
174 | mov data, shift ;[13] | |
175 | lsl shift ;[14] | |
176 | nop2 ;[15] | |
177 | ifioset USBIN, USBMINUS ;[17] <--- bit 2 | |
178 | ori data, 3<<2 ;[18] store in bit 2 AND bit 3 | |
179 | eor shift, data ;[19] do nrzi decoding | |
180 | andi data, 1<<3 ;[20] | |
181 | in phase, USBIN ;[21] <- phase | |
182 | brne jumpToEntryAfterSet ;[22] if USBMINS at bit 3 was 1 | |
183 | nop ;[23] | |
184 | rjmp entryAfterClr ;[24] | |
185 | jumpToEntryAfterSet: | |
186 | rjmp entryAfterSet ;[24] | |
187 | ||
188 | ;---------------------------------------------------------------------------- | |
189 | ; Receiver loop (numbers in brackets are cycles within byte after instr) | |
190 | ;---------------------------------------------------------------------------- | |
191 | #undef fix | |
192 | #define fix x1 | |
193 | #undef data | |
194 | #define data x2 | |
195 | ||
196 | bit7IsSet: | |
197 | ifrclr phase, USBMINUS ;[62] check phase only if D- changed | |
198 | lpm ;[63] | |
199 | in phase, USBIN ;[64] <- phase (one cycle too late) | |
200 | ori shift, 1 << 7 ;[65] | |
201 | nop ;[66] | |
202 | ;;;;rjmp bit0AfterSet ; -> [00] == [67] moved block up to save jump | |
203 | bit0AfterSet: | |
204 | eor fix, shift ;[00] | |
205 | #undef fix | |
206 | #define fix x2 | |
207 | #undef data | |
208 | #define data x1 /* we now have result in data, fix is reset to 0xff */ | |
209 | ifioclr USBIN, USBMINUS ;[01] <--- sample 0 | |
210 | rjmp bit0IsClr ;[02] | |
211 | andi shift, ~(7 << 0) ;[03] | |
212 | breq unstuff0s ;[04] | |
213 | in phase, USBIN ;[05] <- phase | |
214 | rjmp bit1AfterSet ;[06] | |
215 | unstuff0s: | |
216 | in phase, USBIN ;[06] <- phase (one cycle too late) | |
217 | andi fix, ~(1 << 0) ;[07] | |
218 | ifioclr USBIN, USBMINUS ;[00] | |
219 | ifioset USBIN, USBPLUS ;[01] | |
220 | rjmp bit0IsClr ;[02] executed if first expr false or second true | |
221 | se0AndStore: ; executed only if both bits 0 | |
222 | st y+, x1 ;[15/17] cycles after start of byte | |
223 | rjmp se0 ;[17/19] | |
224 | ||
225 | bit0IsClr: | |
226 | ifrset phase, USBMINUS ;[04] check phase only if D- changed | |
227 | lpm ;[05] | |
228 | in phase, USBIN ;[06] <- phase (one cycle too late) | |
229 | ori shift, 1 << 0 ;[07] | |
230 | bit1AfterClr: | |
231 | andi phase, USBMASK ;[08] | |
232 | ifioset USBIN, USBMINUS ;[09] <--- sample 1 | |
233 | rjmp bit1IsSet ;[10] | |
234 | breq se0AndStore ;[11] if D- was 0 in bits 0 AND 1 and D+ was 0 in between, we have SE0 | |
235 | andi shift, ~(7 << 1) ;[12] | |
236 | in phase, USBIN ;[13] <- phase | |
237 | breq unstuff1c ;[14] | |
238 | rjmp bit2AfterClr ;[15] | |
239 | unstuff1c: | |
240 | andi fix, ~(1 << 1) ;[16] | |
241 | nop2 ;[08] | |
242 | nop2 ;[10] | |
243 | bit1IsSet: | |
244 | ifrclr phase, USBMINUS ;[12] check phase only if D- changed | |
245 | lpm ;[13] | |
246 | in phase, USBIN ;[14] <- phase (one cycle too late) | |
247 | ori shift, 1 << 1 ;[15] | |
248 | nop ;[16] | |
249 | bit2AfterSet: | |
250 | ifioclr USBIN, USBMINUS ;[17] <--- sample 2 | |
251 | rjmp bit2IsClr ;[18] | |
252 | andi shift, ~(7 << 2) ;[19] | |
253 | breq unstuff2s ;[20] | |
254 | in phase, USBIN ;[21] <- phase | |
255 | rjmp bit3AfterSet ;[22] | |
256 | unstuff2s: | |
257 | in phase, USBIN ;[22] <- phase (one cycle too late) | |
258 | andi fix, ~(1 << 2) ;[23] | |
259 | nop2 ;[16] | |
260 | nop2 ;[18] | |
261 | bit2IsClr: | |
262 | ifrset phase, USBMINUS ;[20] check phase only if D- changed | |
263 | lpm ;[21] | |
264 | in phase, USBIN ;[22] <- phase (one cycle too late) | |
265 | ori shift, 1 << 2 ;[23] | |
266 | bit3AfterClr: | |
267 | st y+, data ;[24] | |
268 | entryAfterClr: | |
269 | ifioset USBIN, USBMINUS ;[26] <--- sample 3 | |
270 | rjmp bit3IsSet ;[27] | |
271 | andi shift, ~(7 << 3) ;[28] | |
272 | breq unstuff3c ;[29] | |
273 | in phase, USBIN ;[30] <- phase | |
274 | rjmp bit4AfterClr ;[31] | |
275 | unstuff3c: | |
276 | in phase, USBIN ;[31] <- phase (one cycle too late) | |
277 | andi fix, ~(1 << 3) ;[32] | |
278 | nop2 ;[25] | |
279 | nop2 ;[27] | |
280 | bit3IsSet: | |
281 | ifrclr phase, USBMINUS ;[29] check phase only if D- changed | |
282 | lpm ;[30] | |
283 | in phase, USBIN ;[31] <- phase (one cycle too late) | |
284 | ori shift, 1 << 3 ;[32] | |
285 | bit4AfterSet: | |
286 | mov data, fix ;[33] undo this move by swapping defines | |
287 | #undef fix | |
288 | #define fix x1 | |
289 | #undef data | |
290 | #define data x2 | |
291 | ifioclr USBIN, USBMINUS ;[34] <--- sample 4 | |
292 | rjmp bit4IsClr ;[35] | |
293 | andi shift, ~(7 << 4) ;[36] | |
294 | breq unstuff4s ;[37] | |
295 | in phase, USBIN ;[38] <- phase | |
296 | rjmp bit5AfterSet ;[39] | |
297 | unstuff4s: | |
298 | in phase, USBIN ;[39] <- phase (one cycle too late) | |
299 | andi fix, ~(1 << 4) ;[40] | |
300 | nop2 ;[33] | |
301 | nop2 ;[35] | |
302 | bit4IsClr: | |
303 | ifrset phase, USBMINUS ;[37] check phase only if D- changed | |
304 | lpm ;[38] | |
305 | in phase, USBIN ;[39] <- phase (one cycle too late) | |
306 | ori shift, 1 << 4 ;[40] | |
307 | bit5AfterClr: | |
308 | ser data ;[41] | |
309 | ifioset USBIN, USBMINUS ;[42] <--- sample 5 | |
310 | rjmp bit5IsSet ;[43] | |
311 | andi shift, ~(7 << 5) ;[44] | |
312 | breq unstuff5c ;[45] | |
313 | in phase, USBIN ;[46] <- phase | |
314 | rjmp bit6AfterClr ;[47] | |
315 | unstuff5c: | |
316 | in phase, USBIN ;[47] <- phase (one cycle too late) | |
317 | andi fix, ~(1 << 5) ;[48] | |
318 | nop2 ;[41] | |
319 | nop2 ;[43] | |
320 | bit5IsSet: | |
321 | ifrclr phase, USBMINUS ;[45] check phase only if D- changed | |
322 | lpm ;[46] | |
323 | in phase, USBIN ;[47] <- phase (one cycle too late) | |
324 | ori shift, 1 << 5 ;[48] | |
325 | bit6AfterSet: | |
326 | subi cnt, 1 ;[49] | |
327 | brcs jumpToOverflow ;[50] | |
328 | ifioclr USBIN, USBMINUS ;[51] <--- sample 6 | |
329 | rjmp bit6IsClr ;[52] | |
330 | andi shift, ~(3 << 6) ;[53] | |
331 | cpi shift, 2 ;[54] | |
332 | in phase, USBIN ;[55] <- phase | |
333 | brlt unstuff6s ;[56] | |
334 | rjmp bit7AfterSet ;[57] | |
335 | ||
336 | jumpToOverflow: | |
337 | rjmp overflow | |
338 | ||
339 | unstuff6s: | |
340 | andi fix, ~(1 << 6) ;[50] | |
341 | lpm ;[51] | |
342 | bit6IsClr: | |
343 | ifrset phase, USBMINUS ;[54] check phase only if D- changed | |
344 | lpm ;[55] | |
345 | in phase, USBIN ;[56] <- phase (one cycle too late) | |
346 | ori shift, 1 << 6 ;[57] | |
347 | nop ;[58] | |
348 | bit7AfterClr: | |
349 | ifioset USBIN, USBMINUS ;[59] <--- sample 7 | |
350 | rjmp bit7IsSet ;[60] | |
351 | andi shift, ~(1 << 7) ;[61] | |
352 | cpi shift, 4 ;[62] | |
353 | in phase, USBIN ;[63] <- phase | |
354 | brlt unstuff7c ;[64] | |
355 | rjmp bit0AfterClr ;[65] -> [00] == [67] | |
356 | unstuff7c: | |
357 | andi fix, ~(1 << 7) ;[58] | |
358 | nop ;[59] | |
359 | rjmp bit7IsSet ;[60] | |
360 | ||
361 | bit7IsClr: | |
362 | ifrset phase, USBMINUS ;[62] check phase only if D- changed | |
363 | lpm ;[63] | |
364 | in phase, USBIN ;[64] <- phase (one cycle too late) | |
365 | ori shift, 1 << 7 ;[65] | |
366 | nop ;[66] | |
367 | ;;;;rjmp bit0AfterClr ; -> [00] == [67] moved block up to save jump | |
368 | bit0AfterClr: | |
369 | eor fix, shift ;[00] | |
370 | #undef fix | |
371 | #define fix x2 | |
372 | #undef data | |
373 | #define data x1 /* we now have result in data, fix is reset to 0xff */ | |
374 | ifioset USBIN, USBMINUS ;[01] <--- sample 0 | |
375 | rjmp bit0IsSet ;[02] | |
376 | andi shift, ~(7 << 0) ;[03] | |
377 | breq unstuff0c ;[04] | |
378 | in phase, USBIN ;[05] <- phase | |
379 | rjmp bit1AfterClr ;[06] | |
380 | unstuff0c: | |
381 | in phase, USBIN ;[06] <- phase (one cycle too late) | |
382 | andi fix, ~(1 << 0) ;[07] | |
383 | ifioclr USBIN, USBMINUS ;[00] | |
384 | ifioset USBIN, USBPLUS ;[01] | |
385 | rjmp bit0IsSet ;[02] executed if first expr false or second true | |
386 | rjmp se0AndStore ;[03] executed only if both bits 0 | |
387 | bit0IsSet: | |
388 | ifrclr phase, USBMINUS ;[04] check phase only if D- changed | |
389 | lpm ;[05] | |
390 | in phase, USBIN ;[06] <- phase (one cycle too late) | |
391 | ori shift, 1 << 0 ;[07] | |
392 | bit1AfterSet: | |
393 | andi shift, ~(7 << 1) ;[08] compensated by "ori shift, 1<<1" if bit1IsClr | |
394 | ifioclr USBIN, USBMINUS ;[09] <--- sample 1 | |
395 | rjmp bit1IsClr ;[10] | |
396 | breq unstuff1s ;[11] | |
397 | nop2 ;[12] do not check for SE0 if bit 0 was 1 | |
398 | in phase, USBIN ;[14] <- phase (one cycle too late) | |
399 | rjmp bit2AfterSet ;[15] | |
400 | unstuff1s: | |
401 | in phase, USBIN ;[13] <- phase | |
402 | andi fix, ~(1 << 1) ;[14] | |
403 | lpm ;[07] | |
404 | nop2 ;[10] | |
405 | bit1IsClr: | |
406 | ifrset phase, USBMINUS ;[12] check phase only if D- changed | |
407 | lpm ;[13] | |
408 | in phase, USBIN ;[14] <- phase (one cycle too late) | |
409 | ori shift, 1 << 1 ;[15] | |
410 | nop ;[16] | |
411 | bit2AfterClr: | |
412 | ifioset USBIN, USBMINUS ;[17] <--- sample 2 | |
413 | rjmp bit2IsSet ;[18] | |
414 | andi shift, ~(7 << 2) ;[19] | |
415 | breq unstuff2c ;[20] | |
416 | in phase, USBIN ;[21] <- phase | |
417 | rjmp bit3AfterClr ;[22] | |
418 | unstuff2c: | |
419 | in phase, USBIN ;[22] <- phase (one cycle too late) | |
420 | andi fix, ~(1 << 2) ;[23] | |
421 | nop2 ;[16] | |
422 | nop2 ;[18] | |
423 | bit2IsSet: | |
424 | ifrclr phase, USBMINUS ;[20] check phase only if D- changed | |
425 | lpm ;[21] | |
426 | in phase, USBIN ;[22] <- phase (one cycle too late) | |
427 | ori shift, 1 << 2 ;[23] | |
428 | bit3AfterSet: | |
429 | st y+, data ;[24] | |
430 | entryAfterSet: | |
431 | ifioclr USBIN, USBMINUS ;[26] <--- sample 3 | |
432 | rjmp bit3IsClr ;[27] | |
433 | andi shift, ~(7 << 3) ;[28] | |
434 | breq unstuff3s ;[29] | |
435 | in phase, USBIN ;[30] <- phase | |
436 | rjmp bit4AfterSet ;[31] | |
437 | unstuff3s: | |
438 | in phase, USBIN ;[31] <- phase (one cycle too late) | |
439 | andi fix, ~(1 << 3) ;[32] | |
440 | nop2 ;[25] | |
441 | nop2 ;[27] | |
442 | bit3IsClr: | |
443 | ifrset phase, USBMINUS ;[29] check phase only if D- changed | |
444 | lpm ;[30] | |
445 | in phase, USBIN ;[31] <- phase (one cycle too late) | |
446 | ori shift, 1 << 3 ;[32] | |
447 | bit4AfterClr: | |
448 | mov data, fix ;[33] undo this move by swapping defines | |
449 | #undef fix | |
450 | #define fix x1 | |
451 | #undef data | |
452 | #define data x2 | |
453 | ifioset USBIN, USBMINUS ;[34] <--- sample 4 | |
454 | rjmp bit4IsSet ;[35] | |
455 | andi shift, ~(7 << 4) ;[36] | |
456 | breq unstuff4c ;[37] | |
457 | in phase, USBIN ;[38] <- phase | |
458 | rjmp bit5AfterClr ;[39] | |
459 | unstuff4c: | |
460 | in phase, USBIN ;[39] <- phase (one cycle too late) | |
461 | andi fix, ~(1 << 4) ;[40] | |
462 | nop2 ;[33] | |
463 | nop2 ;[35] | |
464 | bit4IsSet: | |
465 | ifrclr phase, USBMINUS ;[37] check phase only if D- changed | |
466 | lpm ;[38] | |
467 | in phase, USBIN ;[39] <- phase (one cycle too late) | |
468 | ori shift, 1 << 4 ;[40] | |
469 | bit5AfterSet: | |
470 | ser data ;[41] | |
471 | ifioclr USBIN, USBMINUS ;[42] <--- sample 5 | |
472 | rjmp bit5IsClr ;[43] | |
473 | andi shift, ~(7 << 5) ;[44] | |
474 | breq unstuff5s ;[45] | |
475 | in phase, USBIN ;[46] <- phase | |
476 | rjmp bit6AfterSet ;[47] | |
477 | unstuff5s: | |
478 | in phase, USBIN ;[47] <- phase (one cycle too late) | |
479 | andi fix, ~(1 << 5) ;[48] | |
480 | nop2 ;[41] | |
481 | nop2 ;[43] | |
482 | bit5IsClr: | |
483 | ifrset phase, USBMINUS ;[45] check phase only if D- changed | |
484 | lpm ;[46] | |
485 | in phase, USBIN ;[47] <- phase (one cycle too late) | |
486 | ori shift, 1 << 5 ;[48] | |
487 | bit6AfterClr: | |
488 | subi cnt, 1 ;[49] | |
489 | brcs overflow ;[50] | |
490 | ifioset USBIN, USBMINUS ;[51] <--- sample 6 | |
491 | rjmp bit6IsSet ;[52] | |
492 | andi shift, ~(3 << 6) ;[53] | |
493 | cpi shift, 2 ;[54] | |
494 | in phase, USBIN ;[55] <- phase | |
495 | brlt unstuff6c ;[56] | |
496 | rjmp bit7AfterClr ;[57] | |
497 | unstuff6c: | |
498 | andi fix, ~(1 << 6) ;[50] | |
499 | lpm ;[51] | |
500 | bit6IsSet: | |
501 | ifrclr phase, USBMINUS ;[54] check phase only if D- changed | |
502 | lpm ;[55] | |
503 | in phase, USBIN ;[56] <- phase (one cycle too late) | |
504 | ori shift, 1 << 6 ;[57] | |
505 | bit7AfterSet: | |
506 | ifioclr USBIN, USBMINUS ;[59] <--- sample 7 | |
507 | rjmp bit7IsClr ;[60] | |
508 | andi shift, ~(1 << 7) ;[61] | |
509 | cpi shift, 4 ;[62] | |
510 | in phase, USBIN ;[63] <- phase | |
511 | brlt unstuff7s ;[64] | |
512 | rjmp bit0AfterSet ;[65] -> [00] == [67] | |
513 | unstuff7s: | |
514 | andi fix, ~(1 << 7) ;[58] | |
515 | nop ;[59] | |
516 | rjmp bit7IsClr ;[60] | |
517 | ||
518 | macro POP_STANDARD ; 14 cycles | |
519 | pop r0 | |
520 | pop cnt | |
521 | pop x3 | |
522 | pop x2 | |
523 | pop x1 | |
524 | pop shift | |
525 | pop YH | |
526 | endm | |
527 | macro POP_RETI ; 5 cycles | |
528 | pop YL | |
529 | out SREG, YL | |
530 | pop YL | |
531 | endm | |
532 | ||
533 | #include "asmcommon.inc" | |
534 | ||
535 | ;---------------------------------------------------------------------------- | |
536 | ; Transmitting data | |
537 | ;---------------------------------------------------------------------------- | |
538 | ||
539 | txByteLoop: | |
540 | txBitloop: | |
541 | stuffN1Delay: ; [03] | |
542 | ror shift ;[-5] [11] [63] | |
543 | brcc doExorN1 ;[-4] [64] | |
544 | subi x3, 1 ;[-3] | |
545 | brne commonN1 ;[-2] | |
546 | lsl shift ;[-1] compensate ror after rjmp stuffDelay | |
547 | nop ;[00] stuffing consists of just waiting 8 cycles | |
548 | rjmp stuffN1Delay ;[01] after ror, C bit is reliably clear | |
549 | ||
550 | sendNakAndReti: | |
551 | ldi cnt, USBPID_NAK ;[-19] | |
552 | rjmp sendCntAndReti ;[-18] | |
553 | sendAckAndReti: | |
554 | ldi cnt, USBPID_ACK ;[-17] | |
555 | sendCntAndReti: | |
556 | mov r0, cnt ;[-16] | |
557 | ldi YL, 0 ;[-15] R0 address is 0 | |
558 | ldi YH, 0 ;[-14] | |
559 | ldi cnt, 2 ;[-13] | |
560 | ; rjmp usbSendAndReti fallthrough | |
561 | ||
562 | ; USB spec says: | |
563 | ; idle = J | |
564 | ; J = (D+ = 0), (D- = 1) or USBOUT = 0x01 | |
565 | ; K = (D+ = 1), (D- = 0) or USBOUT = 0x02 | |
566 | ; Spec allows 7.5 bit times from EOP to SOP for replies (= 60 cycles) | |
567 | ||
568 | ;usbSend: | |
569 | ;pointer to data in 'Y' | |
570 | ;number of bytes in 'cnt' -- including sync byte | |
571 | ;uses: x1...x3, shift, cnt, Y [x1 = mirror USBOUT, x2 = USBMASK, x3 = bitstuff cnt] | |
572 | ;Numbers in brackets are time since first bit of sync pattern is sent (start of instruction) | |
573 | usbSendAndReti: | |
574 | in x2, USBDDR ;[-10] 10 cycles until SOP | |
575 | ori x2, USBMASK ;[-9] | |
576 | sbi USBOUT, USBMINUS ;[-8] prepare idle state; D+ and D- must have been 0 (no pullups) | |
577 | out USBDDR, x2 ;[-6] <--- acquire bus | |
578 | in x1, USBOUT ;[-5] port mirror for tx loop | |
579 | ldi shift, 0x40 ;[-4] sync byte is first byte sent (we enter loop after ror) | |
580 | ldi x2, USBMASK ;[-3] | |
581 | doExorN1: | |
582 | eor x1, x2 ;[-2] [06] [62] | |
583 | ldi x3, 6 ;[-1] [07] [63] | |
584 | commonN1: | |
585 | stuffN2Delay: | |
586 | out USBOUT, x1 ;[00] [08] [64] <--- set bit | |
587 | ror shift ;[01] | |
588 | brcc doExorN2 ;[02] | |
589 | subi x3, 1 ;[03] | |
590 | brne commonN2 ;[04] | |
591 | lsl shift ;[05] compensate ror after rjmp stuffDelay | |
592 | rjmp stuffN2Delay ;[06] after ror, C bit is reliably clear | |
593 | doExorN2: | |
594 | eor x1, x2 ;[04] [12] | |
595 | ldi x3, 6 ;[05] [13] | |
596 | commonN2: | |
597 | nop2 ;[06] [14] | |
598 | subi cnt, 171 ;[08] [16] trick: (3 * 171) & 0xff = 1 | |
599 | out USBOUT, x1 ;[09] [17] <--- set bit | |
600 | brcs txBitloop ;[10] [27] [44] | |
601 | ||
602 | stuff6Delay: | |
603 | ror shift ;[45] [53] | |
604 | brcc doExor6 ;[46] | |
605 | subi x3, 1 ;[47] | |
606 | brne common6 ;[48] | |
607 | lsl shift ;[49] compensate ror after rjmp stuffDelay | |
608 | nop ;[50] stuffing consists of just waiting 8 cycles | |
609 | rjmp stuff6Delay ;[51] after ror, C bit is reliably clear | |
610 | doExor6: | |
611 | eor x1, x2 ;[48] [56] | |
612 | ldi x3, 6 ;[49] | |
613 | common6: | |
614 | stuff7Delay: | |
615 | ror shift ;[50] [58] | |
616 | out USBOUT, x1 ;[51] <--- set bit | |
617 | brcc doExor7 ;[52] | |
618 | subi x3, 1 ;[53] | |
619 | brne common7 ;[54] | |
620 | lsl shift ;[55] compensate ror after rjmp stuffDelay | |
621 | rjmp stuff7Delay ;[56] after ror, C bit is reliably clear | |
622 | doExor7: | |
623 | eor x1, x2 ;[54] [62] | |
624 | ldi x3, 6 ;[55] | |
625 | common7: | |
626 | ld shift, y+ ;[56] | |
627 | nop ;[58] | |
628 | tst cnt ;[59] | |
629 | out USBOUT, x1 ;[60] [00]<--- set bit | |
630 | brne txByteLoop ;[61] [01] | |
631 | ;make SE0: | |
632 | cbr x1, USBMASK ;[02] prepare SE0 [spec says EOP may be 15 to 18 cycles] | |
633 | lds x2, usbNewDeviceAddr;[03] | |
634 | lsl x2 ;[05] we compare with left shifted address | |
635 | subi YL, 2 + 0 ;[06] Only assign address on data packets, not ACK/NAK in r0 | |
636 | sbci YH, 0 ;[07] | |
637 | out USBOUT, x1 ;[00] <-- out SE0 -- from now 2 bits = 16 cycles until bus idle | |
638 | ;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: | |
639 | ;set address only after data packet was sent, not after handshake | |
640 | breq skipAddrAssign ;[01] | |
641 | sts usbDeviceAddr, x2 ; if not skipped: SE0 is one cycle longer | |
642 | skipAddrAssign: | |
643 | ;end of usbDeviceAddress transfer | |
644 | ldi x2, 1<<USB_INTR_PENDING_BIT;[03] int0 occurred during TX -- clear pending flag | |
645 | USB_STORE_PENDING(x2) ;[04] | |
646 | ori x1, USBIDLE ;[05] | |
647 | in x2, USBDDR ;[06] | |
648 | cbr x2, USBMASK ;[07] set both pins to input | |
649 | mov x3, x1 ;[08] | |
650 | cbr x3, USBMASK ;[09] configure no pullup on both pins | |
651 | lpm ;[10] | |
652 | lpm ;[13] | |
653 | out USBOUT, x1 ;[16] <-- out J (idle) -- end of SE0 (EOP signal) | |
654 | out USBDDR, x2 ;[17] <-- release bus now | |
655 | out USBOUT, x3 ;[18] <-- ensure no pull-up resistors are active | |
656 | rjmp doReturn | |
657 | ||
658 | ||
659 | ||
660 | /***************************************************************************** | |
661 | The following PHP script generates a code skeleton for the receiver routine: | |
662 | ||
663 | <?php | |
664 | ||
665 | function printCmdBuffer($thisBit) | |
666 | { | |
667 | global $cycle; | |
668 | ||
669 | $nextBit = ($thisBit + 1) % 8; | |
670 | $s = ob_get_contents(); | |
671 | ob_end_clean(); | |
672 | $s = str_replace("#", $thisBit, $s); | |
673 | $s = str_replace("@", $nextBit, $s); | |
674 | $lines = explode("\n", $s); | |
675 | for($i = 0; $i < count($lines); $i++){ | |
676 | $s = $lines[$i]; | |
677 | if(ereg("\\[([0-9-][0-9])\\]", $s, $regs)){ | |
678 | $c = $cycle + (int)$regs[1]; | |
679 | $s = ereg_replace("\\[[0-9-][0-9]\\]", sprintf("[%02d]", $c), $s); | |
680 | } | |
681 | if(strlen($s) > 0) | |
682 | echo "$s\n"; | |
683 | } | |
684 | } | |
685 | ||
686 | function printBit($isAfterSet, $bitNum) | |
687 | { | |
688 | ob_start(); | |
689 | if($isAfterSet){ | |
690 | ?> | |
691 | ifioclr USBIN, USBMINUS ;[00] <--- sample | |
692 | rjmp bit#IsClr ;[01] | |
693 | andi shift, ~(7 << #) ;[02] | |
694 | breq unstuff#s ;[03] | |
695 | in phase, USBIN ;[04] <- phase | |
696 | rjmp bit@AfterSet ;[05] | |
697 | unstuff#s: | |
698 | in phase, USBIN ;[05] <- phase (one cycle too late) | |
699 | andi fix, ~(1 << #) ;[06] | |
700 | nop2 ;[-1] | |
701 | nop2 ;[01] | |
702 | bit#IsClr: | |
703 | ifrset phase, USBMINUS ;[03] check phase only if D- changed | |
704 | lpm ;[04] | |
705 | in phase, USBIN ;[05] <- phase (one cycle too late) | |
706 | ori shift, 1 << # ;[06] | |
707 | <?php | |
708 | }else{ | |
709 | ?> | |
710 | ifioset USBIN, USBMINUS ;[00] <--- sample | |
711 | rjmp bit#IsSet ;[01] | |
712 | andi shift, ~(7 << #) ;[02] | |
713 | breq unstuff#c ;[03] | |
714 | in phase, USBIN ;[04] <- phase | |
715 | rjmp bit@AfterClr ;[05] | |
716 | unstuff#c: | |
717 | in phase, USBIN ;[05] <- phase (one cycle too late) | |
718 | andi fix, ~(1 << #) ;[06] | |
719 | nop2 ;[-1] | |
720 | nop2 ;[01] | |
721 | bit#IsSet: | |
722 | ifrclr phase, USBMINUS ;[03] check phase only if D- changed | |
723 | lpm ;[04] | |
724 | in phase, USBIN ;[05] <- phase (one cycle too late) | |
725 | ori shift, 1 << # ;[06] | |
726 | <?php | |
727 | } | |
728 | printCmdBuffer($bitNum); | |
729 | } | |
730 | ||
731 | $bitStartCycles = array(1, 9, 17, 26, 34, 42, 51, 59); | |
732 | for($i = 0; $i < 16; $i++){ | |
733 | $bit = $i % 8; | |
734 | $emitClrCode = ($i + (int)($i / 8)) % 2; | |
735 | $cycle = $bitStartCycles[$bit]; | |
736 | if($emitClrCode){ | |
737 | printf("bit%dAfterClr:\n", $bit); | |
738 | }else{ | |
739 | printf("bit%dAfterSet:\n", $bit); | |
740 | } | |
741 | ob_start(); | |
742 | echo " ***** ;[-1]\n"; | |
743 | printCmdBuffer($bit); | |
744 | printBit(!$emitClrCode, $bit); | |
745 | if($i == 7) | |
746 | echo "\n"; | |
747 | } | |
748 | ||
749 | ?> | |
750 | *****************************************************************************/ |