Subversion Repositories group.electronics

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
122 pfowler 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