⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usbdrvasm.s

📁 采用atmel公司的mega8芯片设计的USB cdc类
💻 S
📖 第 1 页 / 共 2 页
字号:
/* Name: usbdrvasm.S
 * Project: AVR USB driver
 * Author: Christian Starkjohann
 * Creation Date: 2004-12-29
 * Tabsize: 4
 * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH
 * License: Proprietary, free under certain conditions. See Documentation.
 * This Revision: $Id: usbdrvasm.S 205 2006-06-23 19:01:45Z cs $
 */

/*
General Description:
This module implements the assembler part of the USB driver. See usbdrv.h
for a description of the entire driver.
Since almost all of this code is timing critical, don't change unless you
really know what you are doing! Many parts require not only a maximum number
of CPU cycles, but even an exact number of cycles!


Timing constraints according to spec (in bit times):
timing subject                                      min max    CPUcycles
---------------------------------------------------------------------------
EOP of OUT/SETUP to sync pattern of DATA0 (both rx) 2   16     16-128
EOP of IN to sync pattern of DATA0 (rx, then tx)    2   7.5    16-60
DATAx (rx) to ACK/NAK/STALL (tx)                    2   7.5    16-60
*/

#include "iarcompat.h"
#ifndef __IAR_SYSTEMS_ASM__
    /* configs for io.h */
#   define __SFR_OFFSET 0
#   define _VECTOR(N)   __vector_ ## N   /* io.h does not define this for asm */
#   include <avr/io.h> /* for CPU I/O register definitions and vectors */
#endif  /* __IAR_SYSTEMS_ASM__ */
#include "usbdrv.h" /* for common defs */


/* register names */
#define x1      r16
#define x2      r17
#define shift   r18
#define cnt     r19
#define x3      r20
#define x4      r21

/* Some assembler dependent definitions and declarations: */

#if (USB_INTR_ENABLE_BIT == INT0)
#define SIG_INTERRUPT	SIG_INTERRUPT0
#define INT_vect	INT0_vect
#elif (defined(INT1) && USB_INTR_ENABLE_BIT == INT1)
#define SIG_INTERRUPT	SIG_INTERRUPT1
#define INT_vect	INT1_vect
#else
#warning "the INTERRUPT is not supported!"
#endif

#ifdef __IAR_SYSTEMS_ASM__

#   define nop2     rjmp    $+2 /* jump to next instruction */
#   define XL       r26
#   define XH       r27
#   define YL       r28
#   define YH       r29
#   define ZL       r30
#   define ZH       r31
#   define lo8(x)   LOW(x)
#   define hi8(x)   ((x)>>8)    /* not HIGH to allow XLINK to make a proper range check */

    extern  usbRxBuf, usbDeviceAddr, usbNewDeviceAddr, usbInputBuf
    extern  usbCurrentTok, usbRxLen, usbRxToken, usbAppBuf, usbTxLen
    extern  usbTxBuf, usbMsgLen, usbNakBuf, usbAckBuf, usbTxLen1, usbTxBuf1
    public  usbCrc16
    public  usbCrc16Append

    COMMON  INTVEC
    ORG     INT_vect
    rjmp    SIG_INTERRUPT
    RSEG    CODE

#else /* __IAR_SYSTEMS_ASM__ */

#   define nop2     rjmp    .+0 /* jump to next instruction */

    .text
    .global SIG_INTERRUPT
    .type   SIG_INTERRUPT, @function
    .global usbCrc16
    .global usbCrc16Append

#endif /* __IAR_SYSTEMS_ASM__ */


SIG_INTERRUPT:
;Software-receiver engine. Strict timing! Don't change unless you can preserve timing!
;interrupt response time: 4 cycles + insn running = 7 max if interrupts always enabled
;max allowable interrupt latency: 32 cycles -> max 25 cycles interrupt disable
;max stack usage: [ret(2), x1, SREG, x2, cnt, shift, YH, YL, x3, x4] = 11 bytes
usbInterrupt:
;order of registers pushed:
;x1, SREG, x2, cnt, shift, [YH, YL, x3]
    push    x1              ;2  push only what is necessary to sync with edge ASAP
    in      x1, SREG        ;1
    push    x1              ;2
