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