]> git.cryptolib.org Git - labortage2013badge.git/blob - firmware/usbdrv/usbdrvasm20.inc
whitespace editing / typo correction
[labortage2013badge.git] / firmware / usbdrv / usbdrvasm20.inc
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