;sync byte (D-) pattern LSb to MSb: 01010100 [1 = idle = J, 0 = K]
;sync up with J to K edge during sync pattern -- use fastest possible loops
;first part has no timeout because it waits for IDLE or SE1 (== disconnected)
#if !USB_CFG_SAMPLE_EXACT
    ldi     x1, 5           ;1 setup a timeout for waitForK
#endif
waitForJ:
    sbis    USBIN, USBMINUS ;1 wait for D- == 1
    rjmp    waitForJ        ;2
#if USB_CFG_SAMPLE_EXACT
;The following code represents the unrolled loop in the else branch. It
;results in a sampling window of 1/4 bit which meets the spec.
    sbis    USBIN, USBMINUS
    rjmp    foundK
    sbis    USBIN, USBMINUS
    rjmp    foundK
    sbis    USBIN, USBMINUS
    rjmp    foundK
    nop
    nop2
foundK:
#else
waitForK:
    dec     x1              ;1
    sbic    USBIN, USBMINUS ;1 wait for D- == 0
    brne    waitForK        ;2
#endif
;{2, 6} after falling D- edge, average delay: 4 cycles [we want 4 for center sampling]
;we have 1 bit time for setup purposes, then sample again:
    push    x2              ;2
    push    cnt             ;2
    push    shift           ;2
shortcutEntry:
    ldi     cnt, 1          ;1 pre-init bit counter (-1 because no dec follows, -1 because 1 bit already sampled)
    ldi     x2, 1<<USB_CFG_DPLUS_BIT    ;1 -> 8   edge sync ended with D- == 0
;now wait until SYNC byte is over. Wait for either 2 bits low (success) or 2 bits high (failure)
waitNoChange:
    in      x1, USBIN       ;1 <-- sample, timing: edge + {2, 6} cycles
    eor     x2, x1          ;1
    sbrc    x2, USBMINUS    ;1 | 2
    ldi     cnt, 2          ;1 | 0 cnt = numBits - 1 (because dec follows)
    mov     x2, x1          ;1
    dec     cnt             ;1
    brne    waitNoChange    ;2 | 1
    sbrc    x1, USBMINUS    ;2
    rjmp    sofError        ;0 two consecutive "1" bits -> framing error
;start reading data, but don't check for bitstuffing because these are the
;first bits. Use the cycles for initialization instead. Note that we read and
;store the binary complement of the data stream because eor results in 1 for
;a change and 0 for no change.
    in      x1, USBIN       ;1 <-- sample bit 0, timing: edge + {3, 7} cycles
    eor     x2, x1          ;1
    ldi     shift, 0x00     ;1 prepare for bitstuff check later on in loop
    bst     x2, USBMINUS    ;1
    bld     shift, 0        ;1
    push    YH              ;2 -> 7
    in      x2, USBIN       ;1 <-- sample bit 1, timing: edge + {2, 6} cycles
    eor     x1, x2          ;1
    bst     x1, USBMINUS    ;1
    bld     shift, 1        ;1
    push    YL              ;2
    lds     YL, usbInputBuf ;2 -> 8
    in      x1, USBIN       ;1 <-- sample bit 2, timing: edge + {2, 6} cycles
    eor     x2, x1          ;1
    bst     x2, USBMINUS    ;1
    bld     shift, 2        ;1
    ldi     cnt, USB_BUFSIZE;1
    ldi     YH, hi8(usbRxBuf);1 assume that usbRxBuf does not cross a page
    push    x3              ;2 -> 8
    in      x2, USBIN       ;1 <-- sample bit 3, timing: edge + {2, 6} cycles
    eor     x1, x2          ;1
    bst     x1, USBMINUS    ;1
    bld     shift, 3        ;1
    ser     x3              ;1
    nop                     ;1
    rjmp    rxbit4          ;2 -> 8

shortcutToStart:            ;{,43} into next frame: max 5.5 sync bits missed
#if !USB_CFG_SAMPLE_EXACT
    ldi     x1, 5           ;2 setup timeout
#endif
waitForJ1:
    sbis    USBIN, USBMINUS ;1 wait for D- == 1
    rjmp    waitForJ1       ;2
#if USB_CFG_SAMPLE_EXACT
;The following code represents the unrolled loop in the else branch. It
;results in a sampling window of 1/4 bit which meets the spec.
    sbis    USBIN, USBMINUS
    rjmp    foundK1
    sbis    USBIN, USBMINUS
    rjmp    foundK1
    sbis    USBIN, USBMINUS
    rjmp    foundK1
    nop
    nop2
