TMR1 ISR (version 2)
Note: The latest TMR1 ISR version is to be found on google code (SVN trunk). The first “V2″ version on this page contains many subtle errors and is missing much functionality compared to the SVN version. It is being kept here just for reference purposes.
; Copyright (C) Angelos Varvitsiotis 2009 -- Licensed under GNU GPLv3 #include p18f2550.inc #include HardwareProfile.inc #define DEBUG_USB ;;;;;;;;;;;;;;;;;;#define DEBUG_CYCLES ; Labels for external addresses ; NOTE: the labels below are valid only in FULL ping-pong mode! ; specific fields of BD for EP2 IN, even ping-pong phase EP2IEAH EQU 0x42b ; bank-part of buffer address field EP2IEAL EQU 0x42a ; offset-part of buffer address field EP2IECN EQU 0x429 ; byte count field EP2IEST EQU 0x428 ; status flags field ; specific fields of BD for EP2 IN, odd ping-pong phase EP2IOAH EQU 0x42f ; bank-part of buffer address field EP2IOAL EQU 0x42e ; offset-part of buffer address field EP2IOCN EQU 0x42d ; byte count field EP2IOST EQU 0x42c ; status flags field ; specific fields of BD for EP2 OUT, even ping-pong phase EP2OEAH EQU 0x423 EP2OEAL EQU 0x422 EP2OECN EQU 0x421 EP2OEST EQU 0x420 ; specific fields of BD for EP2 OUT, odd ping-pong phase EP2OOAH EQU 0x427 EP2OOAL EQU 0x426 EP2OOCN EQU 0x425 EP2OOST EQU 0x424 ; Our exports ; code entry points GLOBAL tmr1_isr_init GLOBAL tmr1_isr ; data GLOBAL OUTPCMData0 ; currently, for debugging GLOBAL OUTPCMData1 ; currently, for debugging GLOBAL IN_PCMData0 ; currently, for debugging GLOBAL IN_PCMData1 ; currently, for debugging GLOBAL passout ; a debug return value GLOBAL pasHout ; a debug return value (for WORDs) GLOBAL pauseIO ; pause USB I/O GLOBAL inisync ; intial SOF sync ; for debugging ONLY GLOBAL cnt4 GLOBAL cnt8 ; Globals, all placed in the "usb4" bank. It is important to have all ; of these in the same bank, because in the ISR we only do a single ; BANKSEL, assuming that all data lie in the same bank, in order to ; save us time. Thus, we ask the assembler to tell the linker to ; place our data at the beginning of GPR1. ; Note: for the specific project, we have only 3 endpoints, 0, 1, ; and 2. The BD for these occupies 4 (bytes/entry) x (#EPs) x 2 ; (in/out) x 2 (even/odd ping-pong) bytes, which amounts to 48 ; bytes; two more 32-byte USB packet buffers are allocated right ; after that, which amounts up to 112 bytes. So theoretically, ; the space from 0x480 is free for us to use. ourvars UDATA 0x480 ; next globals implement two (for ping-pong buffering mode) read ; and another two write audio buffers (NOTE: don't add anything ; before these variables, so as to make sure they are 16-byte ; aligned). ; the next variables should lie on a 64-byte aligned area OUTPCMData0 RES 16 ; even ping-pong buffer for OUT data OUTPCMData1 RES 16 ; odd ping-pong buffer for OUT data IN_PCMData0 RES 16 ; even ping-pong buffer for IN data IN_PCMData1 RES 16 ; odd ping-pong buffer for IN data ppindex RES 1 ; index (incl. ping-pong offset); this ; ranges from 8..15 for even ping-pong ; buffer state and from 24..31 for odd ; state pauseIO RES 1 ; flags whether we should do USB I/O ; next two bytes are bit buffers for the current bytes of PCM data ; that are currently being read in and written out byteDRX RES 1 ; stores 1 DRX byte (PCM data PIC->3210) byteDTX RES 1 ; stores 1 DTX byte (PCM data 3210->PIC) ; next come two timers that are maintained by the ISR in order to do ; different things at each invocation cnt8 RES 1 ; counts 8 isr invocations cnt4 RES 1 ; counts 4x8 invocations ; debugging return value passout RES 1 ; variable to pass out debugging info pasHout RES 1 ; variable to pass out debugging info inisync RES 1 ; variable for doing initial SOF sync ; temporary storage for FSR1's value sfsr1l RES 1 ; FSR1L temp save space sfsr1h RES 1 ; FSR1H temp save space sfsr2l RES 1 ; FSR2L temp save space sfsr2h RES 1 ; FSR2H temp save space ; TMR1 service interrupt routine ; I have modified the bootloader code to jump to 0x820 for the ; high-priority interrupt; thus, the PIC takes 3 instruction ; cycles (I use C to denote an instruction cycle) to process ; the interrupt and another 2 C to jump here, which sums up to ; 5 C. timer1isr CODE 0x820 tmr1_isr ; DOCNOTE: ; I use the @ symbol to denote the cycles already elapsed since ; TMR1 fireup when we enter this routine; this is equal to 5 (see ; previous note). ; T:@, where @ == 5, see above ; Our first job is to raise PCLK (note that PCLK is raised at time @==5) BSF pcm_3210_pclk, ACCESS ; C:1 raise PCLK ; Clear the interrupt flag (otherwise the call to the ISR loops forever!) BCF PIR1, TMR1IF, ACCESS ; C:1 clear interrupt flag ; T:@+2 ; Our next step is to increment our counters and choose accordingly ; what to do next BANKSEL cnt8 ; C:1 use appropriate bank ; Note: this is the only BANKSEL instruction used throughout the ; code. All data are assumed to lie on the same bank. See note on ; this at the UDATA section. ; first increment cnt8 modulo 8 and keep the status handy INCF cnt8, F, BANKED ; C:1 increment 8-step counter MOVLW 7 ; C:1 prepare % 8 operation ANDWF cnt8, F, BANKED ; C:1 AND it and save it ; T:@+6 ; if cnt8 has not just looped over, jump to "notfirstof8" BNZ notfirstof8 ; C:1/2 ; Our cnt8 has looped to 0; we need to increment our cnt4 and check ; if we are starting a new 32-step sequence ; T:@+7 INCF cnt4, F, BANKED ; C:1 increment 4-step counter MOVLW 3 ; C:1 modulo 4 ANDWF cnt4, F, BANKED ; C:1 save it ; T:@+10 ; if cnt4 has not looped over, jump to "firstof8not32" BNZ firstof8not32 ; C:1/2 ; (following label is just for clarity, there's not any jump into it) firstof32 ; T:@+11 ; At this point, we are starting a new 32-step sequence; the tasks that ; we have to do are (i) assert the DRX signal (our output) to the 3210, ; (ii) re-arm the TMR1 to fire at @+46-5 (this is our first round, so it ; should take 23+23=46 cycles), (iii) initialize the input PCM byte to ; zero before collecting any PCM input, (iv) collect our PCM input from ; the DTX signal and (v) lower PCLK at time @+5+23. ; NOTE: this round, just like all other cases where cnt8==0, requires ; a PCLK-high half-cycle of 23TCy in duration (PCLK lowered at @+23) ; task (i): prepare the DRX signal (our output -- must ; be ready before the falling edge of PCLK) RLCF byteDRX, F, BANKED ; C:1 rotate left through carry BNC fo32_clr_DRX ; C:1/2 check if carry is set #ifdef TEST_PCM_TIMING BTG pcm_3210_drx, ACCESS ; (C:1) just toggle (for tests) #else BSF pcm_3210_drx, ACCESS ; C:1 it is set, so set DRX to 1 #endif BRA fo32_DRX_ok ; C:2 done fo32_clr_DRX ; T:@+14 #ifdef TEST_PCM_TIMING BTG pcm_3210_drx, ACCESS ; (C:1) just toggle (for tests) #else BCF pcm_3210_drx, ACCESS ; C:1 carry not set, clear DRX #endif NOP ; C:1 make both paths' delay eq. fo32_DRX_ok ; T:@+16 via both paths ; task (ii): re-arm TMR1 to fire again at @+46-5 SETF TMR1H, ACCESS ; C:1 set TMR1H to 0xFF MOVLW 235 ; C:1 next interrupt at @+46-5 MOVWF TMR1L, ACCESS ; C:1 ; T:@+19 ; task (iii): initialize the DTX PCM byte to zero CLRF byteDTX, BANKED ; C:1 clear byteDTX ; T:@+20 ; task (iv): collect our PCM input from the DTX signal BTFSC pcm_3210_dtx, ACCESS ; C:1/2 skip next if DTX is 0 BSF byteDTX, 0, BANKED ; C:1 if 1, set bit 0 of byteDTX ; T:@+22 via both paths ; task (v): wait until T:@+23, then lower PCLK NOP ; C:1 ; T:@+23 BCF pcm_3210_pclk, ACCESS ; C:1 lower PCLK ; finished with all of our tasks, time to return RETFIE FAST ; C:2 ; We get here if is the first cnt4 round (cnt4==0). We need to ; check if cnt4==0, in which case we 'll do PCM I/O and the other ; cases. notfirstof8 ; T:@+8 DECF cnt4, W, BANKED ; C:1 W:=cnt4-1; if cnt40, the BNN notfirst8of32 ; C:1/2 "neg" bit is clear, jump ; (following label is just for clarity, there is not any jump into it) first8of32 ; T:@+10 ; We come here if cnt8 and cnt4==0, so we are dealing with the ; first 8 cycles of our 32-cycle train. During these cycles, we must ; do PCM I/O. ; task (i): prepare the DRX signal (our output -- must ; be ready before the falling edge of PCLK) RLCF byteDRX, F, BANKED ; C:1 rotate left through carry BNC f8o32_clr_DRX ; C:1/2 check if carry is set #ifdef TEST_PCM_TIMING BTG pcm_3210_drx, ACCESS ; (C:1) just toggle (for tests) #else BSF pcm_3210_drx, ACCESS ; C:1 it is set, so set DRX to 1 #endif BRA f8of32_DRX_ok ; C:2 done f8o32_clr_DRX ; T:@+13 #ifdef TEST_PCM_TIMING BTG pcm_3210_drx, ACCESS ; (C:1) just toggle (for tests) #else BCF pcm_3210_drx, ACCESS ; C:1 carry not set, clear DRX #endif NOP ; C:1 make both paths' delay eq. f8of32_DRX_ok ; T:@+15 via both paths ; task (ii): re-arm TMR1 to fire again at @+47-5 SETF TMR1H, ACCESS ; C:1 set TMR1H to 0xFF MOVLW 233 ; C:1 next interrupt at @+47-5 MOVWF TMR1L, ACCESS ; C:1 ; T:@+18 ; task (iii): collect our PCM input from the DTX signal RLNCF byteDTX, F, BANKED ; C:1 rotate 1 bit current part BTFSC pcm_3210_dtx, ACCESS ; C:1 test DTX, skip if 0 BSF byteDTX, 0, BANKED ; C:1 if it was 1, set our bit 0 ; T:@+21 ; task (iv): wait until time @+24 (this is not the first round, so ; we wait 24 TCy before lowering PCLK), then lower PCLK NOP ; C:1 wait until T+24 NOP ; C:1 NOP ; C:1 BCF pcm_3210_pclk, ACCESS ; C:1 lower PCLK ; done RETFIE FAST ; C:2 ; At this point, other than the last of the 32 cycles, all we have ; to do is lower PCLK at the right time (@+24). For the last cycle ; (cnt4==3 and cnt8==7) we need to pulse FSYNC up and down notfirst8of32 ; T:@+11 ; 246 + 3 + 7 yields zero, so this is the fastest test I could think of ; (it's ok only because cnt4 cannot be > 3 and cnt8 cannot be > 7) MOVLW 246 ; C:1 ADDWF cnt4, W, BANKED ; C:1 W := cnt4 - 10 ADDWF cnt8, W, BANKED ; C:1 BNZ notlastof32 ; C:1/2 ; (following label is just for clarity, there is not any jump into it) raiseFSYNC ; T:@+15 #ifdef DEBUG_CYCLES MOVF TMR3L, W, ACCESS MOVWF passout, BANKED MOVF TMR3H, W, ACCESS MOVWF pasHout, BANKED #endif ; task (i): raise FSYNC ; Timing: the timing diagram on p.16 of the 3210 datasheet requires ; FSYNC to be raised 25ns before the falling edge of PCLK. ; We are definitely safe here. BSF pcm_3210_fsync, ACCESS ; C:1 ; T:@+16 ; task (ii): re-arm TMR1 to fire at @+47-5 SETF TMR1H, ACCESS ; C:1 set TMR1H to 0xFF MOVLW 234 ; C:1 next interrupt at @+47-5 MOVWF TMR1L, ACCESS ; C:1 ; T:@+19 ; task (iii): wait until time @+24, then lower PCLK #ifdef DEBUG_CYCLES ; synchronize TMR3 with TMR1, so that TMR3 will be zero at time @ ; on next (cnt4==cnt8==0) SETF TMR3H, ACCESS ; C:1 MOVLW 231 ; C:1 MOVWF TMR3L, ACCESS ; C:1 NOP ; C:1 NOP ; C:1 #else NOP ; C:1 NOP ; C:1 NOP ; C:1 NOP ; C:1 NOP ; C:1 #endif ; T:@+24 ; task (iv): lower PCLK BCF pcm_3210_pclk, ACCESS ; C:1 lower PCLK ; task (v): lower FSYNC ; Timing: the timing diagram on p.16 of the 3210 datasheet requires ; FSYNC to be held up at least 20ns after the falling edge ; of PCLK. One TCy is 83.33+ ns, so we are safe lowering ; FSYNC at the next PIC instruction after lowering PCLK. ; T:@+25 BCF pcm_3210_fsync, ACCESS ; C:1 ; done RETFIE FAST ; C:2 notlastof32 ; T:@+16 ; In this case (27 out of 32 invocations fall here), all that's left ; for us to do is re-arm, wait, lower PCLK and return. ; task (ii): re-arm TMR1 to fire at @+47-5 SETF TMR1H, ACCESS ; C:1 set TMR1H to 0xFF MOVLW 234 ; C:1 next interrupt at @+47-5 MOVWF TMR1L, ACCESS ; C:1 ; T:@+19 NOP ; C:1 NOP ; C:1 NOP ; C:1 NOP ; C:1 NOP ; C:1 ; T:@+24 BCF pcm_3210_pclk, ACCESS ; C:1 lower PCLK ; T:@+25 ; done RETFIE FAST ; C:2 firstof8not32 ; T:@+12 ; At this point, we are running the first cnt8 (cnt8 == 0) round of a ; cnt4 round other than the first one (cnt4 != 0). We have to do ; different things depending on the value of cnt4, so we test for ; the value of cnt4 first. DECF cnt4, W, BANKED ; C:1 W := cnt4 - 1 BNZ notsecondof4 ; C:1/2 if (cnt4 > 1), jump on ; We are running the first cnt8 (cnt8 == 0) round of the second cnt4 ; (cnt4 == 1) round. PCM I/O had been going on while (cnt4 == 0), so ; now it has just finished and it's time to move the relevant bytes ; to and from the respective USB PCM I/O buffers. Notice that we ; finish this cycle without returning to user space, because we take ; quite long and we finish right on time for a jump back to the start ; of the ISR. Our tasks for this section are: (i) save FSRs to temp ; storage (FSRs are very powerful registers for optimizing all the ; tasks that we need to do), (ii) lower PCLK (has to happen now for ; timing reasons), (iii) initialize FSRs 1 and 2 and REGW with ; appropriate values, (iv) pass data from USB buffer to ISR bit buffer, ; (v) pass data from ISR bit buffer to USB buffer, (vi) restore FSRs ; NOTE: this round, just like all other rounds with cnt8==0, requires ; a PCLK-high half-cycle of 23TCy in duration (PCLK lowered at @+23) ; (following label is just for clarity, there is not any jump into it) secondof4 ; T:@+14 ; task (i): save FSR1 and FSR2 to temp storage MOVFF FSR1L, sfsr1l ; C:2 save FSR1L in ISR space MOVFF FSR1H, sfsr1h ; C:2 save FSR1H in ISR space MOVFF FSR2L, sfsr2l ; C:2 save FSR2L in ISR space MOVFF FSR2H, sfsr2h ; C:2 save FSR2H in ISR space ; T:@+22 NOP ; C:1 ; task (iii) [must occur at @+23]: lower PCLK BCF pcm_3210_pclk, ACCESS ; C:1 lower PCLK ; T:@+24 ; task (ii): initialize FSR1 and FSR2 for passing data between ; the USB buffers and our local bytes LFSR FSR1, OUTPCMData0 ; C:2 FSR1 := &OUTPCMData0[0] LFSR FSR2, byteDRX ; C:2 FSR2 := &byteDRX MOVF ppindex, W, BANKED ; C:1 add the index incl. p-p ; T:@+29 ; task (iv): move one byte of data from the OUT USB data buffer into ; the byteDRX bit buffer MOVFF PLUSW1, POSTINC2 ; C:2 move byte indexed by ; FSR1+W (OUTPCMData0 + ; ppindex) into the byte ; indexed by FSR2 (byteDRX), ; then post-increment FSR2, ; which now points to ; byteDTX ; T:@+31 ; task (v): move one byte of data from the byteDTX bit buffer into the ; IN_ USB data buffer LFSR FSR1, IN_PCMData0 ; C:2 MOVFF INDF2, PLUSW1 ; C:2 move byte indexed by ; FSR2 (byteDTX) into the ; byte indexed by FSR1+W ; (IN_PCMData0+ppindex) ; T:@+35 ; task (vi): restore FSR registers from temporary storage MOVFF sfsr1l, FSR1L ; C:2 restore FSR1L MOVFF sfsr1h, FSR1H ; C:2 restore FSR1H MOVFF sfsr2l, FSR2L ; C:2 restore FSR2L MOVFF sfsr2h, FSR2H ; C:2 restore FSR2H ; T:@+43 NOP ; C:1 ; T:@+44 ; by means of the following BRA, we get back to the start of our ; ISR just in time (@+46) BRA tmr1_isr ; C:2 notsecondof4 ; T:@+15 ; At this point, we are running the first cnt8 (cnt8 == 0) round of a ; cnt4 round other than the first two (cnt4 != 0, cnt4 != 1). We have ; to do different things depending on the value of cnt4, so we test for ; the value of cnt4 first. DECF WREG, W, ACCESS ; C:1 W := cnt4 - 2 BNZ notthirdof4 ; C:1/2 ; (following label is just for clarity, there's not any jump into it) thirdof4 ; T:@+17 ; We are at the first cnt8 (cnt8 == 0) round of the third cnt4 ; (cnt4 == 2) round. During the previous (cnt4 == 1) round, and ; we did I/O between the USB- and the bit-buffer space. ; Now it's time to increment our USB buffer index ppindex, and ; check if an overflow occured; if so, we are to initiate USB I/O. ; Our tasks here are to: (i) increas ppindex modulo 32, (ii) check ; if we overflowed our 8-byte PCM audio buffer space; if not, just ; re-arm TMR1 and return; if yes, (iii) distinguish if we are to ; do an even or an odd-phase ping-pong transmission, (iv) setup ; the respective BD for transmission, (v) lower PCLK at T:@+23, ; and (vi) either re-arm TMR1 or wait and loop over ; FIXDOC: rewrite tasks, PCLK has moved up (now is task (iii)) ; NOTE: this round, just like all other rounds with cnt8==0, requires ; a PCLK-high half-cycle of 23TCy in duration (PCLK lowered at @+23) ; task (i): increase ppindex modulo 32 INCF ppindex, F, BANKED ; C:1 MOVLW 0x1f ; C:1 ANDWF ppindex, F, BANKED ; C:1 ; T:@+20 ; task (ii): check whether we have overflowed: a valid ppindex ; must be between 8 and 15 or between 24 and 31, so it must ; always have bit 3 set; if not, we have just overflowed. BTFSC ppindex, 3, BANKED ; C:1/2 BRA to4noovfl ; C:2 ; T:@+22 NOP ; C:1 ; task (iii): lower PCLK, right at @+23 BCF pcm_3210_pclk, ACCESS ; C:1 lower PCLK ; T:@+24 ; So now we have overflowed, and we must transmit either ; the packet IN_PCMData0 or IN_PCMData1, using the BD at ; address either 0x428 or 0x42c, depending on whether we ; are in an even or an odd ping-pong phase, respectively; ; bit 4 of ppindex tells us in which ping-pong phase we ; are: if bit 4 is set, this means that we have just ; finished with the packet IN_PCMData0; if it is clear, ; this means we have just finished with tha packet ; IN_PCMData1 ; task (iv): distinguish between an even and an odd ping-pong phase BTFSS ppindex, 4, BANKED ; C:1/2 BRA usbIN_odd ; C:2 ; (following label is just for clarity, there's not any jump into it) usbIN_evn ; T:@+26 ; if we are (re) synchronizing with the SOF frame, don't do the ; actual transmit; BTFSC inisync, 0, BANKED ; C:1/2 do initial sync? BRA nosyncevn ; C:2 skip initial sync if not ; The following code synchronizes our TMR1 ISR with the SOF frame that ; the host keeps sending every millisecond to the board. Notice that ; this synchronization destroys the ISR's time schedule, and might ; cause trouble to the 3210 because PCLK pulsing is halted for quite ; a long time. So this should occur only once, during initialization. ; Note that 1 ms = 12,000 TCy, which is (375 * 4) * 8 (that is 8 full ; ISR cycles, one for each PCM data byte). So, hereon, the ISR should ; remain fully synchronized with the SOF pace! ; clear SOF interrupt flag, then wait for SIE hardware to re-assert it BCF UIR, SOFIF, ACCESS ; C:1 clear SOF interrupt flag waitSOF BTFSS UIR, SOFIF, ACCESS ; C:1 break loop if SOF is set BRA waitSOF ; C:2 otherwise loop waiting ; for SOF flag to come up ; T:? (initial time lost) SOF+2 ; Now that we have synchronized with the SOF frame, proceed ASAP ; to the data send task right below. Since the PIC and the USB ; clocks should be 100% synchronized, and provided that our TMR1 ; ISR does not miss any clock cycles at all, we should never again ; need to synchronize with the SOF. That is, isochronous IN should ; forever now on occur at about 15 TCy (eqv., 1.25 microseconds) ; after the SOF frame (which is fast enough for the packet to be ; received correctly by the host)! nosyncevn ; T:@+29 ; clear the SOF flag BCF UIR, SOFIF, ACCESS ; C:1 clear SOF interrupt flag ; T:@+30 BTFSC pauseIO, 0, BANKED ; C:1/2 BRA atpls33 ; C:2 ; T:@+32 INCF IN_PCMData0+7,BANKED ; C:1 NOP ; C:1 ; T:@+34 ; task (iv) case a: transmit even buffer using even BD MOVLW HIGH IN_PCMData0 ; C:1 bank part of IN_PCMData0 MOVWF EP2IEAH, BANKED ; C:1 store it in BD MOVLW LOW IN_PCMData0 ; C:1 offset part of IN_PCMData0 MOVWF EP2IEAL, BANKED ; C:1 store it in BD MOVLW 0x10 ; C:1 packet length, 16 MOVWF EP2IECN, BANKED ; C:1 store it in BD MOVLW 0x40 ; C:1 just keep the DTS bit ANDWF EP2IEST, BANKED ; C:1 MOVLW 0x88 ; C:1 set UOWN and DTSEN bits IORWF EP2IEST, BANKED ; C:1 that's it, packet queued! ; T:@+44 ; right on time to loop back! BRA tmr1_isr ; C:2 ; various NOP-filled delays until we loop back, used as jump labels atpls31 ; T:@+31 NOP ; C:1 atpls32 ; T:@+32 NOP ; C:1 atpls33 ; T:@+33 NOP ; C:1 atpls34 ; T:@+34 NOP ; C:1 atpls35 ; T:@+35 NOP ; C:1 atpls36 ; T:@+36 NOP ; C:1 atpls37 ; T:@+37 NOP ; C:1 atpls38 ; T:@+38 NOP ; C:1 atpls39 ; T:@+39 NOP ; C:1 atpls40 ; T:@+40 NOP ; C:1 atpls41 ; T:@+41 NOP ; C:1 atpls42 ; T:@+42 NOP ; C:1 atpls43 ; T:@+43 NOP ; C:1 ; T:@+44 BRA tmr1_isr usbIN_odd ; T:@+27 ; clear the SOF flag BCF UIR, SOFIF, ACCESS; C:1 clear SOF interrupt flag ; T:@+28 BTFSC pauseIO, 0, BANKED ; C:1/2 BRA atpls31 ; C:2 ; T:@+30 #ifdef DEBUG_USB MOVFF TMR3L, IN_PCMData1+4 ; C:2 MOVFF TMR3H, IN_PCMData1+5 ; C:2 #else INCF IN_PCMData1+7, BANKED ; C:1 NOP ; C:1 NOP ; C:1 NOP ; C:1 #endif ; T:@+34 ; task (iv) case b: transmit odd buffer using odd BD MOVLW HIGH IN_PCMData1 ; C:1 bank part of IN_PCMData1 MOVWF EP2IOAH, BANKED ; C:1 store it in BD MOVLW LOW IN_PCMData1 ; C:1 offset part of IN_PCMData1 MOVWF EP2IOAL, BANKED ; C:1 store it in BD MOVLW 0x10 ; C:1 packet length, 16 MOVWF EP2IOCN, BANKED ; C:1 store it in BD MOVLW 0x40 ; C:1 just keep the DTS bit ANDWF EP2IOST, BANKED ; C:1 MOVLW 0x88 ; C:1 set UOWN and DTSEN bits IORWF EP2IOST, BANKED ; C:1 that's it, packet queued! ; T:@+44 BRA tmr1_isr ; C:2 to4noovfl ; T:@+23 ; We come here if ppindex indicates no overflow, so all we need to do ; is to lower PCLK, re-arm TMR1 and return ; task (ii): lower PCLK at time @+23 BCF pcm_3210_pclk, ACCESS ; C:1 lower PCLK ; T:@+24 ; task (iii): re-arm TMR1 to fire again at @+46-5 SETF TMR1H, ACCESS ; C:1 set TMR1H to 0xFF MOVLW 243 ; C:1 next interrupt at @+46-5 MOVWF TMR1L, ACCESS ; C:1 ; T:@+27 ; we are done RETFIE FAST ; C:2 notthirdof4 ; T:@+18 ; At this point, we are running the first cnt8 (cnt8 == 0) round of ; the last cnt4 round (cnt4 == 3). ; NOTE: this round, just like all other (cnt8 == 0) rounds requires ; a PCLK-high half-cycle of 23TCy in duration (PCLK lowered at @+23) ; (following label is just for clarity, there's not any jump into it) fourthof4 ; T:@+18 ; task (i): check (again) whether we have overflowed our 8-byte ; audio buffer by our last incrementing ppindex (in thirdof4) BTFSC ppindex, 3, BANKED ; C:1/2 BRA fo4noovfl ; C:2 ; T:@+20 ; Here again, we must re-initiate a read for either OUTPCMData0 ; or OUTPCMData1, using the BD at address either 0x424 or 0x428 ; respectively, depending on the ping-pong phase. If bit 4 of ; ppindex is set, this means that we have just finished sending ; OUTPCMData0 to the 3210 and we may arm a USB receive for data ; to fill that buffer; if bit 4 of ppindex is clear, this means ; that we have just finished sending OUTPCMData1 to the 3210 ; and we may arm a USB receive for data to fill that. ; task (ii): distinguish if we are to do an even or an odd ping-pong ; receive BTFSS ppindex, 4, BANKED ; C:1/2 BRA usbOUTodd ; C:2 ; (following label is just for clarity, there's not any jump into it) usbOUTevn ; T:@+22 ; task (iii): fix overflow condition by setting bit 3 of ppindex BSF ppindex, 3, BANKED ; C:1 ; T:@+23 ; task (iv): lower PCLK, right at @+23 BCF pcm_3210_pclk, ACCESS ; C:1 lower PCLK ; T:@+24 ; task (v) case a: check even BD's UOWN flag to see if I/O is ready BTFSC EP2OEST, 7, BANKED ; C:1/2 check UOWN bit (bit is ; zero if I/O is finished) BRA usbOUTnonfreeA ; C:2 ; T:@+26 #ifdef DEBUG_USB_NO_OUT SETF TMR1H, ACCESS ; C:1 set TMR1H to 0xFF MOVLW 245 ; C:1 next interrupt at @+46-5 MOVWF TMR1L, ACCESS ; C:1 RETFIE FAST ; C:2 #else ; task (vi) case a: arm a receive to prepare receiving the even BD MOVLW HIGH OUTPCMData0 ; C:1 bank part of OUTPCMData0 MOVWF EP2OEAH, BANKED ; C:1 store it in BD MOVLW LOW OUTPCMData0 ; C:1 offset part of OUTPCMData0 MOVWF EP2OEAL, BANKED ; C:1 store it in BD MOVLW 0x10 ; C:1 packet length, 16 MOVWF EP2OECN, BANKED ; C:1 store it in BD MOVLW 0x40 ; C:1 just keep the DTS bit ANDWF EP2OEST, BANKED ; C:1 MOVLW 0x88 ; C:1 set UOWN and DTSEN bits IORWF EP2OEST, BANKED ; C:1 that's it, packet queued! ; T:@+36 ; here, if we arm TMR1 and return, we 'll hit userspace just as ; TMR1->0; in this case, the PIC always executes one userspace ; instruction, so we 'll drift by 1TCy; thus, the right thing to ; do is to wait until it's time and then loop over to the start #ifdef DEBUG_USB ; copy serial from last OUT into next IN packet MOVFF OUTPCMData1+3,IN_PCMData1+3 #else NOP ; C:1 NOP ; C:1 #endif NOP ; C:1 NOP ; C:1 NOP ; C:1 NOP ; C:1 NOP ; C:1 NOP ; C:1 ; TODO note: move actual xmitwork a bit down and insert code for ; skipping transmission based on a flag, plus code to increment ; a packet serial number ; T:@+44 BRA tmr1_isr ; C:2 ...just at @+46 #endif usbOUTodd ; T:@+23 ; task (iii): lower PCLK, right at @+23 BCF pcm_3210_pclk, ACCESS ; C:1 lower PCLK ; T:@+24 ; task (iv): fix overflow condition by setting bit 3 of ppindex BSF ppindex, 3, BANKED ; C:1 ; T:@+25 ; task (v) case b: check odd BD's UOWN flag to see if I/O is ready BTFSC EP2OOST, 7, BANKED ; C:1/2 check UOWN bit (bit is ; zero if I/O is finished) BRA usbOUTnonfreeB ; C:2 ; T:@+27 #ifdef DEBUG_USB_NO_OUT SETF TMR1H, ACCESS ; C:1 set TMR1H to 0xFF MOVLW 246 ; C:1 next interrupt at @+46-5 MOVWF TMR1L, ACCESS ; C:1 RETFIE FAST ; C:2 #else ; task (vi) case b: arm a receive to prepare receiving the odd BD MOVLW HIGH OUTPCMData1 ; C:1 bank part of OUTPCMData1 MOVWF EP2OOAH, BANKED ; C:1 store it in BD MOVLW LOW OUTPCMData1 ; C:1 offset part of OUTPCMData1 MOVWF EP2OOAL, BANKED ; C:1 store it in BD MOVLW 0x10 ; C:1 packet length, 16 MOVWF EP2OOCN, BANKED ; C:1 store it in BD MOVLW 0x40 ; C:1 just keep the DTS bit ANDWF EP2OOST, BANKED ; C:1 MOVLW 0x88 ; C:1 set UOWN and DTSEN bits IORWF EP2OOST, BANKED ; C:1 that's it, packet queued! ; T:@+37 ; it' too late to return, so wait until it's time and then restart #ifdef DEBUG_USB ; copy serial from last OUT into next IN packet MOVFF OUTPCMData0+3,IN_PCMData0+3 #else NOP ; C:1 NOP ; C:1 #endif NOP ; C:1 NOP ; C:1 NOP ; C:1 NOP ; C:1 NOP ; C:1 ; TODO note: move actual xmitwork a bit down and insert code for ; skipping transmission based on a flag, plus code to increment ; a packet serial number ; T:@+44 BRA tmr1_isr ; C:2 ...just at @+46 #endif fo4noovfl ; T:@+21 ; task (ii): wait, then lower PCLK NOP ; C:1 NOP ; C:1 ; T:@+23 BCF pcm_3210_pclk, ACCESS ; C:1 lower PCLK ; T:@+24 ; task (iii): re-arm TMR1 to fire again at @+46-5 SETF TMR1H, ACCESS ; C:1 set TMR1H to 0xFF MOVLW 243 ; C:1 next interrupt at @+46-5 MOVWF TMR1L, ACCESS ; C:1 ; T:@+27 ; done RETFIE FAST ; C:2 usbOUTnonfreeA ; T:@+27 ; re-arm TMR1 to fire at time @+46-5 SETF TMR1H, ACCESS ; C:1 set TMR1H to 0xFF MOVLW 246 ; C:1 next interrupt at @+46-5 MOVWF TMR1L, ACCESS ; C:1 ; done RETFIE FAST ; C:2 usbOUTnonfreeB ; T:@+28 ; re-arm TMR1 to fire at time @+46-5 SETF TMR1H, ACCESS ; C:1 set TMR1H to 0xFF MOVLW 247 ; C:1 next interrupt at @+46-5 MOVWF TMR1L, ACCESS ; C:1 ; done RETFIE FAST ; C:2 ; Following code section is relocatable tmr1isrinit CODE ; The TMR1 ISR initialization function tmr1_isr_init ; disable TMR1 interrupts while we are working BCF PIE1, TMR1IE, ACCESS ; (re-?) initialize all ISR data and outputs BANKSEL cnt8 MOVLW 7 MOVWF cnt8, BANKED ; set cnt8 to 7 and cnt4 to 3 MOVLW 3 ; so that the first time around MOVWF cnt4, BANKED ; we start a new 32-cycle train MOVLW 8 ; set ppindex to 8 MOVWF ppindex, BANKED CLRF passout, BANKED ; set passout to 0 CLRF pauseIO ; disable USB I/O (TOFIX) SETF inisync ; do initial sync ; make sure PCLK and FSYNC are set to zero BCF pcm_3210_pclk, ACCESS ; lower PCLK BCF pcm_3210_fsync, ACCESS ; lower FSYNC ; re-enable TMR1 interrupts BSF PIE1, TMR1IE, ACCESS ; done RETURN END