|
1 /* Name: usbdrvasm16.inc |
|
2 * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers |
|
3 * Author: Christian Starkjohann |
|
4 * Creation Date: 2007-06-15 |
|
5 * Tabsize: 4 |
|
6 * Copyright: (c) 2007 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 16 MHz version of the asssembler part of the USB driver. It |
|
17 requires a 16 MHz crystal (not a ceramic resonator and not a calibrated RC |
|
18 oscillator). |
|
19 |
|
20 See usbdrv.h for a description of the entire driver. |
|
21 |
|
22 Since almost all of this code is timing critical, don't change unless you |
|
23 really know what you are doing! Many parts require not only a maximum number |
|
24 of CPU cycles, but even an exact number of cycles! |
|
25 */ |
|
26 |
|
27 ;max stack usage: [ret(2), YL, SREG, YH, bitcnt, shift, x1, x2, x3, x4, cnt] = 12 bytes |
|
28 ;nominal frequency: 16 MHz -> 10.6666666 cycles per bit, 85.333333333 cycles per byte |
|
29 ; Numbers in brackets are clocks counted from center of last sync bit |
|
30 ; when instruction starts |
|
31 |
|
32 USB_INTR_VECTOR: |
|
33 ;order of registers pushed: YL, SREG YH, [sofError], bitcnt, shift, x1, x2, x3, x4, cnt |
|
34 push YL ;[-25] push only what is necessary to sync with edge ASAP |
|
35 in YL, SREG ;[-23] |
|
36 push YL ;[-22] |
|
37 push YH ;[-20] |
|
38 ;---------------------------------------------------------------------------- |
|
39 ; Synchronize with sync pattern: |
|
40 ;---------------------------------------------------------------------------- |
|
41 ;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K] |
|
42 ;sync up with J to K edge during sync pattern -- use fastest possible loops |
|
43 ;The first part waits at most 1 bit long since we must be in sync pattern. |
|
44 ;YL is guarenteed to be < 0x80 because I flag is clear. When we jump to |
|
45 ;waitForJ, ensure that this prerequisite is met. |
|
46 waitForJ: |
|
47 inc YL |
|
48 sbis USBIN, USBMINUS |
|
49 brne waitForJ ; just make sure we have ANY timeout |
|
50 waitForK: |
|
51 ;The following code results in a sampling window of < 1/4 bit which meets the spec. |
|
52 sbis USBIN, USBMINUS ;[-15] |
|
53 rjmp foundK ;[-14] |
|
54 sbis USBIN, USBMINUS |
|
55 rjmp foundK |
|
56 sbis USBIN, USBMINUS |
|
57 rjmp foundK |
|
58 sbis USBIN, USBMINUS |
|
59 rjmp foundK |
|
60 sbis USBIN, USBMINUS |
|
61 rjmp foundK |
|
62 sbis USBIN, USBMINUS |
|
63 rjmp foundK |
|
64 #if USB_COUNT_SOF |
|
65 lds YL, usbSofCount |
|
66 inc YL |
|
67 sts usbSofCount, YL |
|
68 #endif /* USB_COUNT_SOF */ |
|
69 #ifdef USB_SOF_HOOK |
|
70 USB_SOF_HOOK |
|
71 #endif |
|
72 rjmp sofError |
|
73 foundK: ;[-12] |
|
74 ;{3, 5} after falling D- edge, average delay: 4 cycles [we want 5 for center sampling] |
|
75 ;we have 1 bit time for setup purposes, then sample again. Numbers in brackets |
|
76 ;are cycles from center of first sync (double K) bit after the instruction |
|
77 push bitcnt ;[-12] |
|
78 ; [---] ;[-11] |
|
79 lds YL, usbInputBufOffset;[-10] |
|
80 ; [---] ;[-9] |
|
81 clr YH ;[-8] |
|
82 subi YL, lo8(-(usbRxBuf));[-7] [rx loop init] |
|
83 sbci YH, hi8(-(usbRxBuf));[-6] [rx loop init] |
|
84 push shift ;[-5] |
|
85 ; [---] ;[-4] |
|
86 ldi bitcnt, 0x55 ;[-3] [rx loop init] |
|
87 sbis USBIN, USBMINUS ;[-2] we want two bits K (sample 2 cycles too early) |
|
88 rjmp haveTwoBitsK ;[-1] |
|
89 pop shift ;[0] undo the push from before |
|
90 pop bitcnt ;[2] undo the push from before |
|
91 rjmp waitForK ;[4] this was not the end of sync, retry |
|
92 ; The entire loop from waitForK until rjmp waitForK above must not exceed two |
|
93 ; bit times (= 21 cycles). |
|
94 |
|
95 ;---------------------------------------------------------------------------- |
|
96 ; push more registers and initialize values while we sample the first bits: |
|
97 ;---------------------------------------------------------------------------- |
|
98 haveTwoBitsK: |
|
99 push x1 ;[1] |
|
100 push x2 ;[3] |
|
101 push x3 ;[5] |
|
102 ldi shift, 0 ;[7] |
|
103 ldi x3, 1<<4 ;[8] [rx loop init] first sample is inverse bit, compensate that |
|
104 push x4 ;[9] == leap |
|
105 |
|
106 in x1, USBIN ;[11] <-- sample bit 0 |
|
107 andi x1, USBMASK ;[12] |
|
108 bst x1, USBMINUS ;[13] |
|
109 bld shift, 7 ;[14] |
|
110 push cnt ;[15] |
|
111 ldi leap, 0 ;[17] [rx loop init] |
|
112 ldi cnt, USB_BUFSIZE;[18] [rx loop init] |
|
113 rjmp rxbit1 ;[19] arrives at [21] |
|
114 |
|
115 ;---------------------------------------------------------------------------- |
|
116 ; Receiver loop (numbers in brackets are cycles within byte after instr) |
|
117 ;---------------------------------------------------------------------------- |
|
118 |
|
119 ; duration of unstuffing code should be 10.66666667 cycles. We adjust "leap" |
|
120 ; accordingly to approximate this value in the long run. |
|
121 |
|
122 unstuff6: |
|
123 andi x2, USBMASK ;[03] |
|
124 ori x3, 1<<6 ;[04] will not be shifted any more |
|
125 andi shift, ~0x80;[05] |
|
126 mov x1, x2 ;[06] sampled bit 7 is actually re-sampled bit 6 |
|
127 subi leap, -1 ;[07] total duration = 11 bits -> subtract 1/3 |
|
128 rjmp didUnstuff6 ;[08] |
|
129 |
|
130 unstuff7: |
|
131 ori x3, 1<<7 ;[09] will not be shifted any more |
|
132 in x2, USBIN ;[00] [10] re-sample bit 7 |
|
133 andi x2, USBMASK ;[01] |
|
134 andi shift, ~0x80;[02] |
|
135 subi leap, 2 ;[03] total duration = 10 bits -> add 1/3 |
|
136 rjmp didUnstuff7 ;[04] |
|
137 |
|
138 unstuffEven: |
|
139 ori x3, 1<<6 ;[09] will be shifted right 6 times for bit 0 |
|
140 in x1, USBIN ;[00] [10] |
|
141 andi shift, ~0x80;[01] |
|
142 andi x1, USBMASK ;[02] |
|
143 breq se0 ;[03] |
|
144 subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 |
|
145 nop2 ;[05] |
|
146 rjmp didUnstuffE ;[06] |
|
147 |
|
148 unstuffOdd: |
|
149 ori x3, 1<<5 ;[09] will be shifted right 4 times for bit 1 |
|
150 in x2, USBIN ;[00] [10] |
|
151 andi shift, ~0x80;[01] |
|
152 andi x2, USBMASK ;[02] |
|
153 breq se0 ;[03] |
|
154 subi leap, -1 ;[04] total duration = 11 bits -> subtract 1/3 |
|
155 nop2 ;[05] |
|
156 rjmp didUnstuffO ;[06] |
|
157 |
|
158 rxByteLoop: |
|
159 andi x1, USBMASK ;[03] |
|
160 eor x2, x1 ;[04] |
|
161 subi leap, 1 ;[05] |
|
162 brpl skipLeap ;[06] |
|
163 subi leap, -3 ;1 one leap cycle every 3rd byte -> 85 + 1/3 cycles per byte |
|
164 nop ;1 |
|
165 skipLeap: |
|
166 subi x2, 1 ;[08] |
|
167 ror shift ;[09] |
|
168 didUnstuff6: |
|
169 cpi shift, 0xfc ;[10] |
|
170 in x2, USBIN ;[00] [11] <-- sample bit 7 |
|
171 brcc unstuff6 ;[01] |
|
172 andi x2, USBMASK ;[02] |
|
173 eor x1, x2 ;[03] |
|
174 subi x1, 1 ;[04] |
|
175 ror shift ;[05] |
|
176 didUnstuff7: |
|
177 cpi shift, 0xfc ;[06] |
|
178 brcc unstuff7 ;[07] |
|
179 eor x3, shift ;[08] reconstruct: x3 is 1 at bit locations we changed, 0 at others |
|
180 st y+, x3 ;[09] store data |
|
181 rxBitLoop: |
|
182 in x1, USBIN ;[00] [11] <-- sample bit 0/2/4 |
|
183 andi x1, USBMASK ;[01] |
|
184 eor x2, x1 ;[02] |
|
185 andi x3, 0x3f ;[03] topmost two bits reserved for 6 and 7 |
|
186 subi x2, 1 ;[04] |
|
187 ror shift ;[05] |
|
188 cpi shift, 0xfc ;[06] |
|
189 brcc unstuffEven ;[07] |
|
190 didUnstuffE: |
|
191 lsr x3 ;[08] |
|
192 lsr x3 ;[09] |
|
193 rxbit1: |
|
194 in x2, USBIN ;[00] [10] <-- sample bit 1/3/5 |
|
195 andi x2, USBMASK ;[01] |
|
196 breq se0 ;[02] |
|
197 eor x1, x2 ;[03] |
|
198 subi x1, 1 ;[04] |
|
199 ror shift ;[05] |
|
200 cpi shift, 0xfc ;[06] |
|
201 brcc unstuffOdd ;[07] |
|
202 didUnstuffO: |
|
203 subi bitcnt, 0xab;[08] == addi 0x55, 0x55 = 0x100/3 |
|
204 brcs rxBitLoop ;[09] |
|
205 |
|
206 subi cnt, 1 ;[10] |
|
207 in x1, USBIN ;[00] [11] <-- sample bit 6 |
|
208 brcc rxByteLoop ;[01] |
|
209 rjmp overflow |
|
210 |
|
211 macro POP_STANDARD ; 14 cycles |
|
212 pop cnt |
|
213 pop x4 |
|
214 pop x3 |
|
215 pop x2 |
|
216 pop x1 |
|
217 pop shift |
|
218 pop bitcnt |
|
219 endm |
|
220 macro POP_RETI ; 7 cycles |
|
221 pop YH |
|
222 pop YL |
|
223 out SREG, YL |
|
224 pop YL |
|
225 endm |
|
226 |
|
227 #include "asmcommon.inc" |
|
228 |
|
229 ; USB spec says: |
|
230 ; idle = J |
|
231 ; J = (D+ = 0), (D- = 1) |
|
232 ; K = (D+ = 1), (D- = 0) |
|
233 ; Spec allows 7.5 bit times from EOP to SOP for replies |
|
234 |
|
235 bitstuffN: |
|
236 eor x1, x4 ;[5] |
|
237 ldi x2, 0 ;[6] |
|
238 nop2 ;[7] |
|
239 nop ;[9] |
|
240 out USBOUT, x1 ;[10] <-- out |
|
241 rjmp didStuffN ;[0] |
|
242 |
|
243 bitstuff6: |
|
244 eor x1, x4 ;[5] |
|
245 ldi x2, 0 ;[6] Carry is zero due to brcc |
|
246 rol shift ;[7] compensate for ror shift at branch destination |
|
247 rjmp didStuff6 ;[8] |
|
248 |
|
249 bitstuff7: |
|
250 ldi x2, 0 ;[2] Carry is zero due to brcc |
|
251 rjmp didStuff7 ;[3] |
|
252 |
|
253 |
|
254 sendNakAndReti: |
|
255 ldi x3, USBPID_NAK ;[-18] |
|
256 rjmp sendX3AndReti ;[-17] |
|
257 sendAckAndReti: |
|
258 ldi cnt, USBPID_ACK ;[-17] |
|
259 sendCntAndReti: |
|
260 mov x3, cnt ;[-16] |
|
261 sendX3AndReti: |
|
262 ldi YL, 20 ;[-15] x3==r20 address is 20 |
|
263 ldi YH, 0 ;[-14] |
|
264 ldi cnt, 2 ;[-13] |
|
265 ; rjmp usbSendAndReti fallthrough |
|
266 |
|
267 ;usbSend: |
|
268 ;pointer to data in 'Y' |
|
269 ;number of bytes in 'cnt' -- including sync byte [range 2 ... 12] |
|
270 ;uses: x1...x4, btcnt, shift, cnt, Y |
|
271 ;Numbers in brackets are time since first bit of sync pattern is sent |
|
272 ;We don't match the transfer rate exactly (don't insert leap cycles every third |
|
273 ;byte) because the spec demands only 1.5% precision anyway. |
|
274 usbSendAndReti: ; 12 cycles until SOP |
|
275 in x2, USBDDR ;[-12] |
|
276 ori x2, USBMASK ;[-11] |
|
277 sbi USBOUT, USBMINUS;[-10] prepare idle state; D+ and D- must have been 0 (no pullups) |
|
278 in x1, USBOUT ;[-8] port mirror for tx loop |
|
279 out USBDDR, x2 ;[-7] <- acquire bus |
|
280 ; need not init x2 (bitstuff history) because sync starts with 0 |
|
281 ldi x4, USBMASK ;[-6] exor mask |
|
282 ldi shift, 0x80 ;[-5] sync byte is first byte sent |
|
283 txByteLoop: |
|
284 ldi bitcnt, 0x35 ;[-4] [6] binary 0011 0101 |
|
285 txBitLoop: |
|
286 sbrs shift, 0 ;[-3] [7] |
|
287 eor x1, x4 ;[-2] [8] |
|
288 out USBOUT, x1 ;[-1] [9] <-- out N |
|
289 ror shift ;[0] [10] |
|
290 ror x2 ;[1] |
|
291 didStuffN: |
|
292 cpi x2, 0xfc ;[2] |
|
293 brcc bitstuffN ;[3] |
|
294 lsr bitcnt ;[4] |
|
295 brcc txBitLoop ;[5] |
|
296 brne txBitLoop ;[6] |
|
297 |
|
298 sbrs shift, 0 ;[7] |
|
299 eor x1, x4 ;[8] |
|
300 didStuff6: |
|
301 out USBOUT, x1 ;[-1] [9] <-- out 6 |
|
302 ror shift ;[0] [10] |
|
303 ror x2 ;[1] |
|
304 cpi x2, 0xfc ;[2] |
|
305 brcc bitstuff6 ;[3] |
|
306 ror shift ;[4] |
|
307 didStuff7: |
|
308 ror x2 ;[5] |
|
309 sbrs x2, 7 ;[6] |
|
310 eor x1, x4 ;[7] |
|
311 nop ;[8] |
|
312 cpi x2, 0xfc ;[9] |
|
313 out USBOUT, x1 ;[-1][10] <-- out 7 |
|
314 brcc bitstuff7 ;[0] [11] |
|
315 ld shift, y+ ;[1] |
|
316 dec cnt ;[3] |
|
317 brne txByteLoop ;[4] |
|
318 ;make SE0: |
|
319 cbr x1, USBMASK ;[5] prepare SE0 [spec says EOP may be 21 to 25 cycles] |
|
320 lds x2, usbNewDeviceAddr;[6] |
|
321 lsl x2 ;[8] we compare with left shifted address |
|
322 subi YL, 20 + 2 ;[9] Only assign address on data packets, not ACK/NAK in x3 |
|
323 sbci YH, 0 ;[10] |
|
324 out USBOUT, x1 ;[11] <-- out SE0 -- from now 2 bits = 22 cycles until bus idle |
|
325 ;2006-03-06: moved transfer of new address to usbDeviceAddr from C-Code to asm: |
|
326 ;set address only after data packet was sent, not after handshake |
|
327 breq skipAddrAssign ;[0] |
|
328 sts usbDeviceAddr, x2; if not skipped: SE0 is one cycle longer |
|
329 skipAddrAssign: |
|
330 ;end of usbDeviceAddress transfer |
|
331 ldi x2, 1<<USB_INTR_PENDING_BIT;[2] int0 occurred during TX -- clear pending flag |
|
332 USB_STORE_PENDING(x2) ;[3] |
|
333 ori x1, USBIDLE ;[4] |
|
334 in x2, USBDDR ;[5] |
|
335 cbr x2, USBMASK ;[6] set both pins to input |
|
336 mov x3, x1 ;[7] |
|
337 cbr x3, USBMASK ;[8] configure no pullup on both pins |
|
338 ldi x4, 4 ;[9] |
|
339 se0Delay: |
|
340 dec x4 ;[10] [13] [16] [19] |
|
341 brne se0Delay ;[11] [14] [17] [20] |
|
342 out USBOUT, x1 ;[21] <-- out J (idle) -- end of SE0 (EOP signal) |
|
343 out USBDDR, x2 ;[22] <-- release bus now |
|
344 out USBOUT, x3 ;[23] <-- ensure no pull-up resistors are active |
|
345 rjmp doReturn |