foundK1:
#else
waitForK1:
    dec     x1              ;1
    sbic    USBIN, USBMINUS ;1 wait for D- == 0
    brne    waitForK1       ;2
#endif
    pop     YH              ;2 correct stack alignment
    nop2                    ;2 delay for the same time as the pushes in the original code
    rjmp    shortcutEntry   ;2

; ################# receiver loop #################
; extra jobs done during bit interval:
; bit 6:    se0 check
; bit 7:    or, store, clear
; bit 0:    recover from delay  [SE0 is unreliable here due to bit dribbling in hubs]
; bit 1:    se0 check
; bit 2:    se0 check
; bit 3:    overflow check
; bit 4:    se0 check
; bit 5:    rjmp

; stuffed* helpers have the functionality of a subroutine, but we can't afford
; the overhead of a call. We therefore need a separate routine for each caller
; which jumps back appropriately.

stuffed5:               ;1 for branch taken
    in      x2, USBIN   ;1 <-- sample @ +1
    andi    x2, USBMASK ;1
    breq    se0a        ;1
    andi    x3, ~0x20   ;1
    ori     shift, 0x20 ;1
    rjmp    rxbit6      ;2

stuffed6:               ;1 for branch taken
    in      x1, USBIN   ;1 <-- sample @ +1
    andi    x1, USBMASK ;1
    breq    se0a        ;1
    andi    x3, ~0x40   ;1
    ori     shift, 0x40 ;1
    rjmp    rxbit7      ;2

; This is somewhat special because it has to compensate for the delay in bit 7
stuffed7:               ;1 for branch taken
    andi    x1, USBMASK ;1 already sampled by caller
    breq    se0a        ;1
    mov     x2, x1      ;1 ensure correct NRZI sequence
    ori     shift, 0x80 ;1 no need to set reconstruction in x3: shift has already been used
    in      x1, USBIN   ;1 <-- sample bit 0
    rjmp    unstuffed7  ;2

stuffed0:               ;1 for branch taken
    in      x1, USBIN   ;1 <-- sample @ +1
    andi    x1, USBMASK ;1
    breq    se0a        ;1
    andi    x3, ~0x01   ;1
    ori     shift, 0x01 ;1
    rjmp    rxbit1      ;2

;-----------------------------
rxLoop:
    breq    stuffed5    ;1
rxbit6:
    in      x1, USBIN   ;1 <-- sample bit 6
    andi    x1, USBMASK ;1
    breq    se0a        ;1
    eor     x2, x1      ;1
    bst     x2, USBMINUS;1
    bld     shift, 6    ;1
    cpi     shift, 0x02 ;1
    brlo    stuffed6    ;1
rxbit7:
    in      x2, USBIN   ;1 <-- sample bit 7
    eor     x1, x2      ;1
    bst     x1, USBMINUS;1
    bld     shift, 7    ;1
    eor     x3, shift   ;1 x3 is 0 at bit locations we changed, 1 at others
    st      y+, x3      ;2 the eor above reconstructed modified bits and inverted rx data
    ser     x3          ;1
rxbit0:
    in      x1, USBIN   ;1 <-- sample bit 0
    cpi     shift, 0x04 ;1
    brlo    stuffed7    ;1
unstuffed7:
    eor     x2, x1      ;1
    bst     x2, USBMINUS;1
    bld     shift, 0    ;1
    andi    shift, 0xf9 ;1
    breq    stuffed0    ;1
rxbit1:
    in      x2, USBIN   ;1 <-- sample bit 1
    andi    x2, USBMASK ;1
se0a:                   ; enlarge jump range to SE0
    breq    se0         ;1 check for SE0 more often close to start of byte
    eor     x1, x2      ;1
    bst     x1, USBMINUS;1
    bld     shift, 1    ;1
    andi    shift, 0xf3 ;1
    breq    stuffed1    ;1
rxbit2:
    in      x1, USBIN   ;1 <-- sample bit 2
    andi    x1, USBMASK ;1
    breq    se0         ;1
    eor     x2, x1      ;1
    bst     x2, USBMINUS;1
    bld     shift, 2    ;1
    andi    shift, 0xe7 ;1
    breq    stuffed2    ;1
rxbit3:
    in      x2, USBIN   ;1 <-- sample bit 3
    eor     x1, x2      ;1
    bst     x1, USBMINUS;1
    bld     shift, 3    ;1
    dec     cnt         ;1  check for buffer overflow
    breq    overflow    ;1
    andi    shift, 0xcf ;1
    breq    stuffed3    ;1
rxbit4:
    in      x1, USBIN   ;1 <-- sample bit 4
    andi    x1, USBMASK ;1
    breq    se0         ;1
    eor     x2, x1      ;1
    bst     x2, USBMINUS;1
    bld     shift, 4    ;1
    andi    shift, 0x9f ;1
    breq    stuffed4    ;1
rxbit5:
    in      x2, USBIN   ;1 <-- sample bit 5
    eor     x1, x2      ;1
    bst     x1, USBMINUS;1
    bld     shift, 5    ;1
    andi    shift, 0x3f ;1
    rjmp    rxLoop      ;2
;-----------------------------

stuffed1:               ;1 for branch taken
    in      x2, USBIN   ;1 <-- sample @ +1
    andi    x2, USBMASK ;1
    breq    se0         ;1
    andi    x3, ~0x02   ;1
    ori     shift, 0x02 ;1
    rjmp    rxbit2      ;2

stuffed2:               ;1 for branch taken
    in      x1, USBIN   ;1 <-- sample @ +1
    andi    x1, USBMASK ;1
    breq    se0         ;1
    andi    x3, ~0x04   ;1
    ori     shift, 0x04 ;1
    rjmp    rxbit3      ;2

stuffed3:               ;1 for branch taken
    in      x2, USBIN   ;1 <-- sample @ +1
    andi    x2, USBMASK ;1
    breq    se0         ;1
    andi    x3, ~0x08   ;1
    ori     shift, 0x08 ;1
    rjmp    rxbit4      ;2

stuffed4:               ;1 for branch taken
    in      x1, USBIN   ;1 <-- sample @ +1
    andi    x1, USBMASK ;1
    breq    se0         ;1
    andi    x3, ~0x10   ;1
    ori     shift, 0x10 ;1
    rjmp    rxbit5      ;2

;################ end receiver loop ###############

overflow:                   ; ignore package if buffer overflow
    rjmp    rxDoReturn      ; enlarge jump range

;This is the only non-error exit point for the software receiver loop
;{4, 20} cycles after start of SE0, typically {10, 18} after SE0 start = {-6, 2} from end of SE0
;next sync starts {16,} cycles after SE0 -> worst case start: +4 from next sync start
;we don't check any CRCs here because there is no time left.
se0:                            ;{-6, 2} from end of SE0 / {,4} into next frame
    mov     cnt, YL             ;1 assume buffer in lower 256 bytes of memory
    lds     YL, usbInputBuf     ;2 reposition to buffer start
    sub     cnt, YL             ;1 length of message
    ldi     x1, 1<<USB_INTR_PENDING_BIT ;1
    cpi     cnt, 3              ;1
    out     USB_INTR_PENDING, x1;1 clear pending intr and check flag later. SE0 must be over. {,10} into next frame
    brlo    rxDoReturn          ;1 ensure valid packet size, ignore others
    ld      x1, y               ;2 PID
    ldd     x2, y+1             ;2 ADDR + 1 bit endpoint number
    mov     x3, x2              ;1 store for endpoint number
    andi    x2, 0x7f            ;1 mask endpoint number bit
    lds     shift, usbDeviceAddr;2
    cpi     x1, USBPID_SETUP    ;1
    breq    isSetupOrOut        ;2 -> 19 = {13, 21} from SE0 end
    cpi     x1, USBPID_OUT      ;1
    breq    isSetupOrOut        ;2 -> 22 = {16, 24} from SE0 end / {,24} into next frame
    cpi     x1, USBPID_IN       ;1
    breq    handleIn            ;1
#define USB_DATA_MASK   ~(USBPID_DATA0 ^ USBPID_DATA1)
    andi    x1, USB_DATA_MASK   ;1
    cpi     x1, USBPID_DATA0 & USB_DATA_MASK ;1
    brne    rxDoReturn          ;1 not a data PID -- ignore

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -